From 2b60ad875e482ca158152ad3ee6d48af57836856 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 00:50:12 +0000 Subject: [PATCH 1/9] Add missing examples to READMEs, add delegate-transfer - Document delegate-transfer, create-ata-explicit-rent-sponsor, create-spl-mint, create-t22-mint, create-spl-interface in root README - Add delegate-transfer, create-ata-explicit-rent-sponsor, create-token-pool to typescript-client README - Add delegate-transfer action (transferDelegatedInterface) - Update delegate-approve and delegate-revoke Entire-Checkpoint: 4afa49be1970 --- README.md | 5 ++ typescript-client/README.md | 3 + typescript-client/actions/delegate-approve.ts | 16 ++++- typescript-client/actions/delegate-revoke.ts | 24 ++++--- .../actions/delegate-transfer.ts | 63 +++++++++++++++++++ 5 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 typescript-client/actions/delegate-transfer.ts diff --git a/README.md b/README.md index e5fb9464..239a1081 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,11 @@ Light token is a high-performance token standard that reduces the cost of mint a | | | | Description | |---------|--------|-------------|-------------| | create-mint | [Action](typescript-client/actions/create-mint.ts) | [Instruction](typescript-client/instructions/create-mint.ts) | Create a light-token mint with metadata | +| create-spl-mint | [Action](typescript-client/actions/create-spl-mint.ts) | [Instruction](typescript-client/instructions/create-spl-mint.ts) | Create an SPL mint with SPL interface PDA | +| create-t22-mint | [Action](typescript-client/actions/create-t22-mint.ts) | [Instruction](typescript-client/instructions/create-t22-mint.ts) | Create a Token 2022 mint with SPL interface PDA | +| create-spl-interface | [Action](typescript-client/actions/create-spl-interface.ts) | [Instruction](typescript-client/instructions/create-spl-interface.ts) | Register SPL interface PDA for an existing mint | | create-ata | [Action](typescript-client/actions/create-ata.ts) | [Instruction](typescript-client/instructions/create-ata.ts) | Create an associated light-token account | +| create-ata-explicit-rent-sponsor | [Action](typescript-client/actions/create-ata-explicit-rent-sponsor.ts) | | Create an ATA with explicit rent sponsor | | load-ata | [Action](typescript-client/actions/load-ata.ts) | [Instruction](typescript-client/instructions/load-ata.ts) | Load token accounts from light-token, compressed tokens, SPL/T22 to one unified balance | | mint-to | [Action](typescript-client/actions/mint-to.ts) | [Instruction](typescript-client/instructions/mint-to.ts) | Mint tokens to a light-account | | transfer-interface | [Action](typescript-client/actions/transfer-interface.ts) | [Instruction](typescript-client/instructions/transfer-interface.ts) | Transfer between light-token, T22, and SPL accounts | @@ -32,6 +36,7 @@ Light token is a high-performance token standard that reduces the cost of mint a | unwrap | [Action](typescript-client/actions/unwrap.ts) | [Instruction](typescript-client/instructions/unwrap.ts) | Unwrap light-token to SPL/T22 | | approve | [Action](typescript-client/actions/delegate-approve.ts) | | Approve delegate | | revoke | [Action](typescript-client/actions/delegate-revoke.ts) | | Revoke delegate | +| delegate-transfer | [Action](typescript-client/actions/delegate-transfer.ts) | | Delegate transfers tokens on behalf of owner | ### Rust diff --git a/typescript-client/README.md b/typescript-client/README.md index 2c6cf2fe..f84dc212 100644 --- a/typescript-client/README.md +++ b/typescript-client/README.md @@ -10,8 +10,10 @@ TypeScript client examples for light-token-sdk. - **[load-ata](actions/load-ata.ts)** - Load token accounts from light-token, compressed tokens, SPL/T22 to one unified balance - **[mint-to](actions/mint-to.ts)** - Mint tokens to a light-account - **[transfer-interface](actions/transfer-interface.ts)** - Transfer between light-token, T22, and SPL accounts +- **[create-ata-explicit-rent-sponsor](actions/create-ata-explicit-rent-sponsor.ts)** - Create an ATA with explicit rent sponsor - **[delegate-approve](actions/delegate-approve.ts)** - Approve delegate - **[delegate-revoke](actions/delegate-revoke.ts)** - Revoke delegate +- **[delegate-transfer](actions/delegate-transfer.ts)** - Delegate transfers tokens on behalf of owner - **[wrap](actions/wrap.ts)** - Wrap SPL/T22 to light-token - **[unwrap](actions/unwrap.ts)** - Unwrap light-token to SPL/T22 @@ -27,6 +29,7 @@ TypeScript client examples for light-token-sdk. - **[transfer-interface](instructions/transfer-interface.ts)** - Build transfer instruction - **[wrap](instructions/wrap.ts)** - Wrap SPL/T22 to light-token - **[unwrap](instructions/unwrap.ts)** - Unwrap light-token to SPL/T22 +- **[create-token-pool](instructions/create-token-pool.ts)** - Create a token pool ## Setup diff --git a/typescript-client/actions/delegate-approve.ts b/typescript-client/actions/delegate-approve.ts index bb0c59d6..8d0212ef 100644 --- a/typescript-client/actions/delegate-approve.ts +++ b/typescript-client/actions/delegate-approve.ts @@ -4,8 +4,9 @@ import { createRpc } from "@lightprotocol/stateless.js"; import { createMintInterface, mintToCompressed, - approve, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { approveInterface } from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -27,8 +28,19 @@ const payer = Keypair.fromSecretKey( { recipient: payer.publicKey, amount: 1000n }, ]); + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); const delegate = Keypair.generate(); - const tx = await approve(rpc, payer, mint, 500, payer, delegate.publicKey); + const tx = await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + console.log("Allowance: 500,000 tokens"); console.log("Tx:", tx); })(); diff --git a/typescript-client/actions/delegate-revoke.ts b/typescript-client/actions/delegate-revoke.ts index 7eb3d068..0badd23b 100644 --- a/typescript-client/actions/delegate-revoke.ts +++ b/typescript-client/actions/delegate-revoke.ts @@ -4,9 +4,12 @@ import { createRpc } from "@lightprotocol/stateless.js"; import { createMintInterface, mintToCompressed, - approve, - revoke, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { + approveInterface, + revokeInterface, +} from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -28,14 +31,21 @@ const payer = Keypair.fromSecretKey( { recipient: payer.publicKey, amount: 1000n }, ]); + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); const delegate = Keypair.generate(); - await approve(rpc, payer, mint, 500, payer, delegate.publicKey); - - const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate( + await approveInterface( + rpc, + payer, + senderAta, + mint, delegate.publicKey, - { mint } + 500_000, + payer ); - const tx = await revoke(rpc, payer, delegatedAccounts.items, payer); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + const tx = await revokeInterface(rpc, payer, senderAta, mint, payer); + console.log("Revoked all delegate permissions"); console.log("Tx:", tx); })(); diff --git a/typescript-client/actions/delegate-transfer.ts b/typescript-client/actions/delegate-transfer.ts new file mode 100644 index 00000000..d9f147bb --- /dev/null +++ b/typescript-client/actions/delegate-transfer.ts @@ -0,0 +1,63 @@ +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + mintToCompressed, + getAssociatedTokenAddressInterface, +} from "@lightprotocol/compressed-token"; +import { + approveInterface, + transferDelegatedInterface, +} from "@lightprotocol/compressed-token/unified"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + await mintToCompressed(rpc, payer, mint, payer, [ + { recipient: payer.publicKey, amount: 1000n }, + ]); + + const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + const delegate = Keypair.generate(); + const recipient = Keypair.generate(); + + // Approve delegate + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Delegate transfers tokens on behalf of the owner + const tx = await transferDelegatedInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + delegate, + payer.publicKey, + 200_000 + ); + + console.log("Delegated transfer:", tx); +})(); From d871e4843c059405f8d71e71075293db4cf420e8 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 16:09:55 +0000 Subject: [PATCH 2/9] Add delegation instruction examples, delegate-transfer to payments toolkit - Update delegate-transfer to use transferInterface + { owner } option (replaces transferDelegatedInterface) - Add instruction-level delegate-approve and delegate-revoke examples using createApproveInterfaceInstructions / createRevokeInterfaceInstructions - Add delegate-transfer example to payments spend-permissions - Remove stale create-token-pool instruction example - Update READMEs and package.json scripts Entire-Checkpoint: 4afa49be1970 --- toolkits/payments/README.md | 1 + toolkits/payments/package.json | 3 +- .../spend-permissions/delegate-transfer.ts | 42 ++++++++++ typescript-client/README.md | 3 +- .../actions/delegate-transfer.ts | 10 ++- .../instructions/create-token-pool.ts | 40 ---------- .../instructions/delegate-approve.ts | 65 +++++++++++++++ .../instructions/delegate-revoke.ts | 80 +++++++++++++++++++ typescript-client/package.json | 3 + 9 files changed, 201 insertions(+), 46 deletions(-) create mode 100644 toolkits/payments/spend-permissions/delegate-transfer.ts delete mode 100644 typescript-client/instructions/create-token-pool.ts create mode 100644 typescript-client/instructions/delegate-approve.ts create mode 100644 typescript-client/instructions/delegate-revoke.ts diff --git a/toolkits/payments/README.md b/toolkits/payments/README.md index 15a27e47..5748e32a 100644 --- a/toolkits/payments/README.md +++ b/toolkits/payments/README.md @@ -53,6 +53,7 @@ Light Token reduces account creation cost by 200x compared to SPL. Transfer cost | [delegate-approve.ts](spend-permissions/delegate-approve.ts) | Let a delegate spend tokens on your behalf. | `approveInterface` | | [delegate-revoke.ts](spend-permissions/delegate-revoke.ts) | Revoke delegate access. | `revokeInterface` | | [delegate-check.ts](spend-permissions/delegate-check.ts) | Check current delegation status and remaining allowance. | `getAtaInterface` | +| [delegate-transfer.ts](spend-permissions/delegate-transfer.ts) | Delegate transfers tokens on behalf of the owner. | `transferInterface` | | [delegate-full-flow.ts](spend-permissions/delegate-full-flow.ts) | Approve, check, and revoke in one script. | `approveInterface`, `revokeInterface`, `getAtaInterface` | ### Interop (wrap and unwrap) diff --git a/toolkits/payments/package.json b/toolkits/payments/package.json index fd19fce8..472c853e 100644 --- a/toolkits/payments/package.json +++ b/toolkits/payments/package.json @@ -19,7 +19,8 @@ "delegate-approve": "tsx spend-permissions/delegate-approve.ts", "delegate-revoke": "tsx spend-permissions/delegate-revoke.ts", "delegate-check": "tsx spend-permissions/delegate-check.ts", - "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts" + "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts", + "delegate-transfer": "tsx spend-permissions/delegate-transfer.ts" }, "dependencies": { "@lightprotocol/compressed-token": "^0.23.0-beta.10", diff --git a/toolkits/payments/spend-permissions/delegate-transfer.ts b/toolkits/payments/spend-permissions/delegate-transfer.ts new file mode 100644 index 00000000..216107ca --- /dev/null +++ b/toolkits/payments/spend-permissions/delegate-transfer.ts @@ -0,0 +1,42 @@ +import { Keypair } from "@solana/web3.js"; +import { + approveInterface, + transferInterface, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Approve: grant delegate permission to spend up to 500,000 tokens + const delegate = Keypair.generate(); + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Delegate transfers tokens on behalf of the owner + const recipient = Keypair.generate(); + const tx = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + delegate, + 200_000, + undefined, + undefined, + { owner: payer.publicKey } + ); + + console.log("Delegated transfer to:", recipient.publicKey.toBase58()); + console.log("Amount: 200,000 tokens"); + console.log("Tx:", tx); +})(); diff --git a/typescript-client/README.md b/typescript-client/README.md index f84dc212..02f19109 100644 --- a/typescript-client/README.md +++ b/typescript-client/README.md @@ -29,7 +29,8 @@ TypeScript client examples for light-token-sdk. - **[transfer-interface](instructions/transfer-interface.ts)** - Build transfer instruction - **[wrap](instructions/wrap.ts)** - Wrap SPL/T22 to light-token - **[unwrap](instructions/unwrap.ts)** - Unwrap light-token to SPL/T22 -- **[create-token-pool](instructions/create-token-pool.ts)** - Create a token pool +- **[delegate-approve](instructions/delegate-approve.ts)** - Build approve delegate instructions +- **[delegate-revoke](instructions/delegate-revoke.ts)** - Build revoke delegate instructions ## Setup diff --git a/typescript-client/actions/delegate-transfer.ts b/typescript-client/actions/delegate-transfer.ts index d9f147bb..eea04e56 100644 --- a/typescript-client/actions/delegate-transfer.ts +++ b/typescript-client/actions/delegate-transfer.ts @@ -8,7 +8,7 @@ import { } from "@lightprotocol/compressed-token"; import { approveInterface, - transferDelegatedInterface, + transferInterface, } from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -48,15 +48,17 @@ const payer = Keypair.fromSecretKey( console.log("Approved delegate:", delegate.publicKey.toBase58()); // Delegate transfers tokens on behalf of the owner - const tx = await transferDelegatedInterface( + const tx = await transferInterface( rpc, payer, senderAta, mint, recipient.publicKey, delegate, - payer.publicKey, - 200_000 + 200_000, + undefined, + undefined, + { owner: payer.publicKey } ); console.log("Delegated transfer:", tx); diff --git a/typescript-client/instructions/create-token-pool.ts b/typescript-client/instructions/create-token-pool.ts deleted file mode 100644 index 7f375705..00000000 --- a/typescript-client/instructions/create-token-pool.ts +++ /dev/null @@ -1,40 +0,0 @@ -import "dotenv/config"; -import { - Keypair, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { LightTokenProgram } from "@lightprotocol/compressed-token"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// const rpc = createRpc(RPC_URL); -// localnet: -const rpc = createRpc(); - -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - const existingMint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS"); - - const ix = await LightTokenProgram.createSplInterface({ - feePayer: payer.publicKey, - mint: existingMint, - tokenProgramId: TOKEN_PROGRAM_ID, - }); - - const tx = new Transaction().add(ix); - const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); - - console.log("Mint:", existingMint.toBase58()); - console.log("Tx:", signature); -})(); diff --git a/typescript-client/instructions/delegate-approve.ts b/typescript-client/instructions/delegate-approve.ts new file mode 100644 index 00000000..f503cd5b --- /dev/null +++ b/typescript-client/instructions/delegate-approve.ts @@ -0,0 +1,65 @@ +import "dotenv/config"; +import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, + createApproveInterfaceInstructions, +} from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + + const owner = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, owner.publicKey); + const ownerAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + await mintToInterface(rpc, payer, mint, ownerAta, payer, 1_000_000_000); + + const delegate = Keypair.generate(); + + // Build approve instruction batches. + // Returns TransactionInstruction[][] — send [0..n-2] first (load batches), + // then send [n-1] (the approve instruction). + const batches = await createApproveInterfaceInstructions( + rpc, + payer.publicKey, + mint, + ownerAta, + delegate.publicKey, + 500_000_000, + owner.publicKey, + 9 + ); + + // Send load batches in parallel, then the final approve batch + for (let i = 0; i < batches.length - 1; i++) { + const tx = new Transaction().add(...batches[i]); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } + + const approveTx = new Transaction().add(...batches[batches.length - 1]); + const signature = await sendAndConfirmTransaction(rpc, approveTx, [ + payer, + owner, + ]); + + console.log("Approved delegate:", delegate.publicKey.toBase58()); + console.log("Allowance: 500,000,000 tokens"); + console.log("Tx:", signature); +})(); diff --git a/typescript-client/instructions/delegate-revoke.ts b/typescript-client/instructions/delegate-revoke.ts new file mode 100644 index 00000000..9f339a1f --- /dev/null +++ b/typescript-client/instructions/delegate-revoke.ts @@ -0,0 +1,80 @@ +import "dotenv/config"; +import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, + createApproveInterfaceInstructions, + createRevokeInterfaceInstructions, +} from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + + const owner = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, owner.publicKey); + const ownerAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + await mintToInterface(rpc, payer, mint, ownerAta, payer, 1_000_000_000); + + // Approve a delegate first (so we have something to revoke) + const delegate = Keypair.generate(); + const approveBatches = await createApproveInterfaceInstructions( + rpc, + payer.publicKey, + mint, + ownerAta, + delegate.publicKey, + 500_000_000, + owner.publicKey, + 9 + ); + for (const batch of approveBatches) { + const tx = new Transaction().add(...batch); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Build revoke instruction batches. + // Returns TransactionInstruction[][] — send [0..n-2] first (load batches), + // then send [n-1] (the revoke instruction). + const revokeBatches = await createRevokeInterfaceInstructions( + rpc, + payer.publicKey, + mint, + ownerAta, + owner.publicKey, + 9 + ); + + for (let i = 0; i < revokeBatches.length - 1; i++) { + const tx = new Transaction().add(...revokeBatches[i]); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } + + const revokeTx = new Transaction().add( + ...revokeBatches[revokeBatches.length - 1] + ); + const signature = await sendAndConfirmTransaction(rpc, revokeTx, [ + payer, + owner, + ]); + + console.log("Revoked all delegate permissions"); + console.log("Tx:", signature); +})(); diff --git a/typescript-client/package.json b/typescript-client/package.json index d94ae090..a0b2c947 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -42,6 +42,9 @@ "load-ata:instruction": "tsx instructions/load-ata.ts", "delegate:approve": "tsx actions/delegate-approve.ts", "delegate:revoke": "tsx actions/delegate-revoke.ts", + "delegate:transfer": "tsx actions/delegate-transfer.ts", + "delegate-approve:instruction": "tsx instructions/delegate-approve.ts", + "delegate-revoke:instruction": "tsx instructions/delegate-revoke.ts", "merge-accounts": "tsx actions/merge-token-accounts.ts" } } From 9a6ac7b492793fdabcc0b347c6fc0d5e45af9ba1 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 16:16:49 +0000 Subject: [PATCH 3/9] Revert "Add delegation instruction examples, delegate-transfer to payments toolkit" This reverts commit d871e4843c059405f8d71e71075293db4cf420e8. Entire-Checkpoint: 4afa49be1970 --- toolkits/payments/README.md | 1 - toolkits/payments/package.json | 3 +- .../spend-permissions/delegate-transfer.ts | 42 ---------- typescript-client/README.md | 3 +- .../actions/delegate-transfer.ts | 10 +-- .../instructions/create-token-pool.ts | 40 ++++++++++ .../instructions/delegate-approve.ts | 65 --------------- .../instructions/delegate-revoke.ts | 80 ------------------- typescript-client/package.json | 3 - 9 files changed, 46 insertions(+), 201 deletions(-) delete mode 100644 toolkits/payments/spend-permissions/delegate-transfer.ts create mode 100644 typescript-client/instructions/create-token-pool.ts delete mode 100644 typescript-client/instructions/delegate-approve.ts delete mode 100644 typescript-client/instructions/delegate-revoke.ts diff --git a/toolkits/payments/README.md b/toolkits/payments/README.md index 5748e32a..15a27e47 100644 --- a/toolkits/payments/README.md +++ b/toolkits/payments/README.md @@ -53,7 +53,6 @@ Light Token reduces account creation cost by 200x compared to SPL. Transfer cost | [delegate-approve.ts](spend-permissions/delegate-approve.ts) | Let a delegate spend tokens on your behalf. | `approveInterface` | | [delegate-revoke.ts](spend-permissions/delegate-revoke.ts) | Revoke delegate access. | `revokeInterface` | | [delegate-check.ts](spend-permissions/delegate-check.ts) | Check current delegation status and remaining allowance. | `getAtaInterface` | -| [delegate-transfer.ts](spend-permissions/delegate-transfer.ts) | Delegate transfers tokens on behalf of the owner. | `transferInterface` | | [delegate-full-flow.ts](spend-permissions/delegate-full-flow.ts) | Approve, check, and revoke in one script. | `approveInterface`, `revokeInterface`, `getAtaInterface` | ### Interop (wrap and unwrap) diff --git a/toolkits/payments/package.json b/toolkits/payments/package.json index 472c853e..fd19fce8 100644 --- a/toolkits/payments/package.json +++ b/toolkits/payments/package.json @@ -19,8 +19,7 @@ "delegate-approve": "tsx spend-permissions/delegate-approve.ts", "delegate-revoke": "tsx spend-permissions/delegate-revoke.ts", "delegate-check": "tsx spend-permissions/delegate-check.ts", - "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts", - "delegate-transfer": "tsx spend-permissions/delegate-transfer.ts" + "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts" }, "dependencies": { "@lightprotocol/compressed-token": "^0.23.0-beta.10", diff --git a/toolkits/payments/spend-permissions/delegate-transfer.ts b/toolkits/payments/spend-permissions/delegate-transfer.ts deleted file mode 100644 index 216107ca..00000000 --- a/toolkits/payments/spend-permissions/delegate-transfer.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Keypair } from "@solana/web3.js"; -import { - approveInterface, - transferInterface, -} from "@lightprotocol/compressed-token/unified"; -import { rpc, payer, setup } from "../setup.js"; - -(async function () { - const { mint, senderAta } = await setup(); - - // Approve: grant delegate permission to spend up to 500,000 tokens - const delegate = Keypair.generate(); - await approveInterface( - rpc, - payer, - senderAta, - mint, - delegate.publicKey, - 500_000, - payer - ); - console.log("Approved delegate:", delegate.publicKey.toBase58()); - - // Delegate transfers tokens on behalf of the owner - const recipient = Keypair.generate(); - const tx = await transferInterface( - rpc, - payer, - senderAta, - mint, - recipient.publicKey, - delegate, - 200_000, - undefined, - undefined, - { owner: payer.publicKey } - ); - - console.log("Delegated transfer to:", recipient.publicKey.toBase58()); - console.log("Amount: 200,000 tokens"); - console.log("Tx:", tx); -})(); diff --git a/typescript-client/README.md b/typescript-client/README.md index 02f19109..f84dc212 100644 --- a/typescript-client/README.md +++ b/typescript-client/README.md @@ -29,8 +29,7 @@ TypeScript client examples for light-token-sdk. - **[transfer-interface](instructions/transfer-interface.ts)** - Build transfer instruction - **[wrap](instructions/wrap.ts)** - Wrap SPL/T22 to light-token - **[unwrap](instructions/unwrap.ts)** - Unwrap light-token to SPL/T22 -- **[delegate-approve](instructions/delegate-approve.ts)** - Build approve delegate instructions -- **[delegate-revoke](instructions/delegate-revoke.ts)** - Build revoke delegate instructions +- **[create-token-pool](instructions/create-token-pool.ts)** - Create a token pool ## Setup diff --git a/typescript-client/actions/delegate-transfer.ts b/typescript-client/actions/delegate-transfer.ts index eea04e56..d9f147bb 100644 --- a/typescript-client/actions/delegate-transfer.ts +++ b/typescript-client/actions/delegate-transfer.ts @@ -8,7 +8,7 @@ import { } from "@lightprotocol/compressed-token"; import { approveInterface, - transferInterface, + transferDelegatedInterface, } from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -48,17 +48,15 @@ const payer = Keypair.fromSecretKey( console.log("Approved delegate:", delegate.publicKey.toBase58()); // Delegate transfers tokens on behalf of the owner - const tx = await transferInterface( + const tx = await transferDelegatedInterface( rpc, payer, senderAta, mint, recipient.publicKey, delegate, - 200_000, - undefined, - undefined, - { owner: payer.publicKey } + payer.publicKey, + 200_000 ); console.log("Delegated transfer:", tx); diff --git a/typescript-client/instructions/create-token-pool.ts b/typescript-client/instructions/create-token-pool.ts new file mode 100644 index 00000000..7f375705 --- /dev/null +++ b/typescript-client/instructions/create-token-pool.ts @@ -0,0 +1,40 @@ +import "dotenv/config"; +import { + Keypair, + PublicKey, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { LightTokenProgram } from "@lightprotocol/compressed-token"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const existingMint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS"); + + const ix = await LightTokenProgram.createSplInterface({ + feePayer: payer.publicKey, + mint: existingMint, + tokenProgramId: TOKEN_PROGRAM_ID, + }); + + const tx = new Transaction().add(ix); + const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); + + console.log("Mint:", existingMint.toBase58()); + console.log("Tx:", signature); +})(); diff --git a/typescript-client/instructions/delegate-approve.ts b/typescript-client/instructions/delegate-approve.ts deleted file mode 100644 index f503cd5b..00000000 --- a/typescript-client/instructions/delegate-approve.ts +++ /dev/null @@ -1,65 +0,0 @@ -import "dotenv/config"; -import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - mintToInterface, - getAssociatedTokenAddressInterface, - createApproveInterfaceInstructions, -} from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// const rpc = createRpc(RPC_URL); -// localnet: -const rpc = createRpc(); - -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - - const owner = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, owner.publicKey); - const ownerAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - await mintToInterface(rpc, payer, mint, ownerAta, payer, 1_000_000_000); - - const delegate = Keypair.generate(); - - // Build approve instruction batches. - // Returns TransactionInstruction[][] — send [0..n-2] first (load batches), - // then send [n-1] (the approve instruction). - const batches = await createApproveInterfaceInstructions( - rpc, - payer.publicKey, - mint, - ownerAta, - delegate.publicKey, - 500_000_000, - owner.publicKey, - 9 - ); - - // Send load batches in parallel, then the final approve batch - for (let i = 0; i < batches.length - 1; i++) { - const tx = new Transaction().add(...batches[i]); - await sendAndConfirmTransaction(rpc, tx, [payer, owner]); - } - - const approveTx = new Transaction().add(...batches[batches.length - 1]); - const signature = await sendAndConfirmTransaction(rpc, approveTx, [ - payer, - owner, - ]); - - console.log("Approved delegate:", delegate.publicKey.toBase58()); - console.log("Allowance: 500,000,000 tokens"); - console.log("Tx:", signature); -})(); diff --git a/typescript-client/instructions/delegate-revoke.ts b/typescript-client/instructions/delegate-revoke.ts deleted file mode 100644 index 9f339a1f..00000000 --- a/typescript-client/instructions/delegate-revoke.ts +++ /dev/null @@ -1,80 +0,0 @@ -import "dotenv/config"; -import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - mintToInterface, - getAssociatedTokenAddressInterface, - createApproveInterfaceInstructions, - createRevokeInterfaceInstructions, -} from "@lightprotocol/compressed-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// const rpc = createRpc(RPC_URL); -// localnet: -const rpc = createRpc(); - -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - const { mint } = await createMintInterface(rpc, payer, payer, null, 9); - - const owner = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, owner.publicKey); - const ownerAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - await mintToInterface(rpc, payer, mint, ownerAta, payer, 1_000_000_000); - - // Approve a delegate first (so we have something to revoke) - const delegate = Keypair.generate(); - const approveBatches = await createApproveInterfaceInstructions( - rpc, - payer.publicKey, - mint, - ownerAta, - delegate.publicKey, - 500_000_000, - owner.publicKey, - 9 - ); - for (const batch of approveBatches) { - const tx = new Transaction().add(...batch); - await sendAndConfirmTransaction(rpc, tx, [payer, owner]); - } - console.log("Approved delegate:", delegate.publicKey.toBase58()); - - // Build revoke instruction batches. - // Returns TransactionInstruction[][] — send [0..n-2] first (load batches), - // then send [n-1] (the revoke instruction). - const revokeBatches = await createRevokeInterfaceInstructions( - rpc, - payer.publicKey, - mint, - ownerAta, - owner.publicKey, - 9 - ); - - for (let i = 0; i < revokeBatches.length - 1; i++) { - const tx = new Transaction().add(...revokeBatches[i]); - await sendAndConfirmTransaction(rpc, tx, [payer, owner]); - } - - const revokeTx = new Transaction().add( - ...revokeBatches[revokeBatches.length - 1] - ); - const signature = await sendAndConfirmTransaction(rpc, revokeTx, [ - payer, - owner, - ]); - - console.log("Revoked all delegate permissions"); - console.log("Tx:", signature); -})(); diff --git a/typescript-client/package.json b/typescript-client/package.json index a0b2c947..d94ae090 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -42,9 +42,6 @@ "load-ata:instruction": "tsx instructions/load-ata.ts", "delegate:approve": "tsx actions/delegate-approve.ts", "delegate:revoke": "tsx actions/delegate-revoke.ts", - "delegate:transfer": "tsx actions/delegate-transfer.ts", - "delegate-approve:instruction": "tsx instructions/delegate-approve.ts", - "delegate-revoke:instruction": "tsx instructions/delegate-revoke.ts", "merge-accounts": "tsx actions/merge-token-accounts.ts" } } From f05c7adc3718e6bab5568130f022e0cfa60a5b30 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 16:18:26 +0000 Subject: [PATCH 4/9] Add delegation instruction examples, delegate-transfer to payments toolkit - Update delegate-transfer to use transferInterface + { owner } option (replaces transferDelegatedInterface) - Add instruction-level delegate-approve and delegate-revoke examples using createApproveInterfaceInstructions / createRevokeInterfaceInstructions - Add delegate-transfer example to payments spend-permissions - Remove stale create-token-pool instruction example - Update READMEs and package.json scripts Entire-Checkpoint: 4afa49be1970 --- toolkits/payments/README.md | 1 + toolkits/payments/package.json | 3 +- .../spend-permissions/delegate-transfer.ts | 42 ++++++++++ typescript-client/README.md | 3 +- .../actions/delegate-transfer.ts | 10 ++- .../instructions/create-token-pool.ts | 40 ---------- .../instructions/delegate-approve.ts | 65 +++++++++++++++ .../instructions/delegate-revoke.ts | 80 +++++++++++++++++++ typescript-client/package.json | 3 + 9 files changed, 201 insertions(+), 46 deletions(-) create mode 100644 toolkits/payments/spend-permissions/delegate-transfer.ts delete mode 100644 typescript-client/instructions/create-token-pool.ts create mode 100644 typescript-client/instructions/delegate-approve.ts create mode 100644 typescript-client/instructions/delegate-revoke.ts diff --git a/toolkits/payments/README.md b/toolkits/payments/README.md index 15a27e47..5748e32a 100644 --- a/toolkits/payments/README.md +++ b/toolkits/payments/README.md @@ -53,6 +53,7 @@ Light Token reduces account creation cost by 200x compared to SPL. Transfer cost | [delegate-approve.ts](spend-permissions/delegate-approve.ts) | Let a delegate spend tokens on your behalf. | `approveInterface` | | [delegate-revoke.ts](spend-permissions/delegate-revoke.ts) | Revoke delegate access. | `revokeInterface` | | [delegate-check.ts](spend-permissions/delegate-check.ts) | Check current delegation status and remaining allowance. | `getAtaInterface` | +| [delegate-transfer.ts](spend-permissions/delegate-transfer.ts) | Delegate transfers tokens on behalf of the owner. | `transferInterface` | | [delegate-full-flow.ts](spend-permissions/delegate-full-flow.ts) | Approve, check, and revoke in one script. | `approveInterface`, `revokeInterface`, `getAtaInterface` | ### Interop (wrap and unwrap) diff --git a/toolkits/payments/package.json b/toolkits/payments/package.json index fd19fce8..472c853e 100644 --- a/toolkits/payments/package.json +++ b/toolkits/payments/package.json @@ -19,7 +19,8 @@ "delegate-approve": "tsx spend-permissions/delegate-approve.ts", "delegate-revoke": "tsx spend-permissions/delegate-revoke.ts", "delegate-check": "tsx spend-permissions/delegate-check.ts", - "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts" + "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts", + "delegate-transfer": "tsx spend-permissions/delegate-transfer.ts" }, "dependencies": { "@lightprotocol/compressed-token": "^0.23.0-beta.10", diff --git a/toolkits/payments/spend-permissions/delegate-transfer.ts b/toolkits/payments/spend-permissions/delegate-transfer.ts new file mode 100644 index 00000000..216107ca --- /dev/null +++ b/toolkits/payments/spend-permissions/delegate-transfer.ts @@ -0,0 +1,42 @@ +import { Keypair } from "@solana/web3.js"; +import { + approveInterface, + transferInterface, +} from "@lightprotocol/compressed-token/unified"; +import { rpc, payer, setup } from "../setup.js"; + +(async function () { + const { mint, senderAta } = await setup(); + + // Approve: grant delegate permission to spend up to 500,000 tokens + const delegate = Keypair.generate(); + await approveInterface( + rpc, + payer, + senderAta, + mint, + delegate.publicKey, + 500_000, + payer + ); + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Delegate transfers tokens on behalf of the owner + const recipient = Keypair.generate(); + const tx = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipient.publicKey, + delegate, + 200_000, + undefined, + undefined, + { owner: payer.publicKey } + ); + + console.log("Delegated transfer to:", recipient.publicKey.toBase58()); + console.log("Amount: 200,000 tokens"); + console.log("Tx:", tx); +})(); diff --git a/typescript-client/README.md b/typescript-client/README.md index f84dc212..02f19109 100644 --- a/typescript-client/README.md +++ b/typescript-client/README.md @@ -29,7 +29,8 @@ TypeScript client examples for light-token-sdk. - **[transfer-interface](instructions/transfer-interface.ts)** - Build transfer instruction - **[wrap](instructions/wrap.ts)** - Wrap SPL/T22 to light-token - **[unwrap](instructions/unwrap.ts)** - Unwrap light-token to SPL/T22 -- **[create-token-pool](instructions/create-token-pool.ts)** - Create a token pool +- **[delegate-approve](instructions/delegate-approve.ts)** - Build approve delegate instructions +- **[delegate-revoke](instructions/delegate-revoke.ts)** - Build revoke delegate instructions ## Setup diff --git a/typescript-client/actions/delegate-transfer.ts b/typescript-client/actions/delegate-transfer.ts index d9f147bb..eea04e56 100644 --- a/typescript-client/actions/delegate-transfer.ts +++ b/typescript-client/actions/delegate-transfer.ts @@ -8,7 +8,7 @@ import { } from "@lightprotocol/compressed-token"; import { approveInterface, - transferDelegatedInterface, + transferInterface, } from "@lightprotocol/compressed-token/unified"; import { homedir } from "os"; import { readFileSync } from "fs"; @@ -48,15 +48,17 @@ const payer = Keypair.fromSecretKey( console.log("Approved delegate:", delegate.publicKey.toBase58()); // Delegate transfers tokens on behalf of the owner - const tx = await transferDelegatedInterface( + const tx = await transferInterface( rpc, payer, senderAta, mint, recipient.publicKey, delegate, - payer.publicKey, - 200_000 + 200_000, + undefined, + undefined, + { owner: payer.publicKey } ); console.log("Delegated transfer:", tx); diff --git a/typescript-client/instructions/create-token-pool.ts b/typescript-client/instructions/create-token-pool.ts deleted file mode 100644 index 7f375705..00000000 --- a/typescript-client/instructions/create-token-pool.ts +++ /dev/null @@ -1,40 +0,0 @@ -import "dotenv/config"; -import { - Keypair, - PublicKey, - Transaction, - sendAndConfirmTransaction, -} from "@solana/web3.js"; -import { createRpc } from "@lightprotocol/stateless.js"; -import { LightTokenProgram } from "@lightprotocol/compressed-token"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { homedir } from "os"; -import { readFileSync } from "fs"; - -// devnet: -// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; -// const rpc = createRpc(RPC_URL); -// localnet: -const rpc = createRpc(); - -const payer = Keypair.fromSecretKey( - new Uint8Array( - JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) - ) -); - -(async function () { - const existingMint = new PublicKey("YOUR_EXISTING_MINT_ADDRESS"); - - const ix = await LightTokenProgram.createSplInterface({ - feePayer: payer.publicKey, - mint: existingMint, - tokenProgramId: TOKEN_PROGRAM_ID, - }); - - const tx = new Transaction().add(ix); - const signature = await sendAndConfirmTransaction(rpc, tx, [payer]); - - console.log("Mint:", existingMint.toBase58()); - console.log("Tx:", signature); -})(); diff --git a/typescript-client/instructions/delegate-approve.ts b/typescript-client/instructions/delegate-approve.ts new file mode 100644 index 00000000..f503cd5b --- /dev/null +++ b/typescript-client/instructions/delegate-approve.ts @@ -0,0 +1,65 @@ +import "dotenv/config"; +import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, + createApproveInterfaceInstructions, +} from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + + const owner = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, owner.publicKey); + const ownerAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + await mintToInterface(rpc, payer, mint, ownerAta, payer, 1_000_000_000); + + const delegate = Keypair.generate(); + + // Build approve instruction batches. + // Returns TransactionInstruction[][] — send [0..n-2] first (load batches), + // then send [n-1] (the approve instruction). + const batches = await createApproveInterfaceInstructions( + rpc, + payer.publicKey, + mint, + ownerAta, + delegate.publicKey, + 500_000_000, + owner.publicKey, + 9 + ); + + // Send load batches in parallel, then the final approve batch + for (let i = 0; i < batches.length - 1; i++) { + const tx = new Transaction().add(...batches[i]); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } + + const approveTx = new Transaction().add(...batches[batches.length - 1]); + const signature = await sendAndConfirmTransaction(rpc, approveTx, [ + payer, + owner, + ]); + + console.log("Approved delegate:", delegate.publicKey.toBase58()); + console.log("Allowance: 500,000,000 tokens"); + console.log("Tx:", signature); +})(); diff --git a/typescript-client/instructions/delegate-revoke.ts b/typescript-client/instructions/delegate-revoke.ts new file mode 100644 index 00000000..9f339a1f --- /dev/null +++ b/typescript-client/instructions/delegate-revoke.ts @@ -0,0 +1,80 @@ +import "dotenv/config"; +import { Keypair, sendAndConfirmTransaction, Transaction } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + createMintInterface, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, + createApproveInterfaceInstructions, + createRevokeInterfaceInstructions, +} from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// devnet: +// const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +// const rpc = createRpc(RPC_URL); +// localnet: +const rpc = createRpc(); + +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +(async function () { + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + + const owner = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, owner.publicKey); + const ownerAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + await mintToInterface(rpc, payer, mint, ownerAta, payer, 1_000_000_000); + + // Approve a delegate first (so we have something to revoke) + const delegate = Keypair.generate(); + const approveBatches = await createApproveInterfaceInstructions( + rpc, + payer.publicKey, + mint, + ownerAta, + delegate.publicKey, + 500_000_000, + owner.publicKey, + 9 + ); + for (const batch of approveBatches) { + const tx = new Transaction().add(...batch); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } + console.log("Approved delegate:", delegate.publicKey.toBase58()); + + // Build revoke instruction batches. + // Returns TransactionInstruction[][] — send [0..n-2] first (load batches), + // then send [n-1] (the revoke instruction). + const revokeBatches = await createRevokeInterfaceInstructions( + rpc, + payer.publicKey, + mint, + ownerAta, + owner.publicKey, + 9 + ); + + for (let i = 0; i < revokeBatches.length - 1; i++) { + const tx = new Transaction().add(...revokeBatches[i]); + await sendAndConfirmTransaction(rpc, tx, [payer, owner]); + } + + const revokeTx = new Transaction().add( + ...revokeBatches[revokeBatches.length - 1] + ); + const signature = await sendAndConfirmTransaction(rpc, revokeTx, [ + payer, + owner, + ]); + + console.log("Revoked all delegate permissions"); + console.log("Tx:", signature); +})(); diff --git a/typescript-client/package.json b/typescript-client/package.json index d94ae090..a0b2c947 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -42,6 +42,9 @@ "load-ata:instruction": "tsx instructions/load-ata.ts", "delegate:approve": "tsx actions/delegate-approve.ts", "delegate:revoke": "tsx actions/delegate-revoke.ts", + "delegate:transfer": "tsx actions/delegate-transfer.ts", + "delegate-approve:instruction": "tsx instructions/delegate-approve.ts", + "delegate-revoke:instruction": "tsx instructions/delegate-revoke.ts", "merge-accounts": "tsx actions/merge-token-accounts.ts" } } From 6bd27b875ae174e1bc59e2ce4882a757d03da0bf Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 16:29:48 +0000 Subject: [PATCH 5/9] fix: increase mint amount to cover approve/transfer amounts Mint 1_000_000 tokens instead of 1000 so the 500_000 approve allowance and 200_000 delegated transfer don't exceed the minted balance. Entire-Checkpoint: 4afa49be1970 --- typescript-client/actions/delegate-approve.ts | 2 +- typescript-client/actions/delegate-revoke.ts | 2 +- typescript-client/actions/delegate-transfer.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript-client/actions/delegate-approve.ts b/typescript-client/actions/delegate-approve.ts index 8d0212ef..9866f04d 100644 --- a/typescript-client/actions/delegate-approve.ts +++ b/typescript-client/actions/delegate-approve.ts @@ -25,7 +25,7 @@ const payer = Keypair.fromSecretKey( (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1000n }, + { recipient: payer.publicKey, amount: 1_000_000n }, ]); const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); diff --git a/typescript-client/actions/delegate-revoke.ts b/typescript-client/actions/delegate-revoke.ts index 0badd23b..967e8360 100644 --- a/typescript-client/actions/delegate-revoke.ts +++ b/typescript-client/actions/delegate-revoke.ts @@ -28,7 +28,7 @@ const payer = Keypair.fromSecretKey( (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1000n }, + { recipient: payer.publicKey, amount: 1_000_000n }, ]); const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); diff --git a/typescript-client/actions/delegate-transfer.ts b/typescript-client/actions/delegate-transfer.ts index eea04e56..1a78c5ca 100644 --- a/typescript-client/actions/delegate-transfer.ts +++ b/typescript-client/actions/delegate-transfer.ts @@ -28,7 +28,7 @@ const payer = Keypair.fromSecretKey( (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1000n }, + { recipient: payer.publicKey, amount: 1_000_000n }, ]); const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); From 2eabacaa10583d53583372b8bb9548d8a1ae1bba Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 18:40:36 +0000 Subject: [PATCH 6/9] fix getata Entire-Checkpoint: 4afa49be1970 --- toolkits/payments/package.json | 3 +- toolkits/payments/setup.ts | 2 +- .../spend-permissions/delegate-approve.ts | 6 +-- .../spend-permissions/delegate-check.ts | 2 +- .../spend-permissions/delegate-full-flow.ts | 6 +-- .../spend-permissions/delegate-revoke.ts | 2 +- .../spend-permissions/delegate-transfer.ts | 8 ++-- toolkits/payments/verify/get-balance.ts | 48 +++++++++---------- typescript-client/actions/delegate-approve.ts | 6 +-- typescript-client/actions/delegate-revoke.ts | 4 +- .../actions/delegate-transfer.ts | 6 +-- .../instructions/delegate-approve.ts | 2 +- 12 files changed, 46 insertions(+), 49 deletions(-) diff --git a/toolkits/payments/package.json b/toolkits/payments/package.json index 472c853e..a6e771e3 100644 --- a/toolkits/payments/package.json +++ b/toolkits/payments/package.json @@ -25,6 +25,7 @@ "dependencies": { "@lightprotocol/compressed-token": "^0.23.0-beta.10", "@lightprotocol/stateless.js": "^0.23.0-beta.10", - "@solana/spl-memo": "^0.2.5" + "@solana/spl-memo": "^0.2.5", + "@solana/spl-token": "^0.4.13" } } diff --git a/toolkits/payments/setup.ts b/toolkits/payments/setup.ts index 09f330b2..3033482e 100644 --- a/toolkits/payments/setup.ts +++ b/toolkits/payments/setup.ts @@ -28,7 +28,7 @@ export const payer = Keypair.fromSecretKey( ); /** Create SPL mint, fund payer, wrap into light-token ATA. */ -export async function setup(amount = 1_000_000) { +export async function setup(amount = 1_000_000_000) { const { mint } = await createMintInterface( rpc, payer, diff --git a/toolkits/payments/spend-permissions/delegate-approve.ts b/toolkits/payments/spend-permissions/delegate-approve.ts index 11faf096..792686c5 100644 --- a/toolkits/payments/spend-permissions/delegate-approve.ts +++ b/toolkits/payments/spend-permissions/delegate-approve.ts @@ -5,7 +5,7 @@ import { rpc, payer, setup } from "../setup.js"; (async function () { const { mint, senderAta } = await setup(); - // Approve: grant delegate permission to spend up to 500,000 tokens + // Approve: grant delegate permission to spend up to 500,000,000 tokens const delegate = Keypair.generate(); const tx = await approveInterface( rpc, @@ -13,11 +13,11 @@ import { rpc, payer, setup } from "../setup.js"; senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("Approved delegate:", delegate.publicKey.toBase58()); - console.log("Allowance: 500,000 tokens"); + console.log("Allowance: 500,000,000 tokens"); console.log("Tx:", tx); })(); diff --git a/toolkits/payments/spend-permissions/delegate-check.ts b/toolkits/payments/spend-permissions/delegate-check.ts index b38932d3..716aac02 100644 --- a/toolkits/payments/spend-permissions/delegate-check.ts +++ b/toolkits/payments/spend-permissions/delegate-check.ts @@ -31,7 +31,7 @@ import { rpc, payer, setup } from "../setup.js"; senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); diff --git a/toolkits/payments/spend-permissions/delegate-full-flow.ts b/toolkits/payments/spend-permissions/delegate-full-flow.ts index 12e0054d..7fdde7b7 100644 --- a/toolkits/payments/spend-permissions/delegate-full-flow.ts +++ b/toolkits/payments/spend-permissions/delegate-full-flow.ts @@ -9,7 +9,7 @@ import { rpc, payer, setup } from "../setup.js"; (async function () { const { mint, senderAta } = await setup(); - // 1. Owner approves delegate to spend up to 500,000 tokens + // 1. Owner approves delegate to spend up to 500,000,000 tokens const delegate = Keypair.generate(); const approveTx = await approveInterface( rpc, @@ -17,11 +17,11 @@ import { rpc, payer, setup } from "../setup.js"; senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("1. Approved delegate:", delegate.publicKey.toBase58()); - console.log(" Allowance: 500,000 tokens"); + console.log(" Allowance: 500,000,000 tokens"); console.log(" Tx:", approveTx); // 2. Check delegation status diff --git a/toolkits/payments/spend-permissions/delegate-revoke.ts b/toolkits/payments/spend-permissions/delegate-revoke.ts index 252e23d5..d29beac9 100644 --- a/toolkits/payments/spend-permissions/delegate-revoke.ts +++ b/toolkits/payments/spend-permissions/delegate-revoke.ts @@ -16,7 +16,7 @@ import { rpc, payer, setup } from "../setup.js"; senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("Approved delegate:", delegate.publicKey.toBase58()); diff --git a/toolkits/payments/spend-permissions/delegate-transfer.ts b/toolkits/payments/spend-permissions/delegate-transfer.ts index 216107ca..e931a624 100644 --- a/toolkits/payments/spend-permissions/delegate-transfer.ts +++ b/toolkits/payments/spend-permissions/delegate-transfer.ts @@ -8,7 +8,7 @@ import { rpc, payer, setup } from "../setup.js"; (async function () { const { mint, senderAta } = await setup(); - // Approve: grant delegate permission to spend up to 500,000 tokens + // Approve: grant delegate permission to spend up to 500,000,000 tokens const delegate = Keypair.generate(); await approveInterface( rpc, @@ -16,7 +16,7 @@ import { rpc, payer, setup } from "../setup.js"; senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("Approved delegate:", delegate.publicKey.toBase58()); @@ -30,13 +30,13 @@ import { rpc, payer, setup } from "../setup.js"; mint, recipient.publicKey, delegate, - 200_000, + 200_000_000, undefined, undefined, { owner: payer.publicKey } ); console.log("Delegated transfer to:", recipient.publicKey.toBase58()); - console.log("Amount: 200,000 tokens"); + console.log("Amount: 200,000,000 tokens"); console.log("Tx:", tx); })(); diff --git a/toolkits/payments/verify/get-balance.ts b/toolkits/payments/verify/get-balance.ts index e3bbdf25..b68ef612 100644 --- a/toolkits/payments/verify/get-balance.ts +++ b/toolkits/payments/verify/get-balance.ts @@ -1,35 +1,31 @@ -import { Keypair } from "@solana/web3.js"; +import { LAMPORTS_PER_SOL } from "@solana/web3.js"; import { getAssociatedTokenAddressInterface, getAtaInterface, -} from "@lightprotocol/compressed-token"; -import { transferInterface } from "@lightprotocol/compressed-token/unified"; +} from "@lightprotocol/compressed-token/unified"; import { rpc, payer, setup } from "../setup.js"; (async function () { - const { mint, senderAta } = await setup(); + const { mint } = await setup(); - const recipient = Keypair.generate(); - await transferInterface( - rpc, - payer, - senderAta, - mint, - recipient.publicKey, - payer, - 100 - ); + // SOL balance + const solLamports = await rpc.getBalance(payer.publicKey); + console.log(`SOL: ${solLamports / LAMPORTS_PER_SOL}`); - // Get recipient's balance - const recipientAta = getAssociatedTokenAddressInterface( - mint, - recipient.publicKey - ); - const { parsed: account } = await getAtaInterface( - rpc, - recipientAta, - recipient.publicKey, - mint - ); - console.log("Recipient's balance:", account.amount); + // Unified token balance via SDK aggregation + const ata = getAssociatedTokenAddressInterface(mint, payer.publicKey); + const account = await getAtaInterface(rpc, ata, payer.publicKey, mint); + + const decimals = account.parsed.mint ? 9 : 9; + const unified = + Number(account.parsed.amount) / 10 ** decimals; + console.log(`\nToken ${mint.toBase58().slice(0, 6)}... (unified): ${unified}`); + + // Per-source breakdown from _sources + if (account._sources && account._sources.length > 0) { + for (const source of account._sources) { + const amount = Number(source.amount) / 10 ** decimals; + console.log(` ${source.type}: ${amount}`); + } + } })(); diff --git a/typescript-client/actions/delegate-approve.ts b/typescript-client/actions/delegate-approve.ts index 9866f04d..c4bdb6bb 100644 --- a/typescript-client/actions/delegate-approve.ts +++ b/typescript-client/actions/delegate-approve.ts @@ -25,7 +25,7 @@ const payer = Keypair.fromSecretKey( (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1_000_000n }, + { recipient: payer.publicKey, amount: 1_000_000_000n }, ]); const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); @@ -36,11 +36,11 @@ const payer = Keypair.fromSecretKey( senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("Approved delegate:", delegate.publicKey.toBase58()); - console.log("Allowance: 500,000 tokens"); + console.log("Allowance: 500,000,000 tokens"); console.log("Tx:", tx); })(); diff --git a/typescript-client/actions/delegate-revoke.ts b/typescript-client/actions/delegate-revoke.ts index 967e8360..b149e7be 100644 --- a/typescript-client/actions/delegate-revoke.ts +++ b/typescript-client/actions/delegate-revoke.ts @@ -28,7 +28,7 @@ const payer = Keypair.fromSecretKey( (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1_000_000n }, + { recipient: payer.publicKey, amount: 1_000_000_000n }, ]); const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); @@ -39,7 +39,7 @@ const payer = Keypair.fromSecretKey( senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("Approved delegate:", delegate.publicKey.toBase58()); diff --git a/typescript-client/actions/delegate-transfer.ts b/typescript-client/actions/delegate-transfer.ts index 1a78c5ca..cdd718f3 100644 --- a/typescript-client/actions/delegate-transfer.ts +++ b/typescript-client/actions/delegate-transfer.ts @@ -28,7 +28,7 @@ const payer = Keypair.fromSecretKey( (async function () { const { mint } = await createMintInterface(rpc, payer, payer, null, 9); await mintToCompressed(rpc, payer, mint, payer, [ - { recipient: payer.publicKey, amount: 1_000_000n }, + { recipient: payer.publicKey, amount: 1_000_000_000n }, ]); const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); @@ -42,7 +42,7 @@ const payer = Keypair.fromSecretKey( senderAta, mint, delegate.publicKey, - 500_000, + 500_000_000, payer ); console.log("Approved delegate:", delegate.publicKey.toBase58()); @@ -55,7 +55,7 @@ const payer = Keypair.fromSecretKey( mint, recipient.publicKey, delegate, - 200_000, + 200_000_000, undefined, undefined, { owner: payer.publicKey } diff --git a/typescript-client/instructions/delegate-approve.ts b/typescript-client/instructions/delegate-approve.ts index f503cd5b..a26cdb66 100644 --- a/typescript-client/instructions/delegate-approve.ts +++ b/typescript-client/instructions/delegate-approve.ts @@ -47,7 +47,7 @@ const payer = Keypair.fromSecretKey( 9 ); - // Send load batches in parallel, then the final approve batch + // Send load batches sequentially, then the final approve batch for (let i = 0; i < batches.length - 1; i++) { const tx = new Transaction().add(...batches[i]); await sendAndConfirmTransaction(rpc, tx, [payer, owner]); From 1ae66b081e45a0bc7b521a43af7f4a6bb40ba173 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 23:45:44 +0000 Subject: [PATCH 7/9] update Entire-Checkpoint: 4afa49be1970 --- toolkits/payments/receive/receive.ts | 4 +- toolkits/payments/send/batch-send.ts | 57 +++++----------- toolkits/payments/send/payment-with-memo.ts | 35 +++------- toolkits/payments/send/send-action.ts | 1 - .../spend-permissions/delegate-check.ts | 6 +- .../spend-permissions/delegate-full-flow.ts | 2 +- toolkits/payments/verify/get-balance.ts | 33 +++------ toolkits/payments/verify/get-history.ts | 26 ++----- toolkits/payments/verify/verify-address.ts | 67 ++++--------------- 9 files changed, 59 insertions(+), 172 deletions(-) diff --git a/toolkits/payments/receive/receive.ts b/toolkits/payments/receive/receive.ts index a05cd948..37363e4f 100644 --- a/toolkits/payments/receive/receive.ts +++ b/toolkits/payments/receive/receive.ts @@ -4,10 +4,10 @@ import { sendAndConfirmTransaction, } from "@solana/web3.js"; import { + transferInterface, getAssociatedTokenAddressInterface, createLoadAtaInstructions, -} from "@lightprotocol/compressed-token"; -import { transferInterface } from "@lightprotocol/compressed-token/unified"; +} from "@lightprotocol/compressed-token/unified"; import { rpc, payer, setup } from "../setup.js"; (async function () { diff --git a/toolkits/payments/send/batch-send.ts b/toolkits/payments/send/batch-send.ts index 2f93935f..2782c62c 100644 --- a/toolkits/payments/send/batch-send.ts +++ b/toolkits/payments/send/batch-send.ts @@ -4,68 +4,43 @@ import { sendAndConfirmTransaction, } from "@solana/web3.js"; import { - createAtaInterfaceIdempotent, - getAssociatedTokenAddressInterface, + createTransferInterfaceInstructions, getAtaInterface, -} from "@lightprotocol/compressed-token"; -import { - createTransferToAccountInterfaceInstructions, - createLoadAtaInstructions, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token/unified"; import { rpc, payer, setup } from "../setup.js"; (async function () { - // Step 1: Setup — create mint and fund sender const { mint } = await setup(); - const recipients = Array.from({ length: 5 }, (_, i) => ({ - address: Keypair.generate().publicKey, - amount: (i + 1) * 100, - })); + const recipients = [ + { address: Keypair.generate().publicKey, amount: 100 }, + { address: Keypair.generate().publicKey, amount: 200 }, + { address: Keypair.generate().publicKey, amount: 300 }, + ]; - // Step 2: Create ATAs idempotently for sender + all recipients - await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey); - for (const { address } of recipients) { - await createAtaInterfaceIdempotent(rpc, payer, mint, address); - } - - // Step 3: Derive ATA addresses - const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); - const recipientAtas = recipients.map(({ address }) => - getAssociatedTokenAddressInterface(mint, address) - ); - - // Step 4: Create transfer instructions using explicit-account variant - const COMPUTE_BUDGET_ID = - "ComputeBudget111111111111111111111111111111"; + // Build transfer instructions for each recipient const allTransferIxs = []; - let isFirst = true; for (const { address, amount } of recipients) { - const destination = getAssociatedTokenAddressInterface(mint, address); - const ixs = await createTransferToAccountInterfaceInstructions( + const instructions = await createTransferInterfaceInstructions( rpc, payer.publicKey, mint, amount, payer.publicKey, - destination + address ); - // Deduplicate ComputeBudget across transfers. - for (const ix of ixs[0]) { - if (!isFirst && ix.programId.toBase58() === COMPUTE_BUDGET_ID) - continue; - allTransferIxs.push(ix); - } - isFirst = false; + + allTransferIxs.push(...instructions[instructions.length - 1]); } - // Step 5: Batch into single transaction - const batchTx = new Transaction().add(...allTransferIxs); - const sig = await sendAndConfirmTransaction(rpc, batchTx, [payer]); + // Send all transfers in a single transaction + const tx = new Transaction().add(...allTransferIxs); + const sig = await sendAndConfirmTransaction(rpc, tx, [payer]); console.log("Batch tx:", sig); - // Step 6: Verify balances + // Verify balances for (const { address, amount } of recipients) { const ata = getAssociatedTokenAddressInterface(mint, address); const { parsed } = await getAtaInterface(rpc, ata, address, mint); diff --git a/toolkits/payments/send/payment-with-memo.ts b/toolkits/payments/send/payment-with-memo.ts index 83d1054d..ca077fac 100644 --- a/toolkits/payments/send/payment-with-memo.ts +++ b/toolkits/payments/send/payment-with-memo.ts @@ -1,20 +1,12 @@ import { Keypair, Transaction, - TransactionInstruction, - PublicKey, sendAndConfirmTransaction, } from "@solana/web3.js"; -import { - createTransferInterfaceInstructions, - sliceLast, -} from "@lightprotocol/compressed-token/unified"; +import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified"; +import { createMemoInstruction } from "@solana/spl-memo"; import { rpc, payer, setup } from "../setup.js"; -const MEMO_PROGRAM_ID = new PublicKey( - "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" -); - (async function () { const { mint } = await setup(); const recipient = Keypair.generate(); @@ -28,28 +20,19 @@ const MEMO_PROGRAM_ID = new PublicKey( recipient.publicKey ); - const { rest: loadInstructions, last: transferInstructions } = - sliceLast(instructions); + // Append memo to the transfer transaction (last batch) + const memoIx = createMemoInstruction("INV-2024-001"); + instructions[instructions.length - 1].push(memoIx); - // Send load transactions first (if any) - for (const ixs of loadInstructions) { + let signature; + for (const ixs of instructions) { const tx = new Transaction().add(...ixs); - await sendAndConfirmTransaction(rpc, tx, [payer]); + signature = await sendAndConfirmTransaction(rpc, tx, [payer]); } - - // Add memo to the transfer transaction - const memoIx = new TransactionInstruction({ - keys: [], - programId: MEMO_PROGRAM_ID, - data: Buffer.from("INV-2024-001"), - }); - - const transferTx = new Transaction().add(...transferInstructions, memoIx); - const signature = await sendAndConfirmTransaction(rpc, transferTx, [payer]); console.log("Tx with memo:", signature); // Read memo back from transaction logs - const txDetails = await rpc.getTransaction(signature, { + const txDetails = await rpc.getTransaction(signature!, { maxSupportedTransactionVersion: 0, }); diff --git a/toolkits/payments/send/send-action.ts b/toolkits/payments/send/send-action.ts index 28589f4d..f52d381c 100644 --- a/toolkits/payments/send/send-action.ts +++ b/toolkits/payments/send/send-action.ts @@ -15,6 +15,5 @@ import { rpc, payer, setup } from "../setup.js"; payer, 100 ); - console.log("Tx:", sig); })(); diff --git a/toolkits/payments/spend-permissions/delegate-check.ts b/toolkits/payments/spend-permissions/delegate-check.ts index 716aac02..93df30f9 100644 --- a/toolkits/payments/spend-permissions/delegate-check.ts +++ b/toolkits/payments/spend-permissions/delegate-check.ts @@ -1,6 +1,8 @@ import { Keypair } from "@solana/web3.js"; -import { getAtaInterface } from "@lightprotocol/compressed-token"; -import { approveInterface } from "@lightprotocol/compressed-token/unified"; +import { + getAtaInterface, + approveInterface, +} from "@lightprotocol/compressed-token/unified"; import { rpc, payer, setup } from "../setup.js"; (async function () { diff --git a/toolkits/payments/spend-permissions/delegate-full-flow.ts b/toolkits/payments/spend-permissions/delegate-full-flow.ts index 7fdde7b7..068b4ec1 100644 --- a/toolkits/payments/spend-permissions/delegate-full-flow.ts +++ b/toolkits/payments/spend-permissions/delegate-full-flow.ts @@ -1,6 +1,6 @@ import { Keypair } from "@solana/web3.js"; -import { getAtaInterface } from "@lightprotocol/compressed-token"; import { + getAtaInterface, approveInterface, revokeInterface, } from "@lightprotocol/compressed-token/unified"; diff --git a/toolkits/payments/verify/get-balance.ts b/toolkits/payments/verify/get-balance.ts index b68ef612..61ee53b9 100644 --- a/toolkits/payments/verify/get-balance.ts +++ b/toolkits/payments/verify/get-balance.ts @@ -1,31 +1,14 @@ -import { LAMPORTS_PER_SOL } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { getAssociatedTokenAddressInterface, getAtaInterface, } from "@lightprotocol/compressed-token/unified"; -import { rpc, payer, setup } from "../setup.js"; -(async function () { - const { mint } = await setup(); +const rpc = createRpc(); +const mint = new PublicKey("YOUR_MINT_ADDRESS"); +const owner = new PublicKey("YOUR_OWNER_ADDRESS"); - // SOL balance - const solLamports = await rpc.getBalance(payer.publicKey); - console.log(`SOL: ${solLamports / LAMPORTS_PER_SOL}`); - - // Unified token balance via SDK aggregation - const ata = getAssociatedTokenAddressInterface(mint, payer.publicKey); - const account = await getAtaInterface(rpc, ata, payer.publicKey, mint); - - const decimals = account.parsed.mint ? 9 : 9; - const unified = - Number(account.parsed.amount) / 10 ** decimals; - console.log(`\nToken ${mint.toBase58().slice(0, 6)}... (unified): ${unified}`); - - // Per-source breakdown from _sources - if (account._sources && account._sources.length > 0) { - for (const source of account._sources) { - const amount = Number(source.amount) / 10 ** decimals; - console.log(` ${source.type}: ${amount}`); - } - } -})(); +const ata = getAssociatedTokenAddressInterface(mint, owner); +const account = await getAtaInterface(rpc, ata, owner, mint); +console.log("Balance:", account.parsed.amount.toString()); diff --git a/toolkits/payments/verify/get-history.ts b/toolkits/payments/verify/get-history.ts index 725ccd6c..12b9998f 100644 --- a/toolkits/payments/verify/get-history.ts +++ b/toolkits/payments/verify/get-history.ts @@ -1,22 +1,8 @@ -import { Keypair } from "@solana/web3.js"; -import { transferInterface } from "@lightprotocol/compressed-token/unified"; -import { rpc, payer, setup } from "../setup.js"; +import { PublicKey } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; -(async function () { - const { mint, senderAta } = await setup(); +const rpc = createRpc(); +const owner = new PublicKey("YOUR_OWNER_ADDRESS"); - const recipient = Keypair.generate(); - await transferInterface( - rpc, - payer, - senderAta, - mint, - recipient.publicKey, - payer, - 100 - ); - - // Get transaction history - const result = await rpc.getSignaturesForOwnerInterface(payer.publicKey); - console.log("Signatures:", result.signatures.length); -})(); +const result = await rpc.getSignaturesForOwnerInterface(owner); +console.log("Signatures:", result.signatures.length); diff --git a/toolkits/payments/verify/verify-address.ts b/toolkits/payments/verify/verify-address.ts index deeee985..8e3a1334 100644 --- a/toolkits/payments/verify/verify-address.ts +++ b/toolkits/payments/verify/verify-address.ts @@ -1,59 +1,18 @@ -import { Keypair } from "@solana/web3.js"; +import { PublicKey } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { - createMintInterface, - createAtaInterface, getAssociatedTokenAddressInterface, getAtaInterface, -} from "@lightprotocol/compressed-token"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { rpc, payer } from "../setup.js"; +} from "@lightprotocol/compressed-token/unified"; -(async function () { - const { mint } = await createMintInterface( - rpc, - payer, - payer, - null, - 9, - undefined, - undefined, - TOKEN_PROGRAM_ID - ); +const rpc = createRpc(); +const mint = new PublicKey("YOUR_MINT_ADDRESS"); +const owner = new PublicKey("YOUR_OWNER_ADDRESS"); - // Create an associated token account for the payer (so it exists) - await createAtaInterface(rpc, payer, mint, payer.publicKey); - - // --- Verify an address that HAS an associated token account --- - const existingRecipient = payer.publicKey; - const expectedAta = getAssociatedTokenAddressInterface( - mint, - existingRecipient - ); - - try { - const account = await getAtaInterface( - rpc, - expectedAta, - existingRecipient, - mint - ); - console.log("Account exists, balance:", account.parsed.amount); - } catch { - console.log( - "Account does not exist yet — will be created on first transfer" - ); - } - - // --- Verify an address that does NOT have an associated token account --- - const newRecipient = Keypair.generate().publicKey; - const newAta = getAssociatedTokenAddressInterface(mint, newRecipient); - - try { - const account = await getAtaInterface(rpc, newAta, newRecipient, mint); - console.log("Account exists, balance:", account.parsed.amount); - } catch { - console.log( - "Account does not exist yet — will be created on first transfer" - ); - } -})(); +const ata = getAssociatedTokenAddressInterface(mint, owner); +try { + const account = await getAtaInterface(rpc, ata, owner, mint); + console.log("Account exists, balance:", account.parsed.amount.toString()); +} catch { + console.log("Account does not exist yet"); +} From f44486741576a99ed85b10bc84d7b57920dba4ad Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Fri, 20 Mar 2026 23:50:01 +0000 Subject: [PATCH 8/9] update Entire-Checkpoint: 4afa49be1970 --- toolkits/gasless-transactions/rust/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkits/gasless-transactions/rust/Cargo.toml b/toolkits/gasless-transactions/rust/Cargo.toml index 2ee2aebc..7ecd74fd 100644 --- a/toolkits/gasless-transactions/rust/Cargo.toml +++ b/toolkits/gasless-transactions/rust/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" publish = false [[bin]] -name = "sponsor-top-ups" -path = "sponsor-top-ups.rs" +name = "gasless-transfer" +path = "gasless-transfer.rs" [dependencies] light-token = "0.23.0" From ee3f51268f8c45b7a5be6ad778c2d4129eebc785 Mon Sep 17 00:00:00 2001 From: tilo-14 Date: Sat, 21 Mar 2026 01:58:39 +0000 Subject: [PATCH 9/9] testing and add nullifier Entire-Checkpoint: 4afa49be1970 --- .gitmodules | 3 + toolkits/nullifier-program | 1 + toolkits/payments/README.md | 10 ++- toolkits/payments/package.json | 6 +- toolkits/payments/send/batch-send.ts | 13 +++- .../spend-permissions/delegate-full-flow.ts | 63 ------------------- .../spend-permissions/delegate-transfer.ts | 1 - 7 files changed, 26 insertions(+), 71 deletions(-) create mode 160000 toolkits/nullifier-program delete mode 100644 toolkits/payments/spend-permissions/delegate-full-flow.ts diff --git a/.gitmodules b/.gitmodules index ce4eb81d..294b3d02 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "programs/anchor/cp-swap-reference"] path = programs/anchor/cp-swap-reference url = https://github.com/Lightprotocol/cp-swap-reference.git +[submodule "toolkits/nullifier-program"] + path = toolkits/nullifier-program + url = https://github.com/Lightprotocol/nullifier-program.git diff --git a/toolkits/nullifier-program b/toolkits/nullifier-program new file mode 160000 index 00000000..8a5e0ced --- /dev/null +++ b/toolkits/nullifier-program @@ -0,0 +1 @@ +Subproject commit 8a5e0ced6b142dbd74fe4585a0d53cc032166660 diff --git a/toolkits/payments/README.md b/toolkits/payments/README.md index 5748e32a..6537a477 100644 --- a/toolkits/payments/README.md +++ b/toolkits/payments/README.md @@ -54,7 +54,6 @@ Light Token reduces account creation cost by 200x compared to SPL. Transfer cost | [delegate-revoke.ts](spend-permissions/delegate-revoke.ts) | Revoke delegate access. | `revokeInterface` | | [delegate-check.ts](spend-permissions/delegate-check.ts) | Check current delegation status and remaining allowance. | `getAtaInterface` | | [delegate-transfer.ts](spend-permissions/delegate-transfer.ts) | Delegate transfers tokens on behalf of the owner. | `transferInterface` | -| [delegate-full-flow.ts](spend-permissions/delegate-full-flow.ts) | Approve, check, and revoke in one script. | `approveInterface`, `revokeInterface`, `getAtaInterface` | ### Interop (wrap and unwrap) @@ -85,7 +84,7 @@ Run any script: npx tsx send/send-action.ts npx tsx send/payment-with-memo.ts npx tsx send/batch-send.ts -npx tsx spend-permissions/delegate-full-flow.ts +npx tsx spend-permissions/delegate-approve.ts ``` ### Devnet @@ -105,6 +104,13 @@ export const rpc = createRpc(RPC_URL); // export const rpc = createRpc(); ``` +### Nullifier Program + +For some use cases, such as sending payments, you might want to prevent your onchain instruction from being executed more than once. The [nullifier program](../nullifier-program/) utility solves this for you. We also deployed a reference implementation to public networks so you can get started quickly. +- **[TypeScript example](../nullifier-program/examples/action-create-nullifier.ts)** +- **[Rust example](../nullifier-program/examples/rust/src/main.rs)** +- **[Documentation](https://www.zkcompression.com/compressed-pdas/guides/how-to-create-nullifier-pdas)** + ## Documentation - [Payment flows overview](https://www.zkcompression.com/light-token/payments/overview) diff --git a/toolkits/payments/package.json b/toolkits/payments/package.json index a6e771e3..d321c42b 100644 --- a/toolkits/payments/package.json +++ b/toolkits/payments/package.json @@ -4,6 +4,7 @@ "description": "Light Token Payments Toolkit Examples", "type": "module", "scripts": { + "test:all": "tsx send/send-action.ts && tsx send/send-instruction.ts && tsx send/payment-with-memo.ts && tsx send/batch-send.ts && tsx send/sign-all-transactions.ts && tsx receive/receive.ts && tsx spend-permissions/delegate-approve.ts && tsx spend-permissions/delegate-revoke.ts && tsx spend-permissions/delegate-check.ts && tsx spend-permissions/delegate-transfer.ts", "send-action": "tsx send/send-action.ts", "send-instruction": "tsx send/send-instruction.ts", "payment-with-memo": "tsx send/payment-with-memo.ts", @@ -19,12 +20,11 @@ "delegate-approve": "tsx spend-permissions/delegate-approve.ts", "delegate-revoke": "tsx spend-permissions/delegate-revoke.ts", "delegate-check": "tsx spend-permissions/delegate-check.ts", - "delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts", "delegate-transfer": "tsx spend-permissions/delegate-transfer.ts" }, "dependencies": { - "@lightprotocol/compressed-token": "^0.23.0-beta.10", - "@lightprotocol/stateless.js": "^0.23.0-beta.10", + "@lightprotocol/compressed-token": "^0.23.0-beta.12", + "@lightprotocol/stateless.js": "^0.23.0-beta.12", "@solana/spl-memo": "^0.2.5", "@solana/spl-token": "^0.4.13" } diff --git a/toolkits/payments/send/batch-send.ts b/toolkits/payments/send/batch-send.ts index 2782c62c..ea1178c5 100644 --- a/toolkits/payments/send/batch-send.ts +++ b/toolkits/payments/send/batch-send.ts @@ -20,7 +20,9 @@ import { rpc, payer, setup } from "../setup.js"; ]; // Build transfer instructions for each recipient + const COMPUTE_BUDGET = "ComputeBudget111111111111111111111111111111"; const allTransferIxs = []; + let hasComputeBudget = false; for (const { address, amount } of recipients) { const instructions = await createTransferInterfaceInstructions( @@ -31,8 +33,15 @@ import { rpc, payer, setup } from "../setup.js"; payer.publicKey, address ); - - allTransferIxs.push(...instructions[instructions.length - 1]); + + for (const ix of instructions[instructions.length - 1]) { + // Deduplicate ComputeBudget instructions + if (ix.programId.toBase58() === COMPUTE_BUDGET) { + if (hasComputeBudget) continue; + hasComputeBudget = true; + } + allTransferIxs.push(ix); + } } // Send all transfers in a single transaction diff --git a/toolkits/payments/spend-permissions/delegate-full-flow.ts b/toolkits/payments/spend-permissions/delegate-full-flow.ts deleted file mode 100644 index 068b4ec1..00000000 --- a/toolkits/payments/spend-permissions/delegate-full-flow.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Keypair } from "@solana/web3.js"; -import { - getAtaInterface, - approveInterface, - revokeInterface, -} from "@lightprotocol/compressed-token/unified"; -import { rpc, payer, setup } from "../setup.js"; - -(async function () { - const { mint, senderAta } = await setup(); - - // 1. Owner approves delegate to spend up to 500,000,000 tokens - const delegate = Keypair.generate(); - const approveTx = await approveInterface( - rpc, - payer, - senderAta, - mint, - delegate.publicKey, - 500_000_000, - payer - ); - console.log("1. Approved delegate:", delegate.publicKey.toBase58()); - console.log(" Allowance: 500,000,000 tokens"); - console.log(" Tx:", approveTx); - - // 2. Check delegation status - const account = await getAtaInterface( - rpc, - senderAta, - payer.publicKey, - mint - ); - console.log("\n2. Account status after approval:"); - console.log( - " Delegate:", - account.parsed.delegate?.toBase58() ?? "none" - ); - console.log( - " Delegated amount:", - account.parsed.delegatedAmount.toString() - ); - console.log(" Account balance:", account.parsed.amount.toString()); - - // 3. Owner revokes all delegate permissions - const revokeTx = await revokeInterface(rpc, payer, senderAta, mint, payer); - console.log("\n3. Revoked all delegate permissions"); - console.log(" Tx:", revokeTx); - - // 4. Verify delegate is removed - const afterRevoke = await getAtaInterface( - rpc, - senderAta, - payer.publicKey, - mint - ); - console.log("\n4. Account status after revoke:"); - console.log( - " Delegate:", - afterRevoke.parsed.delegate?.toBase58() ?? "none" - ); - console.log(" Balance:", afterRevoke.parsed.amount.toString()); -})(); diff --git a/toolkits/payments/spend-permissions/delegate-transfer.ts b/toolkits/payments/spend-permissions/delegate-transfer.ts index e931a624..3f3a2bd8 100644 --- a/toolkits/payments/spend-permissions/delegate-transfer.ts +++ b/toolkits/payments/spend-permissions/delegate-transfer.ts @@ -32,7 +32,6 @@ import { rpc, payer, setup } from "../setup.js"; delegate, 200_000_000, undefined, - undefined, { owner: payer.publicKey } );