Skip to content

Commit 2f0aa9c

Browse files
tilo-14tilo-14
andauthored
Add delegation examples for PR #2351 API (#32)
* 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 * 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 * Revert "Add delegation instruction examples, delegate-transfer to payments toolkit" This reverts commit d871e48. Entire-Checkpoint: 4afa49be1970 * 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 * 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 * fix getata Entire-Checkpoint: 4afa49be1970 * update Entire-Checkpoint: 4afa49be1970 * update Entire-Checkpoint: 4afa49be1970 * testing and add nullifier Entire-Checkpoint: 4afa49be1970 --------- Co-authored-by: tilo-14 <tilo@luminouslabs.com>
1 parent 19c3f85 commit 2f0aa9c

27 files changed

Lines changed: 389 additions & 302 deletions

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "programs/anchor/cp-swap-reference"]
22
path = programs/anchor/cp-swap-reference
33
url = https://github.com/Lightprotocol/cp-swap-reference.git
4+
[submodule "toolkits/nullifier-program"]
5+
path = toolkits/nullifier-program
6+
url = https://github.com/Lightprotocol/nullifier-program.git

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@ Light token is a high-performance token standard that reduces the cost of mint a
2424
| | | | Description |
2525
|---------|--------|-------------|-------------|
2626
| create-mint | [Action](typescript-client/actions/create-mint.ts) | [Instruction](typescript-client/instructions/create-mint.ts) | Create a light-token mint with metadata |
27+
| 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 |
28+
| 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 |
29+
| 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 |
2730
| create-ata | [Action](typescript-client/actions/create-ata.ts) | [Instruction](typescript-client/instructions/create-ata.ts) | Create an associated light-token account |
31+
| create-ata-explicit-rent-sponsor | [Action](typescript-client/actions/create-ata-explicit-rent-sponsor.ts) | | Create an ATA with explicit rent sponsor |
2832
| 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 |
2933
| mint-to | [Action](typescript-client/actions/mint-to.ts) | [Instruction](typescript-client/instructions/mint-to.ts) | Mint tokens to a light-account |
3034
| transfer-interface | [Action](typescript-client/actions/transfer-interface.ts) | [Instruction](typescript-client/instructions/transfer-interface.ts) | Transfer between light-token, T22, and SPL accounts |
3135
| wrap | [Action](typescript-client/actions/wrap.ts) | [Instruction](typescript-client/instructions/wrap.ts) | Wrap SPL/T22 to light-token |
3236
| unwrap | [Action](typescript-client/actions/unwrap.ts) | [Instruction](typescript-client/instructions/unwrap.ts) | Unwrap light-token to SPL/T22 |
3337
| approve | [Action](typescript-client/actions/delegate-approve.ts) | | Approve delegate |
3438
| revoke | [Action](typescript-client/actions/delegate-revoke.ts) | | Revoke delegate |
39+
| delegate-transfer | [Action](typescript-client/actions/delegate-transfer.ts) | | Delegate transfers tokens on behalf of owner |
3540

3641
### Rust
3742

toolkits/gasless-transactions/rust/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ edition = "2021"
55
publish = false
66

77
[[bin]]
8-
name = "sponsor-top-ups"
9-
path = "sponsor-top-ups.rs"
8+
name = "gasless-transfer"
9+
path = "gasless-transfer.rs"
1010

1111
[dependencies]
1212
light-token = "0.23.0"

toolkits/nullifier-program

Submodule nullifier-program added at 8a5e0ce

toolkits/payments/README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ Light Token reduces account creation cost by 200x compared to SPL. Transfer cost
5353
| [delegate-approve.ts](spend-permissions/delegate-approve.ts) | Let a delegate spend tokens on your behalf. | `approveInterface` |
5454
| [delegate-revoke.ts](spend-permissions/delegate-revoke.ts) | Revoke delegate access. | `revokeInterface` |
5555
| [delegate-check.ts](spend-permissions/delegate-check.ts) | Check current delegation status and remaining allowance. | `getAtaInterface` |
56-
| [delegate-full-flow.ts](spend-permissions/delegate-full-flow.ts) | Approve, check, and revoke in one script. | `approveInterface`, `revokeInterface`, `getAtaInterface` |
56+
| [delegate-transfer.ts](spend-permissions/delegate-transfer.ts) | Delegate transfers tokens on behalf of the owner. | `transferInterface` |
5757

5858
### Interop (wrap and unwrap)
5959

