-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
885 additions
and
63 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,14 @@ | ||
import { z } from "zod"; | ||
import { publicKeySchema } from "../../utils/validation"; | ||
|
||
export const MemoQuerySchema = z.object({ | ||
message: z.string().min(1, "Message is required").max(256, "Message must be less than 256 characters"), | ||
message: z.string().min(1, "Message is required"), | ||
}); | ||
|
||
export type MemoQuery = z.infer<typeof MemoQuerySchema>; | ||
export type MemoQuery = z.infer<typeof MemoQuerySchema>; | ||
|
||
export const MemoBodySchema = z.object({ | ||
account: publicKeySchema, | ||
}); | ||
|
||
export type MemoBody = z.infer<typeof MemoBodySchema>; |
125 changes: 125 additions & 0 deletions
125
examples/next-js/src/app/api/actions/paymaster/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { | ||
ActionGetResponse, | ||
ActionPostRequest, | ||
ActionPostResponse, | ||
createActionHeaders, | ||
createPostResponse, | ||
} from "@solana/actions"; | ||
import { createActionRoutes } from "../../utils/action-handler"; | ||
import { loadPaymasterKeypair } from "../../utils/keypair"; | ||
import { createQueryParser } from "../../utils/validation"; | ||
import { GenericTransactionExtensionSchema } from "./schema"; | ||
import { | ||
AddressLookupTableAccount, | ||
Message, | ||
MessageV0, | ||
Transaction, | ||
TransactionMessage, | ||
VersionedMessage, | ||
VersionedTransaction, | ||
} from "@solana/web3.js"; | ||
import { getConnection } from "../../utils/connection"; | ||
|
||
const headers = createActionHeaders(); | ||
|
||
async function handleGet(req: Request): Promise<ActionGetResponse> { | ||
const requestUrl = new URL(req.url); | ||
const paymaster = loadPaymasterKeypair(); | ||
|
||
const baseHref = new URL( | ||
`/api/actions/paymaster`, | ||
requestUrl.origin, | ||
).toString(); | ||
|
||
return { | ||
type: "action", | ||
title: "Actions Example - Paymaster", | ||
icon: new URL("/solana_devs.jpg", requestUrl.origin).toString(), | ||
description: `Have ${paymaster.publicKey.toBase58()} cover the tx fee`, | ||
label: "Paymaster", | ||
links: { | ||
actions: [ | ||
{ | ||
label: "Cover Fee", | ||
href: `${baseHref}?blink={blink}`, | ||
parameters: [ | ||
{ | ||
type: "url", | ||
name: "blink", | ||
label: "Blink URL", | ||
required: true, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
}; | ||
} | ||
|
||
const parseQueryParams = createQueryParser(GenericTransactionExtensionSchema); | ||
|
||
// http://localhost:3000/api/actions/transfer-sol?amount=0.01&to=67ZiM1TRqPFR5s2Jz1z4d6noHHBRRzt1Te6xbWmPgYF7 | ||
// http://localhost:3000/api/actions/transfer-sol?amount=0.001&to=6Le7uLy8Y2JvCq5x5huvF3pSQBvP1Y6W325wNpFz4s4u | ||
// http://localhost:3000/api/actions/transfer-spl?&amount=1&to=67ZiM1TRqPFR5s2Jz1z4d6noHHBRRzt1Te6xbWmPgYF7&mint=CzLSujWBLFsSjncfkh59rUFqvafWcY5tzedWJSuypump | ||
|
||
async function handlePost(req: Request): Promise<ActionPostResponse> { | ||
const requestUrl = new URL(req.url); | ||
const paymaster = loadPaymasterKeypair(); | ||
const { blink } = parseQueryParams(requestUrl); | ||
|
||
const body: ActionPostRequest = await req.json(); | ||
|
||
const txResponse = await fetch(blink, { | ||
method: "POST", | ||
body: JSON.stringify({ account: body.account }), | ||
}); | ||
|
||
const txResponseBody: ActionPostResponse = await txResponse.json(); | ||
const tx = VersionedTransaction.deserialize( | ||
Buffer.from(txResponseBody.transaction, "base64"), | ||
); | ||
|
||
// hydrate the message's instructions using the static account keys and lookup tables | ||
const connection = getConnection(); | ||
const LUTs = ( | ||
await Promise.all( | ||
tx.message.addressTableLookups.map((acc) => | ||
connection.getAddressLookupTable(acc.accountKey), | ||
), | ||
) | ||
) | ||
.map((lut) => lut.value) | ||
.filter((val) => val !== null) as AddressLookupTableAccount[]; | ||
|
||
// if we need to get all accounts | ||
// const allAccs = tx.message.getAccountKeys({ addressLookupTableAccounts: LUTs }) | ||
// .keySegments().reduce((acc, cur) => acc.concat(cur), []); | ||
|
||
const txMessage = TransactionMessage.decompile(tx.message, { | ||
addressLookupTableAccounts: LUTs, | ||
}); | ||
|
||
// Modify the message to use the paymaster as the payer | ||
txMessage.payerKey = paymaster.publicKey; | ||
txMessage.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; | ||
|
||
const finalTx = new VersionedTransaction(txMessage.compileToV0Message()); | ||
finalTx.sign([paymaster]); | ||
|
||
return createPostResponse({ | ||
fields: { | ||
transaction: finalTx, | ||
message: `Covered fee for "${Buffer.from(tx.serialize()).toString( | ||
"base64", | ||
)}"`, | ||
}, | ||
}); | ||
} | ||
|
||
export const { GET, POST, OPTIONS } = createActionRoutes( | ||
{ | ||
GET: handleGet, | ||
POST: handlePost, | ||
}, | ||
headers, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { z } from "zod"; | ||
import { publicKeySchema, numberFromStringSchema } from "../../utils/validation"; | ||
|
||
export const GenericTransactionExtensionSchema = z.object({ | ||
blink: z.string(), | ||
}); | ||
|
||
export type GenericTransactionExtension = z.infer<typeof GenericTransactionExtensionSchema>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 5 additions & 7 deletions
12
examples/next-js/src/app/api/actions/transfer-sol/schema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,13 @@ | ||
import { z } from "zod"; | ||
import { PublicKey } from "@solana/web3.js"; | ||
import { DEFAULT_SOL_AMOUNT } from "./const"; | ||
import { publicKeySchema, numberFromStringSchema } from "../../utils/validation"; | ||
import { numberFromStringSchema, publicKeySchema } from "../../utils/validation"; | ||
|
||
// Define the input type (what comes from URL params) | ||
export const TransferSolQuerySchema = z.object({ | ||
to: publicKeySchema, | ||
amount: numberFromStringSchema({ | ||
min: 0, | ||
description: "SOL amount" | ||
}) | ||
.optional() | ||
.default(DEFAULT_SOL_AMOUNT.toString()), | ||
amount: numberFromStringSchema({ min: 0 }), | ||
}); | ||
|
||
// Type representing the parsed and transformed data | ||
export type TransferSolQuery = z.infer<typeof TransferSolQuerySchema>; |
99 changes: 99 additions & 0 deletions
99
examples/next-js/src/app/api/actions/transfer-spl/route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { ActionGetResponse, ActionPostRequest, ActionPostResponse, createActionHeaders, createPostResponse } from "@solana/actions"; | ||
import { PublicKey, Transaction } from "@solana/web3.js"; | ||
import { getConnection } from "../../utils/connection"; | ||
import { createActionRoutes } from "../../utils/action-handler"; | ||
import { TransferSplQuerySchema } from "./schema"; | ||
import { createQueryParser } from "../../utils/validation"; | ||
import { createTransferInstruction, getAssociatedTokenAddressSync, getMint } from "@solana/spl-token"; | ||
|
||
// create the standard headers for this route (including CORS) | ||
const headers = createActionHeaders(); | ||
|
||
const parseQueryParams = createQueryParser(TransferSplQuerySchema); | ||
|
||
async function handleGet(req: Request): Promise<ActionGetResponse> { | ||
const requestUrl = new URL(req.url); | ||
const result = TransferSplQuerySchema.safeParse( | ||
Object.fromEntries(requestUrl.searchParams) | ||
); | ||
|
||
const baseHref = new URL( | ||
`/api/actions/transfer-spl?`, | ||
requestUrl.origin, | ||
).toString(); | ||
|
||
return { | ||
type: "action", | ||
title: "Actions Example - Transfer SPL Token", | ||
icon: new URL("/solana_devs.jpg", requestUrl.origin).toString(), | ||
description: "Transfer SOL to another Solana wallet", | ||
label: "Transfer", | ||
links: { | ||
actions: [ | ||
{ | ||
label: "Send SPL Token", | ||
href: `${baseHref}&amount={amount}&to={to}&mint={mint}`, | ||
parameters: [ | ||
{ | ||
name: "amount", | ||
label: "Enter the amount of SPL tokens to send", | ||
required: true, | ||
}, | ||
{ | ||
name: "to", | ||
label: "Enter the address to send SPL tokens", | ||
required: true, | ||
}, | ||
{ | ||
name: "mint", | ||
label: "Enter the SPL token mint address", | ||
required: true, | ||
}, | ||
], | ||
}, | ||
], | ||
}, | ||
}; | ||
} | ||
|
||
async function handlePost(req: Request): Promise<ActionPostResponse> { | ||
const requestUrl = new URL(req.url); | ||
const { to: toPubkey, mint, amount } = parseQueryParams(requestUrl); | ||
|
||
const body: ActionPostRequest = await req.json(); | ||
const account = new PublicKey(body.account); | ||
|
||
const connection = getConnection(); | ||
// get ATAs | ||
const mintInfo = await getMint(connection, mint, "confirmed"); | ||
const sourceAta = getAssociatedTokenAddressSync(mint, account); | ||
const destinationAta = getAssociatedTokenAddressSync(mint, toPubkey); | ||
// get the true amount of tokens to transfer | ||
const amountToTransfer = BigInt(amount) * BigInt(10 ** mintInfo.decimals); | ||
const transferSplInstruction = createTransferInstruction(sourceAta, destinationAta, account, amountToTransfer); | ||
|
||
const { blockhash, lastValidBlockHeight } = | ||
await connection.getLatestBlockhash(); | ||
|
||
const transaction = new Transaction({ | ||
feePayer: account, | ||
blockhash, | ||
lastValidBlockHeight, | ||
}).add(transferSplInstruction); | ||
|
||
return createPostResponse({ | ||
fields: { | ||
transaction, | ||
message: `Send ${amount} ${mint.toBase58()} to ${toPubkey.toBase58()}`, | ||
}, | ||
}); | ||
} | ||
|
||
|
||
export const { GET, POST, OPTIONS } = createActionRoutes( | ||
{ | ||
GET: handleGet, | ||
POST: handlePost, | ||
}, | ||
headers, | ||
); |
13 changes: 13 additions & 0 deletions
13
examples/next-js/src/app/api/actions/transfer-spl/schema.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { z } from "zod"; | ||
import { publicKeySchema, numberFromStringSchema } from "../../utils/validation"; | ||
|
||
export const TransferSplQuerySchema = z.object({ | ||
to: publicKeySchema, | ||
mint: publicKeySchema, | ||
amount: numberFromStringSchema({ | ||
min: 1, | ||
description: "SPL token amount" | ||
}) | ||
}); | ||
|
||
export type TransferSolQuery = z.infer<typeof TransferSplQuerySchema>; |
Oops, something went wrong.