@@ -84,7 +84,7 @@ Run any script:
8484
npx tsx send/send-action.ts
8585
npx tsx send/payment-with-memo.ts
8686
npx tsx send/batch-send.ts
87-
npx tsx spend-permissions/delegate-full-flow.ts
87+
npx tsx spend-permissions/delegate-approve.ts
8888
```
8989

9090
### Devnet
@@ -104,6 +104,13 @@ export const rpc = createRpc(RPC_URL);
104104
// export const rpc = createRpc();
105105
```
106106

107+
### Nullifier Program
108+
109+
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.
110+
- **[TypeScript example](../nullifier-program/examples/action-create-nullifier.ts)**
111+
- **[Rust example](../nullifier-program/examples/rust/src/main.rs)**
112+
- **[Documentation](https://www.zkcompression.com/compressed-pdas/guides/how-to-create-nullifier-pdas)**
113+
107114
## Documentation
108115

109116
- [Payment flows overview](https://www.zkcompression.com/light-token/payments/overview)

toolkits/payments/package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "Light Token Payments Toolkit Examples",
55
"type": "module",
66
"scripts": {
7+
"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",
78
"send-action": "tsx send/send-action.ts",
89
"send-instruction": "tsx send/send-instruction.ts",
910
"payment-with-memo": "tsx send/payment-with-memo.ts",
@@ -19,11 +20,12 @@
1920
"delegate-approve": "tsx spend-permissions/delegate-approve.ts",
2021
"delegate-revoke": "tsx spend-permissions/delegate-revoke.ts",
2122
"delegate-check": "tsx spend-permissions/delegate-check.ts",
22-
"delegate-full-flow": "tsx spend-permissions/delegate-full-flow.ts"
23+
"delegate-transfer": "tsx spend-permissions/delegate-transfer.ts"
2324
},
2425
"dependencies": {
25-
"@lightprotocol/compressed-token": "^0.23.0-beta.10",
26-
"@lightprotocol/stateless.js": "^0.23.0-beta.10",
27-
"@solana/spl-memo": "^0.2.5"
26+
"@lightprotocol/compressed-token": "^0.23.0-beta.12",
27+
"@lightprotocol/stateless.js": "^0.23.0-beta.12",
28+
"@solana/spl-memo": "^0.2.5",
29+
"@solana/spl-token": "^0.4.13"
2830
}
2931
}

toolkits/payments/receive/receive.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {
44
sendAndConfirmTransaction,
55
} from "@solana/web3.js";
66
import {
7+
transferInterface,
78
getAssociatedTokenAddressInterface,
89
createLoadAtaInstructions,
9-
} from "@lightprotocol/compressed-token";
10-
import { transferInterface } from "@lightprotocol/compressed-token/unified";
10+
} from "@lightprotocol/compressed-token/unified";
1111
import { rpc, payer, setup } from "../setup.js";
1212

1313
(async function () {

toolkits/payments/send/batch-send.ts

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,68 +4,52 @@ import {
44
sendAndConfirmTransaction,
55
} from "@solana/web3.js";
66
import {
7-
createAtaInterfaceIdempotent,
8-
getAssociatedTokenAddressInterface,
7+
createTransferInterfaceInstructions,
98
getAtaInterface,
10-
} from "@lightprotocol/compressed-token";
11-
import {
12-
createTransferToAccountInterfaceInstructions,
13-
createLoadAtaInstructions,
9+
getAssociatedTokenAddressInterface,
1410
} from "@lightprotocol/compressed-token/unified";
1511
import { rpc, payer, setup } from "../setup.js";
1612

1713
(async function () {
18-
// Step 1: Setup — create mint and fund sender
1914
const { mint } = await setup();
2015

21-
const recipients = Array.from({ length: 5 }, (_, i) => ({
22-
address: Keypair.generate().publicKey,
23-
amount: (i + 1) * 100,
24-
}));
25-
26-
// Step 2: Create ATAs idempotently for sender + all recipients
27-
await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey);
28-
for (const { address } of recipients) {
29-
await createAtaInterfaceIdempotent(rpc, payer, mint, address);
30-
}
31-
32-
// Step 3: Derive ATA addresses
33-
const senderAta = getAssociatedTokenAddressInterface(mint, payer.publicKey);
34-
const recipientAtas = recipients.map(({ address }) =>
35-
getAssociatedTokenAddressInterface(mint, address)
36-
);
16+
const recipients = [
17+
{ address: Keypair.generate().publicKey, amount: 100 },
18+
{ address: Keypair.generate().publicKey, amount: 200 },
19+
{ address: Keypair.generate().publicKey, amount: 300 },
20+
];
3721

38-
// Step 4: Create transfer instructions using explicit-account variant
39-
const COMPUTE_BUDGET_ID =
40-
"ComputeBudget111111111111111111111111111111";
22+
// Build transfer instructions for each recipient
23+
const COMPUTE_BUDGET = "ComputeBudget111111111111111111111111111111";
4124
const allTransferIxs = [];
42-
let isFirst = true;
25+
let hasComputeBudget = false;
4326

4427
for (const { address, amount } of recipients) {
45-
const destination = getAssociatedTokenAddressInterface(mint, address);
46-
const ixs = await createTransferToAccountInterfaceInstructions(
28+
const instructions = await createTransferInterfaceInstructions(
4729
rpc,
4830
payer.publicKey,
4931
mint,
5032
amount,
5133
payer.publicKey,
52-
destination
34+
address
5335
);
54-
// Deduplicate ComputeBudget across transfers.
55-
for (const ix of ixs[0]) {
56-
if (!isFirst && ix.programId.toBase58() === COMPUTE_BUDGET_ID)
57-
continue;
36+
37+
for (const ix of instructions[instructions.length - 1]) {
38+
// Deduplicate ComputeBudget instructions
39+
if (ix.programId.toBase58() === COMPUTE_BUDGET) {
40+
if (hasComputeBudget) continue;
41+
hasComputeBudget = true;
42+
}
5843
allTransferIxs.push(ix);
5944
}
60-
isFirst = false;
6145
}
6246

63-
// Step 5: Batch into single transaction
64-
const batchTx = new Transaction().add(...allTransferIxs);
65-
const sig = await sendAndConfirmTransaction(rpc, batchTx, [payer]);
47+
// Send all transfers in a single transaction
48+
const tx = new Transaction().add(...allTransferIxs);
49+
const sig = await sendAndConfirmTransaction(rpc, tx, [payer]);
6650
console.log("Batch tx:", sig);
6751

68-
// Step 6: Verify balances
52+
// Verify balances
6953
for (const { address, amount } of recipients) {
7054
const ata = getAssociatedTokenAddressInterface(mint, address);
7155
const { parsed } = await getAtaInterface(rpc, ata, address, mint);

toolkits/payments/send/payment-with-memo.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
import {
22
Keypair,
33
Transaction,
4-
TransactionInstruction,
5-
PublicKey,
64
sendAndConfirmTransaction,
75
} from "@solana/web3.js";
8-
import {
9-
createTransferInterfaceInstructions,
10-
sliceLast,
11-
} from "@lightprotocol/compressed-token/unified";
6+
import { createTransferInterfaceInstructions } from "@lightprotocol/compressed-token/unified";
7+
import { createMemoInstruction } from "@solana/spl-memo";
128
import { rpc, payer, setup } from "../setup.js";
139

14-
const MEMO_PROGRAM_ID = new PublicKey(
15-
"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
16-
);
17-
1810
(async function () {
1911
const { mint } = await setup();
2012
const recipient = Keypair.generate();
@@ -28,28 +20,19 @@ const MEMO_PROGRAM_ID = new PublicKey(
2820
recipient.publicKey
2921
);
3022

31-
const { rest: loadInstructions, last: transferInstructions } =
32-
sliceLast(instructions);
23+
// Append memo to the transfer transaction (last batch)
24+
const memoIx = createMemoInstruction("INV-2024-001");
25+
instructions[instructions.length - 1].push(memoIx);
3326

34-
// Send load transactions first (if any)
35-
for (const ixs of loadInstructions) {
27+
let signature;
28+
for (const ixs of instructions) {
3629
const tx = new Transaction().add(...ixs);
37-
await sendAndConfirmTransaction(rpc, tx, [payer]);
30+
signature = await sendAndConfirmTransaction(rpc, tx, [payer]);
3831
}
39-
40-
// Add memo to the transfer transaction
41-
const memoIx = new TransactionInstruction({
42-
keys: [],
43-
programId: MEMO_PROGRAM_ID,
44-
data: Buffer.from("INV-2024-001"),
45-
});
46-
47-
const transferTx = new Transaction().add(...transferInstructions, memoIx);
48-
const signature = await sendAndConfirmTransaction(rpc, transferTx, [payer]);
4932
console.log("Tx with memo:", signature);
5033

5134
// Read memo back from transaction logs
52-
const txDetails = await rpc.getTransaction(signature, {
35+
const txDetails = await rpc.getTransaction(signature!, {
5336
maxSupportedTransactionVersion: 0,
5437
});
5538

toolkits/payments/send/send-action.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,5 @@ import { rpc, payer, setup } from "../setup.js";
1515
payer,
1616
100
1717
);
18-
1918
console.log("Tx:", sig);
2019
})();

0 commit comments

Comments
 (0)