diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5c6ddac --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +API_KEY=YOUR_KEY diff --git a/.gitignore b/.gitignore index 941faf8..d57540e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ -node_modules/ -dist/ -.env -pnpm-lock.yaml -package-lock.json +**/node_modules/ +**/dist/ +**/.env +**/pnpm-lock.yaml +**/package-lock.json +**/*.json.bak +**/test-ledger \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0a02bce --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "tabWidth": 4 +} diff --git a/README.md b/README.md index bf26d53..2377e5b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ -# examples-light-token -Examples for Light-Token Program +# Light Token Examples + +## Setup + +```bash +cp .env.example .env # ...and set RPC_URL +npm install +``` + +## Run + +From repo root: + +```bash +# quickstart +npm run quickstart + +# cookbook (local) +npm run cookbook create-mint:action +npm run cookbook compress:action +# ... see cookbook/package.json for more + +# payments +npm run toolkit:payments send-and-receive +``` + +Cookbook examples use local test-validator (install via `npm i -g @lightprotocol/zk-compression-cli@alpha`, then run `light test-validator`). diff --git a/cookbook/.env.example b/cookbook/.env.example deleted file mode 100644 index abc0cdb..0000000 --- a/cookbook/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -HELIUS_API_KEY=your-helius-api-key-here -LIGHT_PROTOCOL_VERSION=V2 diff --git a/cookbook/actions/compress-batch.ts b/cookbook/actions/compress-batch.ts index 654a4d5..e0ab6f0 100644 --- a/cookbook/actions/compress-batch.ts +++ b/cookbook/actions/compress-batch.ts @@ -2,71 +2,58 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, - compress, + createMint, + mintTo, + decompress, + compress, } from "@lightprotocol/compressed-token"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); - - // 3. Create SPL ATA and fund it - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(10000)); - await decompress(rpc, payer, mint, bn(10000), owner, splAta); - console.log("Funded SPL ATA with 10000 tokens"); - - // 4. Batch compress to multiple recipients (max 10 for V2 trees) - const recipients = Array.from({ length: 5 }, () => Keypair.generate().publicKey); - const amounts = [bn(100), bn(200), bn(300), bn(400), bn(500)]; - - const signature = await compress( - rpc, - payer, - mint, - amounts, // array of amounts - owner, - splAta, - recipients // array of recipients (same length as amounts) - ); - - console.log("Batch compressed to 5 recipients"); - console.log("Amounts:", amounts.map((a) => a.toString()).join(", ")); - console.log("Transaction:", signature); +async function main() { + const rpc = createRpc(RPC_URL); + + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); + + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + payer.publicKey + ); + + // Fund SPL ATA: mint compressed, then decompress + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(10000)); + await decompress(rpc, payer, mint, bn(10000), payer, splAta); + + // Batch compress to multiple recipients + const recipients = Array.from( + { length: 5 }, + () => Keypair.generate().publicKey + ); + const amounts = [bn(100), bn(200), bn(300), bn(400), bn(500)]; + + const signature = await compress( + rpc, + payer, + mint, + amounts, + payer, + splAta, + recipients + ); + + console.log("Batch compressed to 5 recipients"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/compress.ts b/cookbook/actions/compress.ts index 3047502..9791a1a 100644 --- a/cookbook/actions/compress.ts +++ b/cookbook/actions/compress.ts @@ -2,71 +2,52 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, - compress, + createMint, + mintTo, + decompress, + compress, } from "@lightprotocol/compressed-token"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); - - // 3. Create SPL ATA and fund it with SPL tokens - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - // Mint compressed then decompress to get SPL tokens - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - await decompress(rpc, payer, mint, bn(1000), owner, splAta); - console.log("Funded SPL ATA with 1000 tokens"); - - // 4. Compress SPL tokens to cold storage - const recipient = Keypair.generate(); - - const signature = await compress( - rpc, - payer, - mint, - bn(500), // amount to compress - owner, // owner of SPL account (signer) - splAta, // source SPL token account - recipient.publicKey // recipient of compressed tokens - ); - - console.log("Compressed 500 tokens to cold storage"); - console.log("Recipient:", recipient.publicKey.toBase58()); - console.log("Transaction:", signature); +async function main() { + const rpc = createRpc(RPC_URL); + + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); + + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + payer.publicKey + ); + + // Fund SPL ATA: mint compressed, then decompress + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); + await decompress(rpc, payer, mint, bn(1000), payer, splAta); + + const recipient = Keypair.generate(); + const signature = await compress( + rpc, + payer, + mint, + bn(500), + payer, + splAta, + recipient.publicKey + ); + + console.log("Compressed 500 tokens to:", recipient.publicKey.toBase58()); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/create-ata.ts b/cookbook/actions/create-ata.ts index d477592..95a347f 100644 --- a/cookbook/actions/create-ata.ts +++ b/cookbook/actions/create-ata.ts @@ -1,51 +1,29 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { - createRpc, - featureFlags, - VERSION, -} from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - getAssociatedTokenAddressInterface, + createMintInterface, + createAtaInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Setup RPC and fund payer - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - // 2. Create a light-mint - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, // mintAuthority - null, // freezeAuthority - 9, // decimals - mintSigner - ); - console.log("Mint:", mint.toBase58()); + const rpc = createRpc(RPC_URL); - // 3. Create associated token account for owner - const owner = Keypair.generate(); - const txSignature = await createAtaInterface(rpc, payer, mint, owner.publicKey); - console.log("ATA created for:", owner.publicKey.toBase58()); - console.log("Transaction:", txSignature); + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + console.log("Mint:", mint.toBase58()); - // 4. Derive the ATA address - const ata = getAssociatedTokenAddressInterface(mint, owner.publicKey); - console.log("ATA address:", ata.toBase58()); + const owner = Keypair.generate(); + const ata = await createAtaInterface(rpc, payer, mint, owner.publicKey); + console.log("ATA:", ata.toBase58()); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/create-mint.ts b/cookbook/actions/create-mint.ts index 858dd7d..3faf198 100644 --- a/cookbook/actions/create-mint.ts +++ b/cookbook/actions/create-mint.ts @@ -1,57 +1,41 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { - createRpc, - featureFlags, - VERSION, -} from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createTokenMetadata, + createMintInterface, + createTokenMetadata, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Connect to local test-validator (default: localhost:8899) - // Start with: light test-validator - const rpc = createRpc(); - - // 2. Load payer keypair from local Solana config - // For localnet, use the test keypair or airdrop SOL - const payer = Keypair.generate(); - - // Airdrop SOL to payer for localnet - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - // 3. Generate a new mint signer keypair - const mintSigner = Keypair.generate(); - - // 4. Generate mint authority keypair - const mintAuthority = Keypair.generate(); - - // 5. Create compressed mint with token metadata - // The SDK auto-fetches V2 tree info from local validator - const { mint, transactionSignature } = await createMintInterface( - rpc, - payer, - mintAuthority, // mintAuthority (must be Signer for compressed mints) - null, // freezeAuthority - 9, // decimals - mintSigner, - { skipPreflight: true }, // confirmOptions - undefined, // programId (defaults to CTOKEN_PROGRAM_ID) - createTokenMetadata( - "Example Token", - "EXT", - "https://example.com/metadata.json" - ), - ); - - console.log("Mint created:", mint.toBase58()); - console.log(`Transaction: ${transactionSignature}`); + const rpc = createRpc(RPC_URL); + + const { mint, transactionSignature } = await createMintInterface( + rpc, + payer, + payer, // mintAuthority + null, // freezeAuthority + 9, + undefined, // mintSigner + undefined, // confirmOptions + undefined, // programId + createTokenMetadata( + "Example Token", + "EXT", + "https://example.com/metadata.json" + ) + ); + + console.log("Mint:", mint.toBase58()); + console.log("Tx:", transactionSignature); } main().catch(console.error); diff --git a/cookbook/actions/decompress-with-interface-pda.ts b/cookbook/actions/decompress-with-interface-pda.ts new file mode 100644 index 0000000..e48024a --- /dev/null +++ b/cookbook/actions/decompress-with-interface-pda.ts @@ -0,0 +1,60 @@ +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc, bn } from "@lightprotocol/stateless.js"; +import { + createMint, + mintTo, + decompress, + getTokenPoolInfos, + selectTokenPoolInfosForDecompression, + selectSplInterfaceInfosForDecompression, + getSplInterfaceInfos, +} from "@lightprotocol/compressed-token"; +import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +async function main() { + const rpc = createRpc(RPC_URL); + + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); + + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + payer.publicKey + ); + + // Get and select Interface Info for decompression + const amount = bn(500); + const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); + const splInterfaceInfo = selectSplInterfaceInfosForDecompression( + splInterfaceInfos, + amount + ); + + const signature = await decompress( + rpc, + payer, + mint, + amount, + payer, + splAta, + splInterfaceInfo + ); + + console.log(`Decompressed ${amount.toString()} tokens`); + console.log("Tx:", signature); +} + +main().catch(console.error); diff --git a/cookbook/actions/decompress-with-token-pool.ts b/cookbook/actions/decompress-with-token-pool.ts deleted file mode 100644 index 3f325a5..0000000 --- a/cookbook/actions/decompress-with-token-pool.ts +++ /dev/null @@ -1,79 +0,0 @@ -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { - createMint, - mintTo, - decompress, - getTokenPoolInfos, - selectTokenPoolInfosForDecompression, -} from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount } from "@solana/spl-token"; - -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); - - // 3. Mint compressed tokens (cold storage) - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens"); - - // 4. Create destination SPL ATA - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); - - // 5. Get token pool infos and select for decompression - const tokenPoolInfos = await getTokenPoolInfos(rpc, mint); - console.log("Token pools found:", tokenPoolInfos.length); - - const selectedPools = selectTokenPoolInfosForDecompression( - tokenPoolInfos, - bn(500) // amount to decompress - ); - console.log("Selected pools:", selectedPools.length); - - // 6. Decompress with explicit token pool selection - const signature = await decompress( - rpc, - payer, - mint, - bn(500), - owner, - splAta, - selectedPools // optional: explicitly pass token pools - ); - - console.log("Decompressed 500 tokens to SPL ATA"); - console.log("Transaction:", signature); -} - -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); diff --git a/cookbook/actions/decompress.ts b/cookbook/actions/decompress.ts index b462724..3b619ec 100644 --- a/cookbook/actions/decompress.ts +++ b/cookbook/actions/decompress.ts @@ -2,65 +2,47 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, + createMint, + mintTo, + decompress, } from "@lightprotocol/compressed-token"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); - - // 3. Mint compressed tokens (cold storage) - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens"); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - // 4. Create destination SPL ATA (must exist before decompressing) - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); - - // 5. Decompress to SPL ATA - const signature = await decompress( - rpc, - payer, - mint, - bn(500), // amount to decompress - owner, // owner of compressed tokens (signer) - splAta // destination SPL token account - ); - - console.log("Decompressed 500 tokens to SPL ATA"); - console.log("Transaction:", signature); +async function main() { + const rpc = createRpc(RPC_URL); + + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); + + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); + + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + payer.publicKey + ); + + const signature = await decompress( + rpc, + payer, + mint, + bn(500), + payer, + splAta + ); + + console.log("Decompressed 500 tokens to SPL ATA"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/delegate-approve.ts b/cookbook/actions/delegate-approve.ts index fb93dff..1055c11 100644 --- a/cookbook/actions/delegate-approve.ts +++ b/cookbook/actions/delegate-approve.ts @@ -1,66 +1,33 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { - createMint, - mintTo, - approve, -} from "@lightprotocol/compressed-token"; +import { createMint, mintTo, approve } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - // 2. Create mint and mint tokens to owner - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); +async function main() { + const rpc = createRpc(RPC_URL); - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens to owner"); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 3. Create delegate - const delegate = Keypair.generate(); - console.log("Delegate:", delegate.publicKey.toBase58()); + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - // 4. Approve delegation - const signature = await approve( - rpc, - payer, - mint, - bn(500), // amount to delegate - owner, // owner of tokens (signer) - delegate.publicKey // delegate address - ); + const delegate = Keypair.generate(); + const signature = await approve(rpc, payer, mint, bn(500), payer, delegate.publicKey); - console.log("Approved delegation of 500 tokens"); - console.log("Transaction:", signature); + console.log("Approved delegation of 500 tokens to:", delegate.publicKey.toBase58()); + console.log("Tx:", signature); - // 5. Verify delegation - const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate( - delegate.publicKey, - { mint } - ); - console.log("Delegated accounts:", delegatedAccounts.items.length); + const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate(delegate.publicKey, { mint }); + console.log("Delegated accounts:", delegatedAccounts.items.length); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/delegate-revoke.ts b/cookbook/actions/delegate-revoke.ts index e1da000..f7e7e17 100644 --- a/cookbook/actions/delegate-revoke.ts +++ b/cookbook/actions/delegate-revoke.ts @@ -1,74 +1,39 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { - createMint, - mintTo, - approve, - revoke, -} from "@lightprotocol/compressed-token"; +import { createMint, mintTo, approve, revoke } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +async function main() { + const rpc = createRpc(RPC_URL); - // 2. Create mint and mint tokens - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens to owner"); + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - // 3. Approve delegation - const delegate = Keypair.generate(); - console.log("Delegate:", delegate.publicKey.toBase58()); - await approve(rpc, payer, mint, bn(500), owner, delegate.publicKey); - console.log("Approved delegation of 500 tokens"); + const delegate = Keypair.generate(); + await approve(rpc, payer, mint, bn(500), payer, delegate.publicKey); + console.log("Approved delegation to:", delegate.publicKey.toBase58()); - // 4. Get delegated accounts - const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate( - delegate.publicKey, - { mint } - ); - console.log("Before revoke:", delegatedAccounts.items.length, "accounts"); + const delegatedAccounts = await rpc.getCompressedTokenAccountsByDelegate(delegate.publicKey, { mint }); + console.log("Delegated accounts:", delegatedAccounts.items.length); - // 5. Revoke delegation - const signature = await revoke( - rpc, - payer, - delegatedAccounts.items, // accounts to revoke - owner // owner (signer) - ); + const signature = await revoke(rpc, payer, delegatedAccounts.items, payer); - console.log("Revoked delegation"); - console.log("Transaction:", signature); + console.log("Revoked delegation"); + console.log("Tx:", signature); - // 6. Verify revocation - const afterRevoke = await rpc.getCompressedTokenAccountsByDelegate( - delegate.publicKey, - { mint } - ); - console.log("After revoke:", afterRevoke.items.length, "accounts"); + const afterRevoke = await rpc.getCompressedTokenAccountsByDelegate(delegate.publicKey, { mint }); + console.log("After revoke:", afterRevoke.items.length, "accounts"); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/load-ata.ts b/cookbook/actions/load-ata.ts index c288f95..6675333 100644 --- a/cookbook/actions/load-ata.ts +++ b/cookbook/actions/load-ata.ts @@ -2,63 +2,40 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - loadAta, - getAssociatedTokenAddressInterface, + createMint, + mintTo, + loadAta, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +async function main() { + const rpc = createRpc(RPC_URL); - // 2. Create mint and mint compressed tokens (cold) - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens (cold)"); + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - // 3. Get c-token ATA address - const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - console.log("c-token ATA:", ctokenAta.toBase58()); + const ctokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); - // 4. Load compressed tokens to hot balance - // Creates ATA if needed, returns null if nothing to load - const signature = await loadAta( - rpc, - ctokenAta, // c-token ATA address - owner, // owner (signer) - mint, - payer // optional: fee payer - ); + // Load compressed tokens (cold) to hot balance, creates ATA if needed + const signature = await loadAta(rpc, ctokenAta, payer, mint, payer); - if (signature) { - console.log("Loaded tokens to hot balance"); - console.log("Transaction:", signature); - } else { - console.log("Nothing to load"); - } + if (signature) { + console.log("Loaded tokens to hot balance"); + console.log("Tx:", signature); + } else { + console.log("Nothing to load"); + } } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/merge-token-accounts.ts b/cookbook/actions/merge-token-accounts.ts index 6800317..cc9042c 100644 --- a/cookbook/actions/merge-token-accounts.ts +++ b/cookbook/actions/merge-token-accounts.ts @@ -1,87 +1,37 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { - createMint, - mintTo, - mergeTokenAccounts, -} from "@lightprotocol/compressed-token"; +import { createMint, mintTo, mergeTokenAccounts } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - // 2. Create mint - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); +async function main() { + const rpc = createRpc(RPC_URL); - // 3. Mint multiple times to create multiple compressed accounts - console.log("Minting 5 times to create multiple accounts..."); - for (let i = 0; i < 5; i++) { - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(100)); - } - console.log("Minted 500 total tokens across 5 accounts"); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 4. Check accounts before merge - const accountsBefore = await rpc.getCompressedTokenAccountsByOwner( - owner.publicKey, - { mint } - ); - console.log("Accounts before merge:", accountsBefore.items.length); + // Mint multiple times to create multiple compressed accounts + for (let i = 0; i < 5; i++) { + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(100)); + } - // 5. Merge token accounts - const signature = await mergeTokenAccounts( - rpc, - payer, - mint, - owner // owner (signer) - ); + const accountsBefore = await rpc.getCompressedTokenAccountsByOwner(payer.publicKey, { mint }); + console.log("Accounts before merge:", accountsBefore.items.length); - console.log("Merged token accounts"); - console.log("Transaction:", signature); + const signature = await mergeTokenAccounts(rpc, payer, mint, payer); - // 6. Check accounts after merge - const accountsAfter = await rpc.getCompressedTokenAccountsByOwner( - owner.publicKey, - { mint } - ); - console.log("Accounts after merge:", accountsAfter.items.length); + console.log("Tx:", signature); - // 7. Verify total balance unchanged - const totalBefore = accountsBefore.items.reduce( - (sum, acc) => sum.add(acc.parsed.amount), - bn(0) - ); - const totalAfter = accountsAfter.items.reduce( - (sum, acc) => sum.add(acc.parsed.amount), - bn(0) - ); - console.log("Total balance before:", totalBefore.toString()); - console.log("Total balance after:", totalAfter.toString()); - console.log( - "Balance preserved:", - totalBefore.toString() === totalAfter.toString() - ); + const accountsAfter = await rpc.getCompressedTokenAccountsByOwner(payer.publicKey, { mint }); + console.log("Accounts after merge:", accountsAfter.items.length); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/mint-to.ts b/cookbook/actions/mint-to.ts index 4fb100f..c8ac7da 100644 --- a/cookbook/actions/mint-to.ts +++ b/cookbook/actions/mint-to.ts @@ -1,63 +1,48 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { - createRpc, - featureFlags, - VERSION, -} from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - mintToInterface, - getAssociatedTokenAddressInterface, + createMintInterface, + createAtaInterface, + mintToInterface, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Setup RPC and fund payer - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - // 2. Create a light-mint (payer is mint authority) - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, // mintAuthority - null, // freezeAuthority - 9, // decimals - mintSigner, - ); - console.log("Mint created:", mint.toBase58()); - - // 3. Create associated token account for recipient - const recipient = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, recipient.publicKey); - console.log("Recipient ATA created for:", recipient.publicKey.toBase58()); - - // 4. Mint tokens to the recipient's account - const destination = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - const amount = 1_000_000_000; // 1 token with 9 decimals - - const txSignature = await mintToInterface( - rpc, - payer, - mint, - destination, - payer, // mintAuthority (must be Signer) - amount, - ); - - console.log("Minted tokens:", amount); - console.log("Transaction:", txSignature); + const rpc = createRpc(RPC_URL); + + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + console.log("Mint:", mint.toBase58()); + + const recipient = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, recipient.publicKey); + + const destination = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); + const amount = 1_000_000_000; + + const txSignature = await mintToInterface( + rpc, + payer, + mint, + destination, + payer, + amount + ); + + console.log("Minted:", amount); + console.log("Tx:", txSignature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/transfer-interface.ts b/cookbook/actions/transfer-interface.ts index 13f8adf..a85d419 100644 --- a/cookbook/actions/transfer-interface.ts +++ b/cookbook/actions/transfer-interface.ts @@ -1,73 +1,56 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; import { - createRpc, - featureFlags, - VERSION, -} from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - mintToInterface, - transferInterface, - getAssociatedTokenAddressInterface, + createMintInterface, + createAtaInterface, + mintToInterface, + transferInterface, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Setup RPC and fund payer - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - // 2. Create a light-mint - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, - null, - 9, - mintSigner - ); - console.log("Mint:", mint.toBase58()); - - // 3. Create sender's ATA and mint tokens - const sender = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, sender.publicKey); - const senderAta = getAssociatedTokenAddressInterface(mint, sender.publicKey); - await mintToInterface(rpc, payer, mint, senderAta, payer, 1_000_000_000); - console.log("Sender ATA:", senderAta.toBase58()); - - // 4. Create recipient's ATA - const recipient = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, recipient.publicKey); - const recipientAta = getAssociatedTokenAddressInterface( - mint, - recipient.publicKey - ); - console.log("Recipient ATA:", recipientAta.toBase58()); - - // 5. Transfer tokens - const txSignature = await transferInterface( - rpc, - payer, - senderAta, - mint, - recipientAta, - sender, // owner (must be Signer) - 500_000_000 // amount to transfer - ); - - console.log("Transferred 0.5 tokens"); - console.log("Transaction:", txSignature); + const rpc = createRpc(RPC_URL); + + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + console.log("Mint:", mint.toBase58()); + + const sender = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, sender.publicKey); + const senderAta = getAssociatedTokenAddressInterface( + mint, + sender.publicKey + ); + await mintToInterface(rpc, payer, mint, senderAta, payer, 1_000_000_000); + + const recipient = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, recipient.publicKey); + const recipientAta = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); + + const txSignature = await transferInterface( + rpc, + payer, + senderAta, + mint, + recipientAta, + sender, + 500_000_000 + ); + + console.log("Transferred 0.5 tokens"); + console.log("Tx:", txSignature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/unwrap.ts b/cookbook/actions/unwrap.ts index 7a530c4..09d76b0 100644 --- a/cookbook/actions/unwrap.ts +++ b/cookbook/actions/unwrap.ts @@ -1,67 +1,34 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { - createMint, - mintTo, -} from "@lightprotocol/compressed-token"; +import { createMint, mintTo } from "@lightprotocol/compressed-token"; import { unwrap } from "@lightprotocol/compressed-token/unified"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +async function main() { + const rpc = createRpc(RPC_URL); - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 3. Mint compressed tokens to owner - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens"); + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - // 4. Create destination SPL ATA (must exist before unwrap) - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); + const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey); - // 5. Unwrap c-tokens to SPL ATA - // This auto-loads compressed tokens to hot balance first - const signature = await unwrap( - rpc, - payer, - splAta, // destination: SPL token account - owner, // owner of c-tokens (signer) - mint, - bn(500) // amount to unwrap (omit for full balance) - ); + // Unwrap auto-loads compressed tokens to hot balance first + const signature = await unwrap(rpc, payer, splAta, payer, mint, bn(500)); - console.log("Unwrapped 500 tokens to SPL ATA"); - console.log("Transaction:", signature); + console.log("Unwrapped 500 tokens to SPL ATA"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/actions/wrap.ts b/cookbook/actions/wrap.ts index 00ab676..de1454e 100644 --- a/cookbook/actions/wrap.ts +++ b/cookbook/actions/wrap.ts @@ -2,75 +2,43 @@ import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, - wrap, - getAssociatedTokenAddressInterface, - createAtaInterfaceIdempotent, + createMint, + mintTo, + decompress, + wrap, + getAssociatedTokenAddressInterface, + createAtaInterfaceIdempotent, } from "@lightprotocol/compressed-token"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +async function main() { + const rpc = createRpc(RPC_URL); - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 3. Create SPL ATA and fund it - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); + const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey); - // Mint compressed then decompress to SPL ATA - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - await decompress(rpc, payer, mint, bn(1000), owner, splAta); - console.log("Funded SPL ATA with 1000 tokens"); + // Fund SPL ATA: mint compressed, then decompress + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); + await decompress(rpc, payer, mint, bn(1000), payer, splAta); - // 4. Create c-token ATA (destination) - const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - await createAtaInterfaceIdempotent(rpc, payer, mint, owner.publicKey); - console.log("c-token ATA:", ctokenAta.toBase58()); + const ctokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey); - // 5. Wrap SPL tokens to c-token ATA - const signature = await wrap( - rpc, - payer, - splAta, // source: SPL token account - ctokenAta, // destination: c-token ATA - owner, // owner of source account (signer) - mint, - bn(500) // amount to wrap - ); + const signature = await wrap(rpc, payer, splAta, ctokenAta, payer, mint, bn(500)); - console.log("Wrapped 500 tokens to c-token ATA"); - console.log("Transaction:", signature); + console.log("Wrapped 500 tokens to c-token ATA:", ctokenAta.toBase58()); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/instructions/create-ata.ts b/cookbook/instructions/create-ata.ts index 6c9bf37..5334f83 100644 --- a/cookbook/instructions/create-ata.ts +++ b/cookbook/instructions/create-ata.ts @@ -1,71 +1,53 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram } from "@solana/web3.js"; import { - createRpc, - buildAndSignTx, - sendAndConfirmTx, - featureFlags, - VERSION, - CTOKEN_PROGRAM_ID, + createRpc, + buildAndSignTx, + sendAndConfirmTx, + CTOKEN_PROGRAM_ID, } from "@lightprotocol/stateless.js"; import { - createMintInterface, - createAssociatedTokenAccountInterfaceInstruction, - getAssociatedTokenAddressInterface, + createMintInterface, + createAssociatedTokenAccountInterfaceInstruction, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Setup RPC and fund payer - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - // 2. Create a light-mint - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, // mintAuthority - null, // freezeAuthority - 9, // decimals - mintSigner - ); - console.log("Mint:", mint.toBase58()); - - // 3. Derive the ATA address - const owner = Keypair.generate(); - const associatedToken = getAssociatedTokenAddressInterface(mint, owner.publicKey); - console.log("Owner:", owner.publicKey.toBase58()); - console.log("ATA address:", associatedToken.toBase58()); - - // 4. Create the instruction - const ix = createAssociatedTokenAccountInterfaceInstruction( - payer.publicKey, // payer - associatedToken, // associatedToken - owner.publicKey, // owner - mint, // mint - CTOKEN_PROGRAM_ID // programId (cToken) - ); - - // 5. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 100_000 }), ix], - payer, - blockhash - ); - const signature = await sendAndConfirmTx(rpc, tx); - - console.log("ATA created"); - console.log("Transaction:", signature); + const rpc = createRpc(RPC_URL); + + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + console.log("Mint:", mint.toBase58()); + + const owner = Keypair.generate(); + const associatedToken = getAssociatedTokenAddressInterface(mint, owner.publicKey); + + const ix = createAssociatedTokenAccountInterfaceInstruction( + payer.publicKey, + associatedToken, + owner.publicKey, + mint, + CTOKEN_PROGRAM_ID + ); + + const { blockhash } = await rpc.getLatestBlockhash(); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 100_000 }), ix], + payer, + blockhash + ); + const signature = await sendAndConfirmTx(rpc, tx); + + console.log("ATA:", associatedToken.toBase58()); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/instructions/create-mint.ts b/cookbook/instructions/create-mint.ts index 7cb9a91..eab10be 100644 --- a/cookbook/instructions/create-mint.ts +++ b/cookbook/instructions/create-mint.ts @@ -1,99 +1,72 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram, PublicKey } from "@solana/web3.js"; import { - createRpc, - buildAndSignTx, - sendAndConfirmTx, - getBatchAddressTreeInfo, - selectStateTreeInfo, - featureFlags, - VERSION, - CTOKEN_PROGRAM_ID, - DerivationMode, + createRpc, + buildAndSignTx, + sendAndConfirmTx, + getBatchAddressTreeInfo, + selectStateTreeInfo, + CTOKEN_PROGRAM_ID, + DerivationMode, } from "@lightprotocol/stateless.js"; -import { - createMintInstruction, - createTokenMetadata, -} from "@lightprotocol/compressed-token"; - -featureFlags.version = VERSION.V2; +import { createMintInstruction, createTokenMetadata } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -// Inline findMintAddress since it's not exported from the package const COMPRESSED_MINT_SEED = Buffer.from("compressed_mint"); function findMintAddress(mintSigner: PublicKey): [PublicKey, number] { - return PublicKey.findProgramAddressSync( - [COMPRESSED_MINT_SEED, mintSigner.toBuffer()], - CTOKEN_PROGRAM_ID - ); + return PublicKey.findProgramAddressSync( + [COMPRESSED_MINT_SEED, mintSigner.toBuffer()], + CTOKEN_PROGRAM_ID + ); } -async function main() { - // 1. Setup RPC connection to local validator - const rpc = createRpc(); - - // 2. Create and fund payer - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - // 3. Prepare mint parameters - const mintSigner = Keypair.generate(); - const decimals = 9; +async function main() { + const rpc = createRpc(RPC_URL); - // 4. Get tree infos - // Address Merkle tree stores the mint address - // State Merkle tree stores the mint account data - const addressTreeInfo = getBatchAddressTreeInfo(); - const stateTreeInfo = selectStateTreeInfo( - await rpc.getStateTreeInfos() - ); + const mintSigner = Keypair.generate(); + const addressTreeInfo = getBatchAddressTreeInfo(); + const stateTreeInfo = selectStateTreeInfo(await rpc.getStateTreeInfos()); - // 5. Derive the mint PDA address - const [mintPda] = findMintAddress(mintSigner.publicKey); + const [mintPda] = findMintAddress(mintSigner.publicKey); - // 6. Get validity proof for address creation - // Proves the mint address does not exist yet - const validityProof = await rpc.getValidityProofV2( - [], - [{ address: mintPda.toBytes(), treeInfo: addressTreeInfo }], - DerivationMode.compressible - ); + const validityProof = await rpc.getValidityProofV2( + [], + [{ address: mintPda.toBytes(), treeInfo: addressTreeInfo }], + DerivationMode.compressible + ); - // 7. Create instruction - const ix = createMintInstruction( - mintSigner.publicKey, - decimals, - payer.publicKey, // mintAuthority - null, // freezeAuthority - payer.publicKey, // payer - validityProof, - addressTreeInfo, - stateTreeInfo, - createTokenMetadata( - "Example Token", - "EXT", - "https://example.com/metadata.json" - ) - ); + const ix = createMintInstruction( + mintSigner.publicKey, + 9, + payer.publicKey, // mintAuthority + null, // freezeAuthority + payer.publicKey, + validityProof, + addressTreeInfo, + stateTreeInfo, + createTokenMetadata("Example Token", "EXT", "https://example.com/metadata.json") + ); - // 8. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ix], - payer, - blockhash, - [mintSigner] - ); - const signature = await sendAndConfirmTx(rpc, tx, { skipPreflight: true }); + const { blockhash } = await rpc.getLatestBlockhash(); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ix], + payer, + blockhash, + [mintSigner] + ); + const signature = await sendAndConfirmTx(rpc, tx, { skipPreflight: true }); - console.log("Mint created:", mintPda.toBase58()); - console.log("Transaction:", signature); + console.log("Mint:", mintPda.toBase58()); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/instructions/load-ata.ts b/cookbook/instructions/load-ata.ts index 6575c6d..28724a3 100644 --- a/cookbook/instructions/load-ata.ts +++ b/cookbook/instructions/load-ata.ts @@ -1,84 +1,58 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram } from "@solana/web3.js"; import { - createRpc, - bn, - buildAndSignTx, - sendAndConfirmTx, - dedupeSigner, + createRpc, + bn, + buildAndSignTx, + sendAndConfirmTx, + dedupeSigner, } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - createLoadAtaInstructions, - getAssociatedTokenAddressInterface, + createMint, + mintTo, + createLoadAtaInstructions, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - // 2. Create mint and mint compressed tokens - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); +async function main() { + const rpc = createRpc(RPC_URL); - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens (cold)"); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 3. Get c-token ATA address - const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - console.log("c-token ATA:", ctokenAta.toBase58()); + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - // 4. Create load instructions - const ixs = await createLoadAtaInstructions( - rpc, - ctokenAta, - owner.publicKey, - mint, - payer.publicKey - ); + const ctokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); - if (ixs.length === 0) { - console.log("Nothing to load"); - return; - } + const ixs = await createLoadAtaInstructions(rpc, ctokenAta, payer.publicKey, mint, payer.publicKey); - console.log("Created", ixs.length, "load instructions"); + if (ixs.length === 0) { + console.log("Nothing to load"); + return; + } - // 5. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const additionalSigners = dedupeSigner(payer, [owner]); + const { blockhash } = await rpc.getLatestBlockhash(); + const additionalSigners = dedupeSigner(payer, [payer]); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ...ixs], - payer, - blockhash, - additionalSigners - ); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ...ixs], + payer, + blockhash, + additionalSigners + ); - const signature = await sendAndConfirmTx(rpc, tx); - console.log("Loaded tokens to hot balance"); - console.log("Transaction:", signature); + const signature = await sendAndConfirmTx(rpc, tx); + console.log("Loaded tokens to hot balance"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/instructions/mint-to.ts b/cookbook/instructions/mint-to.ts index d52baa3..7bb9884 100644 --- a/cookbook/instructions/mint-to.ts +++ b/cookbook/instructions/mint-to.ts @@ -1,92 +1,77 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram } from "@solana/web3.js"; import { - createRpc, - buildAndSignTx, - sendAndConfirmTx, - bn, - DerivationMode, - featureFlags, - VERSION, + createRpc, + buildAndSignTx, + sendAndConfirmTx, + bn, + DerivationMode, } from "@lightprotocol/stateless.js"; import { - createMintInterface, - createAtaInterface, - createMintToInterfaceInstruction, - getMintInterface, - getAssociatedTokenAddressInterface, + createMintInterface, + createAtaInterface, + createMintToInterfaceInstruction, + getMintInterface, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Setup RPC and fund payer - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); + const rpc = createRpc(RPC_URL); - // 2. Create a light-mint (payer is mint authority) - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, - null, - 9, - mintSigner, - ); - console.log("Mint created:", mint.toBase58()); + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + console.log("Mint:", mint.toBase58()); - // 3. Create associated token account for recipient - const recipient = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, recipient.publicKey); - const destination = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - console.log("Recipient ATA created:", destination.toBase58()); + const recipient = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, recipient.publicKey); + const destination = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - // 4. Get mint interface (includes merkle context for c-tokens) - const mintInterface = await getMintInterface(rpc, mint); + const mintInterface = await getMintInterface(rpc, mint); - // 5. Get validity proof for the mint (required for c-token mints) - let validityProof; - if (mintInterface.merkleContext) { - validityProof = await rpc.getValidityProofV2( - [ - { - hash: bn(mintInterface.merkleContext.hash), - leafIndex: mintInterface.merkleContext.leafIndex, - treeInfo: mintInterface.merkleContext.treeInfo, - proveByIndex: mintInterface.merkleContext.proveByIndex, - }, - ], - [], - DerivationMode.compressible, - ); - } + let validityProof; + if (mintInterface.merkleContext) { + validityProof = await rpc.getValidityProofV2( + [ + { + hash: bn(mintInterface.merkleContext.hash), + leafIndex: mintInterface.merkleContext.leafIndex, + treeInfo: mintInterface.merkleContext.treeInfo, + proveByIndex: mintInterface.merkleContext.proveByIndex, + }, + ], + [], + DerivationMode.compressible + ); + } - // 6. Create instruction - const amount = 1_000_000_000; - const ix = createMintToInterfaceInstruction( - mintInterface, - destination, - payer.publicKey, // authority - payer.publicKey, // payer - amount, - validityProof, - ); + const amount = 1_000_000_000; + const ix = createMintToInterfaceInstruction( + mintInterface, + destination, + payer.publicKey, + payer.publicKey, + amount, + validityProof + ); - // 7. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ix], - payer, - blockhash, - ); - const signature = await sendAndConfirmTx(rpc, tx); + const { blockhash } = await rpc.getLatestBlockhash(); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 }), ix], + payer, + blockhash + ); + const signature = await sendAndConfirmTx(rpc, tx); - console.log("Minted tokens:", amount); - console.log("Transaction:", signature); + console.log("Minted:", amount); + console.log("Tx:", signature); } main().catch(console.error); diff --git a/cookbook/instructions/transfer-interface.ts b/cookbook/instructions/transfer-interface.ts index 5217b53..d6d2c38 100644 --- a/cookbook/instructions/transfer-interface.ts +++ b/cookbook/instructions/transfer-interface.ts @@ -1,83 +1,55 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram } from "@solana/web3.js"; import { - createRpc, - buildAndSignTx, - sendAndConfirmTx, - featureFlags, - VERSION, + createRpc, + buildAndSignTx, + sendAndConfirmTx, } from "@lightprotocol/stateless.js"; import { - createMintInterface, - createAtaInterface, - mintToInterface, - createTransferInterfaceInstruction, - getAssociatedTokenAddressInterface, + createMintInterface, + createAtaInterface, + mintToInterface, + createTransferInterfaceInstruction, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -featureFlags.version = VERSION.V2; +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); async function main() { - // 1. Setup RPC and fund payer - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); + const rpc = createRpc(RPC_URL); - // 2. Create a light-mint - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, - null, - 9, - mintSigner - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMintInterface(rpc, payer, payer, null, 9); + console.log("Mint:", mint.toBase58()); - // 3. Create sender's ATA and mint tokens - const sender = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, sender.publicKey); - const senderAta = getAssociatedTokenAddressInterface(mint, sender.publicKey); - await mintToInterface(rpc, payer, mint, senderAta, payer, 1_000_000_000); - console.log("Sender ATA:", senderAta.toBase58()); + const sender = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, sender.publicKey); + const senderAta = getAssociatedTokenAddressInterface(mint, sender.publicKey); + await mintToInterface(rpc, payer, mint, senderAta, payer, 1_000_000_000); - // 4. Create recipient's ATA - const recipient = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, recipient.publicKey); - const recipientAta = getAssociatedTokenAddressInterface( - mint, - recipient.publicKey - ); - console.log("Recipient ATA:", recipientAta.toBase58()); + const recipient = Keypair.generate(); + await createAtaInterface(rpc, payer, mint, recipient.publicKey); + const recipientAta = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - // 5. Create transfer instruction - const amount = 500_000_000; - const ix = createTransferInterfaceInstruction( - senderAta, // source - recipientAta, // destination - sender.publicKey, // owner - amount - ); + const ix = createTransferInterfaceInstruction(senderAta, recipientAta, sender.publicKey, 500_000_000); - // 6. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 10_000 }), ix], - payer, - blockhash, - [sender] - ); - const signature = await sendAndConfirmTx(rpc, tx); + const { blockhash } = await rpc.getLatestBlockhash(); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 10_000 }), ix], + payer, + blockhash, + [sender] + ); + const signature = await sendAndConfirmTx(rpc, tx); - console.log("Transferred 0.5 tokens"); - console.log("Transaction:", signature); + console.log("Transferred 0.5 tokens"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/instructions/unwrap.ts b/cookbook/instructions/unwrap.ts index ac689b9..303ce9e 100644 --- a/cookbook/instructions/unwrap.ts +++ b/cookbook/instructions/unwrap.ts @@ -1,101 +1,74 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram } from "@solana/web3.js"; import { - createRpc, - buildAndSignTx, - sendAndConfirmTx, - dedupeSigner, - bn, + createRpc, + buildAndSignTx, + sendAndConfirmTx, + dedupeSigner, + bn, } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - loadAta, - getAssociatedTokenAddressInterface, - getSplInterfaceInfos, + createMint, + mintTo, + loadAta, + getAssociatedTokenAddressInterface, + getSplInterfaceInfos, } from "@lightprotocol/compressed-token"; import { createUnwrapInstruction } from "@lightprotocol/compressed-token/unified"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +async function main() { + const rpc = createRpc(RPC_URL); - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 3. Mint compressed tokens to owner - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 compressed tokens"); + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); - // 4. Load compressed tokens to c-token ATA (hot balance) - const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - await loadAta(rpc, ctokenAta, owner, mint, payer); - console.log("Loaded compressed tokens to c-token ATA"); + const ctokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + await loadAta(rpc, ctokenAta, payer, mint, payer); - // 5. Create destination SPL ATA - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); + const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey); - // 6. Get SPL interface info for the mint - const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); - const splInterfaceInfo = splInterfaceInfos.find((info) => info.isInitialized); + const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); + const splInterfaceInfo = splInterfaceInfos.find((info) => info.isInitialized); - if (!splInterfaceInfo) { - throw new Error("No SPL interface found"); - } + if (!splInterfaceInfo) { + throw new Error("No SPL interface found"); + } - // 7. Create unwrap instruction - const ix = createUnwrapInstruction( - ctokenAta, // source: c-token ATA - splAta, // destination: SPL token account - owner.publicKey, // owner of source account - mint, - bn(500), // amount to unwrap - splInterfaceInfo, // SPL interface info - payer.publicKey // fee payer - ); + const ix = createUnwrapInstruction( + ctokenAta, + splAta, + payer.publicKey, + mint, + bn(500), + splInterfaceInfo, + payer.publicKey + ); - // 8. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const additionalSigners = dedupeSigner(payer, [owner]); + const { blockhash } = await rpc.getLatestBlockhash(); + const additionalSigners = dedupeSigner(payer, [payer]); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), ix], - payer, - blockhash, - additionalSigners - ); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), ix], + payer, + blockhash, + additionalSigners + ); - const signature = await sendAndConfirmTx(rpc, tx); - console.log("Unwrapped 500 tokens to SPL ATA"); - console.log("Transaction:", signature); + const signature = await sendAndConfirmTx(rpc, tx); + console.log("Unwrapped 500 tokens"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/instructions/wrap.ts b/cookbook/instructions/wrap.ts index e37047c..5df0564 100644 --- a/cookbook/instructions/wrap.ts +++ b/cookbook/instructions/wrap.ts @@ -1,102 +1,77 @@ import "dotenv/config"; import { Keypair, ComputeBudgetProgram } from "@solana/web3.js"; import { - createRpc, - buildAndSignTx, - sendAndConfirmTx, - dedupeSigner, - bn, + createRpc, + buildAndSignTx, + sendAndConfirmTx, + dedupeSigner, + bn, } from "@lightprotocol/stateless.js"; import { - createMint, - mintTo, - decompress, - createWrapInstruction, - getAssociatedTokenAddressInterface, - createAtaInterfaceIdempotent, - getSplInterfaceInfos, + createMint, + mintTo, + decompress, + createWrapInstruction, + getAssociatedTokenAddressInterface, + createAtaInterfaceIdempotent, + getSplInterfaceInfos, } from "@lightprotocol/compressed-token"; import { createAssociatedTokenAccount } from "@solana/spl-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; -async function main() { - // 1. Setup RPC and fund accounts - const rpc = createRpc(); - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - console.log("Payer:", payer.publicKey.toBase58()); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); +async function main() { + const rpc = createRpc(RPC_URL); - // 2. Create SPL mint with token pool - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + const { mint } = await createMint(rpc, payer, payer.publicKey, 9); + console.log("Mint:", mint.toBase58()); - // 3. Create SPL ATA and fund it - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); + const splAta = await createAssociatedTokenAccount(rpc, payer, mint, payer.publicKey); - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - await decompress(rpc, payer, mint, bn(1000), owner, splAta); - console.log("Funded SPL ATA with 1000 tokens"); + // Fund SPL ATA: mint compressed, then decompress + await mintTo(rpc, payer, mint, payer.publicKey, payer, bn(1000)); + await decompress(rpc, payer, mint, bn(1000), payer, splAta); - // 4. Create c-token ATA (destination) - const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - await createAtaInterfaceIdempotent(rpc, payer, mint, owner.publicKey); - console.log("c-token ATA:", ctokenAta.toBase58()); + const ctokenAta = getAssociatedTokenAddressInterface(mint, payer.publicKey); + await createAtaInterfaceIdempotent(rpc, payer, mint, payer.publicKey); - // 5. Get SPL interface info for the mint - const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); - const splInterfaceInfo = splInterfaceInfos.find((info) => info.isInitialized); + const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); + const splInterfaceInfo = splInterfaceInfos.find((info) => info.isInitialized); - if (!splInterfaceInfo) { - throw new Error("No SPL interface found"); - } + if (!splInterfaceInfo) { + throw new Error("No SPL interface found"); + } - // 6. Create wrap instruction - const ix = createWrapInstruction( - splAta, // source: SPL token account - ctokenAta, // destination: c-token ATA - owner.publicKey, // owner of source account - mint, - bn(500), // amount to wrap - splInterfaceInfo, // SPL interface info - payer.publicKey // fee payer - ); + const ix = createWrapInstruction( + splAta, + ctokenAta, + payer.publicKey, + mint, + bn(500), + splInterfaceInfo, + payer.publicKey + ); - // 7. Build, sign, and send transaction - const { blockhash } = await rpc.getLatestBlockhash(); - const additionalSigners = dedupeSigner(payer, [owner]); + const { blockhash } = await rpc.getLatestBlockhash(); + const additionalSigners = dedupeSigner(payer, [payer]); - const tx = buildAndSignTx( - [ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), ix], - payer, - blockhash, - additionalSigners - ); + const tx = buildAndSignTx( + [ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }), ix], + payer, + blockhash, + additionalSigners + ); - const signature = await sendAndConfirmTx(rpc, tx); - console.log("Wrapped 500 tokens to c-token ATA"); - console.log("Transaction:", signature); + const signature = await sendAndConfirmTx(rpc, tx); + console.log("Wrapped 500 tokens"); + console.log("Tx:", signature); } -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); +main().catch(console.error); diff --git a/cookbook/package.json b/cookbook/package.json index 04fedc9..e5dabe3 100644 --- a/cookbook/package.json +++ b/cookbook/package.json @@ -1,41 +1,41 @@ { - "name": "light-token-cookbook", - "version": "1.0.0", - "type": "module", - "description": "Working examples for light-token operations on Solana devnet", - "scripts": { - "create-mint:action": "node --loader ts-node/esm actions/create-mint.ts", - "create-mint:instruction": "node --loader ts-node/esm instructions/create-mint.ts", - "mint-to:action": "node --loader ts-node/esm actions/mint-to.ts", - "mint-to:instruction": "node --loader ts-node/esm instructions/mint-to.ts", - "transfer-interface:action": "node --loader ts-node/esm actions/transfer-interface.ts", - "transfer-interface:instruction": "node --loader ts-node/esm instructions/transfer-interface.ts", - "create-ata:action": "node --loader ts-node/esm actions/create-ata.ts", - "create-ata:instruction": "node --loader ts-node/esm instructions/create-ata.ts", - "compress:action": "node --loader ts-node/esm actions/compress.ts", - "compress:batch": "node --loader ts-node/esm actions/compress-batch.ts", - "decompress:action": "node --loader ts-node/esm actions/decompress.ts", - "decompress:with-token-pool": "node --loader ts-node/esm actions/decompress-with-token-pool.ts", - "wrap:action": "node --loader ts-node/esm actions/wrap.ts", - "wrap:instruction": "node --loader ts-node/esm instructions/wrap.ts", - "unwrap:action": "node --loader ts-node/esm actions/unwrap.ts", - "unwrap:instruction": "node --loader ts-node/esm instructions/unwrap.ts", - "load-ata:action": "node --loader ts-node/esm actions/load-ata.ts", - "load-ata:instruction": "node --loader ts-node/esm instructions/load-ata.ts", - "delegate:approve": "node --loader ts-node/esm actions/delegate-approve.ts", - "delegate:revoke": "node --loader ts-node/esm actions/delegate-revoke.ts", - "merge-accounts": "node --loader ts-node/esm actions/merge-token-accounts.ts" - }, - "dependencies": { - "@lightprotocol/compressed-token": "file:../light-protocol/js/compressed-token", - "@lightprotocol/stateless.js": "file:../light-protocol/js/stateless.js", - "@solana/spl-token": "^0.4.13", - "@solana/web3.js": "1.98.4", - "dotenv": "^16.5.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.0.0" - } + "name": "light-token-cookbook", + "version": "1.0.0", + "type": "module", + "description": "Working examples for light-token operations on Solana devnet", + "scripts": { + "create-mint:action": "ts-node-esm actions/create-mint.ts", + "create-mint:instruction": "ts-node-esm instructions/create-mint.ts", + "mint-to:action": "ts-node-esm actions/mint-to.ts", + "mint-to:instruction": "ts-node-esm instructions/mint-to.ts", + "transfer-interface:action": "ts-node-esm actions/transfer-interface.ts", + "transfer-interface:instruction": "ts-node-esm instructions/transfer-interface.ts", + "create-ata:action": "ts-node-esm actions/create-ata.ts", + "create-ata:instruction": "ts-node-esm instructions/create-ata.ts", + "compress:action": "ts-node-esm actions/compress.ts", + "compress:batch": "ts-node-esm actions/compress-batch.ts", + "decompress:action": "ts-node-esm actions/decompress.ts", + "decompress:with-token-pool": "ts-node-esm actions/decompress-with-interface-pda.ts", + "wrap:action": "ts-node-esm actions/wrap.ts", + "wrap:instruction": "ts-node-esm instructions/wrap.ts", + "unwrap:action": "ts-node-esm actions/unwrap.ts", + "unwrap:instruction": "ts-node-esm instructions/unwrap.ts", + "load-ata:action": "ts-node-esm actions/load-ata.ts", + "load-ata:instruction": "ts-node-esm instructions/load-ata.ts", + "delegate:approve": "ts-node-esm actions/delegate-approve.ts", + "delegate:revoke": "ts-node-esm actions/delegate-revoke.ts", + "merge-accounts": "ts-node-esm actions/merge-token-accounts.ts" + }, + "dependencies": { + "@lightprotocol/compressed-token": "^0.22.1-alpha", + "@lightprotocol/stateless.js": "^0.22.1-alpha", + "@solana/spl-token": "^0.4.13", + "@solana/web3.js": "1.98.4", + "dotenv": "^16.5.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.0.0" + } } diff --git a/cookbook/tsconfig.json b/cookbook/tsconfig.json index c2c463d..de8ab73 100644 --- a/cookbook/tsconfig.json +++ b/cookbook/tsconfig.json @@ -1,15 +1,15 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "Node16", - "moduleResolution": "Node16", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "outDir": "./dist" - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "moduleResolution": "Node16", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "./dist" + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] } diff --git a/devnet-quickstart/.env.example b/devnet-quickstart/.env.example deleted file mode 100644 index 490c20c..0000000 --- a/devnet-quickstart/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -api_key=your-helius-api-key-here -KEYPAIR_PATH=~/.config/solana/id.json diff --git a/devnet-quickstart/.gitignore b/devnet-quickstart/.gitignore deleted file mode 100644 index 87ae5ea..0000000 --- a/devnet-quickstart/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -dist/ -.env -*.json.bak diff --git a/devnet-quickstart/index.ts b/devnet-quickstart/index.ts deleted file mode 100644 index d7d14d5..0000000 --- a/devnet-quickstart/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -import "dotenv/config"; -import { Keypair } from "@solana/web3.js"; -import { readFileSync } from "fs"; -import { homedir } from "os"; -import { - createRpc, - featureFlags, - VERSION, -} from "@lightprotocol/stateless.js"; -import { - createMintInterface, - createAtaInterface, - mintToInterface, - getAssociatedTokenAddressInterface, -} from "@lightprotocol/compressed-token"; - -featureFlags.version = VERSION.V2; - -// Load wallet from filesystem -const keypairPath = - process.env.KEYPAIR_PATH?.replace("~", homedir()) || - `${homedir()}/.config/solana/id.json`; -const payer = Keypair.fromSecretKey( - new Uint8Array(JSON.parse(readFileSync(keypairPath, "utf8"))) -); - -// Helius exposes Solana and compression RPC endpoints through a single URL -const RPC_ENDPOINT = `https://devnet.helius-rpc.com?api-key=${process.env.api_key}`; - -async function main() { - // 1. Setup RPC connection - const rpc = createRpc(RPC_ENDPOINT, RPC_ENDPOINT, RPC_ENDPOINT); - console.log("Payer:", payer.publicKey.toBase58()); - - // 2. Create a light-mint (payer is mint authority) - const mintSigner = Keypair.generate(); - const { mint } = await createMintInterface( - rpc, - payer, - payer, // mintAuthority - null, // freezeAuthority - 9, // decimals - mintSigner, - ); - console.log("Mint created:", mint.toBase58()); - - // 3. Create associated token account for recipient - const recipient = Keypair.generate(); - await createAtaInterface(rpc, payer, mint, recipient.publicKey); - console.log("Recipient ATA created for:", recipient.publicKey.toBase58()); - - // 4. Mint tokens to the recipient's account - const destination = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - const amount = 1_000_000_000; // 1 token with 9 decimals - - const txSignature = await mintToInterface( - rpc, - payer, - mint, - destination, - payer, // mintAuthority (must be Signer) - amount, - ); - - console.log("Minted tokens:", amount); - console.log(`Transaction: https://explorer.solana.com/tx/${txSignature}?cluster=devnet`); -} - -main().catch((err) => { - console.error("Error:", err); - if (err.logs) console.error("Logs:", err.logs); - process.exit(1); -}); diff --git a/devnet-quickstart/package.json b/devnet-quickstart/package.json deleted file mode 100644 index 21bbb6d..0000000 --- a/devnet-quickstart/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "devnet-quickstart", - "type": "module", - "scripts": { - "test": "node --loader ts-node/esm index.ts" - }, - "dependencies": { - "@lightprotocol/compressed-token": "0.22.1-alpha.3", - "@lightprotocol/stateless.js": "0.22.1-alpha.2", - "@solana/web3.js": "1.98.4", - "dotenv": "^16.5.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.0.0" - } -} diff --git a/devnet-quickstart/tsconfig.json b/devnet-quickstart/tsconfig.json deleted file mode 100644 index c2c463d..0000000 --- a/devnet-quickstart/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "Node16", - "moduleResolution": "Node16", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "outDir": "./dist" - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] -} diff --git a/package.json b/package.json new file mode 100644 index 0000000..da09447 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "examples-light-token", + "private": true, + "workspaces": [ + "quickstart", + "cookbook", + "toolkits/payments-and-wallets" + ], + "scripts": { + "quickstart": "npm run -w quickstart start", + "cookbook": "npm run -w cookbook", + "toolkit:payments": "npm run -w toolkits/payments-and-wallets" + } +} diff --git a/quickstart/index.ts b/quickstart/index.ts new file mode 100644 index 0000000..97ad3dc --- /dev/null +++ b/quickstart/index.ts @@ -0,0 +1,80 @@ +import dotenv from "dotenv"; +import { Keypair } from "@solana/web3.js"; +import { createRpc } from "@lightprotocol/stateless.js"; +import { + mintToInterface, + createMintInterface, + getOrCreateAtaInterface, + getAtaInterface, +} from "@lightprotocol/compressed-token"; +import { homedir } from "os"; +import { readFileSync } from "fs"; + +// Load wallet +const payer = Keypair.fromSecretKey( + new Uint8Array( + JSON.parse(readFileSync(`${homedir()}/.config/solana/id.json`, "utf8")) + ) +); + +// Use a compatible RPC provider, such as Helius and Triton. +// Enable to use Devnet: +dotenv.config(); +const RPC_URL = `https://devnet.helius-rpc.com?api-key=${process.env.API_KEY!}`; + +(async function () { + const rpc = createRpc(RPC_URL); + + // 1. create test mint + const mintAuthority = payer; + const { mint, transactionSignature } = await createMintInterface( + rpc, + payer, + mintAuthority, + null, + 9 + ); + + // 2. get recipient's associated token account + const recipient = Keypair.generate(); + let recipientAta = await getOrCreateAtaInterface( + rpc, + payer, + mint, + recipient + ); + + // 3. mint to recipient + const txId = await mintToInterface( + rpc, + payer, + mint, + recipientAta.parsed.address, + payer, + 1e9 + ); + + // 4. fetch state + recipientAta = await getAtaInterface( + rpc, + recipientAta.parsed.address, + recipient.publicKey, + mint + ); + + console.log( + JSON.stringify( + { + payer: payer.publicKey.toBase58(), + mint: mint.toBase58(), + recipient: recipient.publicKey.toBase58(), + recipientAta: recipientAta.parsed.address.toBase58(), + recipientBalance: recipientAta.parsed.amount.toString(), + createMintTx: transactionSignature, + mintTx: txId, + }, + null, + 2 + ) + ); +})(); diff --git a/quickstart/package.json b/quickstart/package.json new file mode 100644 index 0000000..1f1137e --- /dev/null +++ b/quickstart/package.json @@ -0,0 +1,18 @@ +{ + "name": "quickstart", + "type": "module", + "scripts": { + "start": "cd .. && ts-node-esm quickstart/index.ts" + }, + "dependencies": { + "@lightprotocol/compressed-token": "^0.22.1-alpha.6", + "@lightprotocol/stateless.js": "^0.22.1-alpha.5", + "@solana/web3.js": "1.98.4", + "dotenv": "^16.5.0" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.0.0" + } +} diff --git a/quickstart/tsconfig.json b/quickstart/tsconfig.json new file mode 100644 index 0000000..de8ab73 --- /dev/null +++ b/quickstart/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "moduleResolution": "Node16", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "./dist" + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/toolkits/payments-and-wallets/README.md b/toolkits/payments-and-wallets/README.md index ba54a6e..028d2c5 100644 --- a/toolkits/payments-and-wallets/README.md +++ b/toolkits/payments-and-wallets/README.md @@ -2,25 +2,24 @@ ## SPL vs Light -| Operation | SPL | Light | -|-----------|-----|-------| -| Get/Create ATA | `getOrCreateAssociatedTokenAccount()` | `getOrCreateAtaInterface()` | -| Derive ATA | `getAssociatedTokenAddress()` | `getAssociatedTokenAddressInterface()` | -| Transfer | `transferChecked()` | `transferInterface()` | -| Get Balance | `getAccount()` | `getAtaInterface()` | -| Tx History | `getSignaturesForAddress()` | `rpc.getSignaturesForOwnerInterface()` | -| Exit to SPL | N/A | `unwrap()` | +| Operation | SPL | Light | +| -------------- | ------------------------------------- | -------------------------------------- | +| Get/Create ATA | `getOrCreateAssociatedTokenAccount()` | `getOrCreateAtaInterface()` | +| Derive ATA | `getAssociatedTokenAddress()` | `getAssociatedTokenAddressInterface()` | +| Transfer | `transferChecked()` | `transferInterface()` | +| Get Balance | `getAccount()` | `getAtaInterface()` | +| Tx History | `getSignaturesForAddress()` | `rpc.getSignaturesForOwnerInterface()` | +| Exit to SPL | N/A | `unwrap()` | ## Scripts -| File | Description | Key Function | -|------|-------------|--------------| -| `send-and-receive.ts` | Send/receive payments | `getOrCreateAtaInterface`, `transferInterface` | -| `get-balance.ts` | Check token balance | `getAtaInterface` | -| `get-history.ts` | Transaction history | `getSignaturesForOwnerInterface` | -| `wrap-from-spl.ts` | On-ramp from CEX (SPL → light-token) | `wrap` | -| `unwrap-to-spl.ts` | Off-ramp to CEX (light-token → SPL) | `unwrap` | - +| File | Description | Key Function | +| --------------------- | ------------------------------------ | ---------------------------------------------- | +| `send-and-receive.ts` | Send/receive payments | `getOrCreateAtaInterface`, `transferInterface` | +| `get-balance.ts` | Check token balance | `getAtaInterface` | +| `get-history.ts` | Transaction history | `getSignaturesForOwnerInterface` | +| `wrap.ts` | On-ramp from CEX (SPL → light-token) | `wrap` | +| `unwrap.ts` | Off-ramp to CEX (light-token → SPL) | `unwrap` | ## Get Started @@ -38,6 +37,6 @@ light test-validator pnpm run send-and-receive pnpm run get-balance pnpm run get-history -pnpm run wrap-from-spl -pnpm run unwrap-to-spl -``` \ No newline at end of file +pnpm run wrap +pnpm run unwrap +``` diff --git a/toolkits/payments-and-wallets/comparison-spl-light.md b/toolkits/payments-and-wallets/comparison-spl-light.md index a9f450a..ad35418 100644 --- a/toolkits/payments-and-wallets/comparison-spl-light.md +++ b/toolkits/payments-and-wallets/comparison-spl-light.md @@ -6,7 +6,7 @@ ## Quick Reference -| Operation | SPL | Light | +| Operation | SPL | Light | | -------------- | ------------------------------------- | -------------------------------------- | | Get/Create ATA | `getOrCreateAssociatedTokenAccount()` | `getOrCreateAtaInterface()` | | Derive ATA | `getAssociatedTokenAddress()` | `getAssociatedTokenAddressInterface()` | @@ -21,11 +21,11 @@ import { createRpc } from "@lightprotocol/stateless.js"; import { - getOrCreateAtaInterface, - getAtaInterface, - getAssociatedTokenAddressInterface, - transferInterface, - unwrap, + getOrCreateAtaInterface, + getAtaInterface, + getAssociatedTokenAddressInterface, + transferInterface, + unwrap, } from "@lightprotocol/compressed-token/unified"; const rpc = createRpc(RPC_ENDPOINT); @@ -43,10 +43,10 @@ const rpc = createRpc(RPC_ENDPOINT); import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token"; const ata = await getOrCreateAssociatedTokenAccount( - connection, - payer, - mint, - recipient + connection, + payer, + mint, + recipient, ); // Share ata.address with sender @@ -59,19 +59,19 @@ console.log(ata.amount); ```typescript import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountIdempotentInstruction, + getAssociatedTokenAddressSync, + createAssociatedTokenAccountIdempotentInstruction, } from "@solana/spl-token"; const ata = getAssociatedTokenAddressSync(mint, recipient); const tx = new Transaction().add( - createAssociatedTokenAccountIdempotentInstruction( - payer.publicKey, - ata, - recipient, - mint - ) + createAssociatedTokenAccountIdempotentInstruction( + payer.publicKey, + ata, + recipient, + mint, + ), ); ``` @@ -88,29 +88,29 @@ console.log(ata.parsed.amount); ```typescript import { - createAssociatedTokenAccountInterfaceIdempotentInstruction, - createLoadAtaInstructions, - getAssociatedTokenAddressInterface, + createAssociatedTokenAccountInterfaceIdempotentInstruction, + createLoadAtaInstructions, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token/unified"; import { CTOKEN_PROGRAM_ID } from "@lightprotocol/stateless.js"; const ata = getAssociatedTokenAddressInterface(mint, recipient); const tx = new Transaction().add( - createAssociatedTokenAccountInterfaceIdempotentInstruction( - payer.publicKey, - ata, - recipient, - mint, - CTOKEN_PROGRAM_ID - ), - ...(await createLoadAtaInstructions( - rpc, - ata, - recipient, - mint, - payer.publicKey - )) + createAssociatedTokenAccountInterfaceIdempotentInstruction( + payer.publicKey, + ata, + recipient, + mint, + CTOKEN_PROGRAM_ID, + ), + ...(await createLoadAtaInstructions( + rpc, + ata, + recipient, + mint, + payer.publicKey, + )), ); ``` @@ -128,13 +128,13 @@ const sourceAta = getAssociatedTokenAddressSync(mint, owner.publicKey); const destinationAta = getAssociatedTokenAddressSync(mint, recipient); await transfer( - connection, - payer, - sourceAta, - destinationAta, - owner, - amount, - decimals + connection, + payer, + sourceAta, + destinationAta, + owner, + amount, + decimals, ); ``` @@ -144,15 +144,20 @@ await transfer( ```typescript import { - getAssociatedTokenAddressSync, - createTransferInstruction, + getAssociatedTokenAddressSync, + createTransferInstruction, } from "@solana/spl-token"; const sourceAta = getAssociatedTokenAddressSync(mint, owner.publicKey); const destinationAta = getAssociatedTokenAddressSync(mint, recipient); const tx = new Transaction().add( - createTransferInstruction(sourceAta, destinationAta, owner.publicKey, amount) + createTransferInstruction( + sourceAta, + destinationAta, + owner.publicKey, + amount, + ), ); ``` @@ -163,13 +168,13 @@ const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); const destinationAta = getAssociatedTokenAddressInterface(mint, recipient); await transferInterface( - rpc, - payer, - sourceAta, - mint, - destinationAta, - owner, - amount + rpc, + payer, + sourceAta, + mint, + destinationAta, + owner, + amount, ); ``` @@ -177,28 +182,28 @@ await transferInterface( ```typescript import { - createLoadAtaInstructions, - createTransferInterfaceInstruction, - getAssociatedTokenAddressInterface, + createLoadAtaInstructions, + createTransferInterfaceInstruction, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token/unified"; const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); const destinationAta = getAssociatedTokenAddressInterface(mint, recipient); const tx = new Transaction().add( - ...(await createLoadAtaInstructions( - rpc, - sourceAta, - owner.publicKey, - mint, - payer.publicKey - )), - createTransferInterfaceInstruction( - sourceAta, - destinationAta, - owner.publicKey, - amount - ) + ...(await createLoadAtaInstructions( + rpc, + sourceAta, + owner.publicKey, + mint, + payer.publicKey, + )), + createTransferInterfaceInstruction( + sourceAta, + destinationAta, + owner.publicKey, + amount, + ), ); ``` @@ -208,16 +213,16 @@ To ensure your recipient's ATA exists you can prepend an idempotent creation ins ```typescript import { - getAssociatedTokenAddressSync, - createAssociatedTokenAccountIdempotentInstruction, + getAssociatedTokenAddressSync, + createAssociatedTokenAccountIdempotentInstruction, } from "@solana/spl-token"; const destinationAta = getAssociatedTokenAddressSync(mint, recipient); const createAtaIx = createAssociatedTokenAccountIdempotentInstruction( - payer.publicKey, - destinationAta, - recipient, - mint + payer.publicKey, + destinationAta, + recipient, + mint, ); new Transaction().add(createAtaIx, transferIx); @@ -227,18 +232,18 @@ new Transaction().add(createAtaIx, transferIx); ```typescript import { - getAssociatedTokenAddressInterface, - createAssociatedTokenAccountInterfaceIdempotentInstruction, + getAssociatedTokenAddressInterface, + createAssociatedTokenAccountInterfaceIdempotentInstruction, } from "@lightprotocol/compressed-token/unified"; import { CTOKEN_PROGRAM_ID } from "@lightprotocol/stateless.js"; const destinationAta = getAssociatedTokenAddressInterface(mint, recipient); const createAtaIx = createAssociatedTokenAccountInterfaceIdempotentInstruction( - payer.publicKey, - destinationAta, - recipient, - mint, - CTOKEN_PROGRAM_ID + payer.publicKey, + destinationAta, + recipient, + mint, + CTOKEN_PROGRAM_ID, ); new Transaction().add(createAtaIx, transferIx); @@ -319,9 +324,9 @@ await unwrap(rpc, payer, owner, mint, splAta, amount); ```typescript import { getAssociatedTokenAddressSync } from "@solana/spl-token"; import { - createLoadAtaInstructions, - createUnwrapInstruction, - getAssociatedTokenAddressInterface, + createLoadAtaInstructions, + createUnwrapInstruction, + getAssociatedTokenAddressInterface, } from "@lightprotocol/compressed-token/unified"; import { getSplInterfaceInfos } from "@lightprotocol/compressed-token"; @@ -332,20 +337,20 @@ const splInterfaceInfos = await getSplInterfaceInfos(rpc, mint); const splInterfaceInfo = splInterfaceInfos.find((i) => i.isInitialized); const tx = new Transaction().add( - ...(await createLoadAtaInstructions( - rpc, - ctokenAta, - owner.publicKey, - mint, - payer.publicKey - )), - createUnwrapInstruction( - ctokenAta, - splAta, - owner.publicKey, - mint, - amount, - splInterfaceInfo - ) + ...(await createLoadAtaInstructions( + rpc, + ctokenAta, + owner.publicKey, + mint, + payer.publicKey, + )), + createUnwrapInstruction( + ctokenAta, + splAta, + owner.publicKey, + mint, + amount, + splInterfaceInfo, + ), ); ``` diff --git a/toolkits/payments-and-wallets/get-balance.ts b/toolkits/payments-and-wallets/get-balance.ts index 4d223a9..fc7b3a2 100644 --- a/toolkits/payments-and-wallets/get-balance.ts +++ b/toolkits/payments-and-wallets/get-balance.ts @@ -1,53 +1,54 @@ +import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { createMint, mintTo } from "@lightprotocol/compressed-token"; import { - getAtaInterface, - getAssociatedTokenAddressInterface, - getOrCreateAtaInterface, + getAtaInterface, + getAssociatedTokenAddressInterface, + getOrCreateAtaInterface, } from "@lightprotocol/compressed-token/unified"; async function main() { - const rpc = createRpc(); - - // Setup - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // Create test mint and tokens - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - - // Mint tokens (creates cold balance) - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - - // Create ATA and load cold to hot - await getOrCreateAtaInterface(rpc, payer, mint, owner); - - // === GET BALANCE === - const ata = getAssociatedTokenAddressInterface(mint, owner.publicKey); - const account = await getAtaInterface(rpc, ata, owner.publicKey, mint); - - console.log("ATA:", ata.toBase58()); - console.log("Balance:", account.parsed.amount.toString()); - - // Show balance breakdown by source - console.log("\n=== Balance Sources ==="); - for (const source of account._sources ?? []) { - console.log(`${source.type}: ${source.amount.toString()}`); - } + const rpc = createRpc(); + + // Setup + const payer = Keypair.generate(); + const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); + await rpc.confirmTransaction(airdropSig, "confirmed"); + + const owner = Keypair.generate(); + const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig2, "confirmed"); + + // Create test mint and tokens + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const { mint } = await createMint( + rpc, + payer, + mintAuthority.publicKey, + 9, + mintKeypair + ); + + // Mint tokens (creates cold balance) + await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); + + // Create ATA and load cold to hot + await getOrCreateAtaInterface(rpc, payer, mint, owner); + + // === GET BALANCE === + const ata = getAssociatedTokenAddressInterface(mint, owner.publicKey); + const account = await getAtaInterface(rpc, ata, owner.publicKey, mint); + + console.log("ATA:", ata.toBase58()); + console.log("Balance:", account.parsed.amount.toString()); + + // Show balance breakdown by source + console.log("\n=== Balance Sources ==="); + for (const source of account._sources ?? []) { + console.log(`${source.type}: ${source.amount.toString()}`); + } } main().catch(console.error); diff --git a/toolkits/payments-and-wallets/get-history.ts b/toolkits/payments-and-wallets/get-history.ts index 2c11b34..7b15c05 100644 --- a/toolkits/payments-and-wallets/get-history.ts +++ b/toolkits/payments-and-wallets/get-history.ts @@ -1,63 +1,83 @@ +import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; import { createMint, mintTo } from "@lightprotocol/compressed-token"; import { - getOrCreateAtaInterface, - getAssociatedTokenAddressInterface, - transferInterface, + getOrCreateAtaInterface, + getAssociatedTokenAddressInterface, + transferInterface, } from "@lightprotocol/compressed-token/unified"; async function main() { - const rpc = createRpc(); - - // Setup - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - const recipient = Keypair.generate(); - const airdropSig3 = await rpc.requestAirdrop(recipient.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig3, "confirmed"); - - // Create test mint and tokens - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - - // Mint and create ATAs - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - await getOrCreateAtaInterface(rpc, payer, mint, owner); - await getOrCreateAtaInterface(rpc, payer, mint, recipient); - - // Make a transfer to create transaction history - const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - const destAta = getAssociatedTokenAddressInterface(mint, recipient.publicKey); - - await transferInterface(rpc, payer, sourceAta, mint, destAta, owner, bn(100)); - await transferInterface(rpc, payer, sourceAta, mint, destAta, owner, bn(200)); - - // === GET TRANSACTION HISTORY === - const result = await rpc.getSignaturesForOwnerInterface(owner.publicKey); - - console.log("=== Transaction History ==="); - console.log("Total signatures:", result.signatures.length); - console.log("On-chain txs:", result.solana.length); - console.log("Compressed txs:", result.compressed.length); - - console.log("\n=== Recent Transactions ==="); - for (const sig of result.signatures.slice(0, 5)) { - console.log(sig.signature); - } + const rpc = createRpc(); + + // Setup + const payer = Keypair.generate(); + const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); + await rpc.confirmTransaction(airdropSig, "confirmed"); + + const owner = Keypair.generate(); + const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig2, "confirmed"); + + const recipient = Keypair.generate(); + const airdropSig3 = await rpc.requestAirdrop(recipient.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig3, "confirmed"); + + // Create test mint and tokens + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const { mint } = await createMint( + rpc, + payer, + mintAuthority.publicKey, + 9, + mintKeypair + ); + + // Mint and create ATAs + await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); + await getOrCreateAtaInterface(rpc, payer, mint, owner); + await getOrCreateAtaInterface(rpc, payer, mint, recipient); + + // Make a transfer to create transaction history + const sourceAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + const destAta = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey + ); + + await transferInterface( + rpc, + payer, + sourceAta, + mint, + destAta, + owner, + bn(100) + ); + await transferInterface( + rpc, + payer, + sourceAta, + mint, + destAta, + owner, + bn(200) + ); + + // === GET TRANSACTION HISTORY === + const result = await rpc.getSignaturesForOwnerInterface(owner.publicKey); + + console.log("=== Transaction History ==="); + console.log("Total signatures:", result.signatures.length); + console.log("On-chain txs:", result.solana.length); + console.log("Compressed txs:", result.compressed.length); + + console.log("\n=== Recent Transactions ==="); + for (const sig of result.signatures.slice(0, 5)) { + console.log(sig.signature); + } } main().catch(console.error); diff --git a/toolkits/payments-and-wallets/package.json b/toolkits/payments-and-wallets/package.json index 21a46dd..1d4c25f 100644 --- a/toolkits/payments-and-wallets/package.json +++ b/toolkits/payments-and-wallets/package.json @@ -1,23 +1,24 @@ { - "name": "light-token-toolkit-payments", - "version": "1.0.0", - "description": "Light Token Payments Toolkit Examples", - "type": "module", - "scripts": { - "send-and-receive": "node --loader ts-node/esm send-and-receive.ts", - "get-balance": "node --loader ts-node/esm get-balance.ts", - "get-history": "node --loader ts-node/esm get-history.ts", - "wrap-from-spl": "node --loader ts-node/esm wrap-from-spl.ts", - "unwrap-to-spl": "node --loader ts-node/esm unwrap-to-spl.ts" - }, - "dependencies": { - "@lightprotocol/compressed-token": "file:../../light-protocol/js/compressed-token", - "@lightprotocol/stateless.js": "file:../../light-protocol/js/stateless.js", - "@solana/spl-token": "^0.4.13", - "@solana/web3.js": "1.98.4" - }, - "devDependencies": { - "ts-node": "^10.9.2", - "typescript": "^5.7.2" - } + "name": "light-token-toolkit-payments", + "version": "1.0.0", + "description": "Light Token Payments Toolkit Examples", + "type": "module", + "scripts": { + "send-and-receive": "ts-node-esm send-and-receive.ts", + "get-balance": "ts-node-esm get-balance.ts", + "get-history": "ts-node-esm get-history.ts", + "wrap": "ts-node-esm wrap.ts", + "unwrap": "ts-node-esm unwrap.ts" + }, + "dependencies": { + "@lightprotocol/compressed-token": "^0.22.1-alpha", + "@lightprotocol/stateless.js": "^0.22.1-alpha", + "@solana/spl-token": "^0.4.13", + "@solana/web3.js": "1.98.4", + "dotenv": "^16.5.0" + }, + "devDependencies": { + "ts-node": "^10.9.2", + "typescript": "^5.7.2" + } } diff --git a/toolkits/payments-and-wallets/send-and-receive.ts b/toolkits/payments-and-wallets/send-and-receive.ts index 8392a41..0b2123a 100644 --- a/toolkits/payments-and-wallets/send-and-receive.ts +++ b/toolkits/payments-and-wallets/send-and-receive.ts @@ -1,95 +1,112 @@ +import "dotenv/config"; import { Keypair } from "@solana/web3.js"; import { createRpc, bn } from "@lightprotocol/stateless.js"; +import { createMint, mintTo } from "@lightprotocol/compressed-token"; import { - createMint, - mintTo, -} from "@lightprotocol/compressed-token"; -import { - getOrCreateAtaInterface, - getAssociatedTokenAddressInterface, - transferInterface, + getOrCreateAtaInterface, + getAssociatedTokenAddressInterface, + transferInterface, } from "@lightprotocol/compressed-token/unified"; async function main() { - const rpc = createRpc(); + const rpc = createRpc(); - // Setup wallets - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); + // Setup wallets + const payer = Keypair.generate(); + const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); + await rpc.confirmTransaction(airdropSig, "confirmed"); - const sender = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(sender.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); + const sender = Keypair.generate(); + const airdropSig2 = await rpc.requestAirdrop(sender.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig2, "confirmed"); - const recipient = Keypair.generate(); - const airdropSig3 = await rpc.requestAirdrop(recipient.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig3, "confirmed"); + const recipient = Keypair.generate(); + const airdropSig3 = await rpc.requestAirdrop(recipient.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig3, "confirmed"); - console.log("Sender:", sender.publicKey.toBase58()); - console.log("Recipient:", recipient.publicKey.toBase58()); + console.log("Sender:", sender.publicKey.toBase58()); + console.log("Recipient:", recipient.publicKey.toBase58()); - // Create a test mint (in production, use existing stablecoin mint) - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); + // Create a test mint (in production, use existing stablecoin mint) + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const { mint } = await createMint( + rpc, + payer, + mintAuthority.publicKey, + 9, + mintKeypair, + ); + console.log("Mint:", mint.toBase58()); - // Mint tokens to sender - await mintTo(rpc, payer, mint, sender.publicKey, mintAuthority, bn(1000)); - console.log("Minted 1000 tokens to sender"); + // Mint tokens to sender + await mintTo(rpc, payer, mint, sender.publicKey, mintAuthority, bn(1000)); + console.log("Minted 1000 tokens to sender"); - // === RECEIVE: Get/create sender's ATA === - // This creates the ATA and loads any cold balance to hot - const senderAccount = await getOrCreateAtaInterface( - rpc, - payer, - mint, - sender // Signer enables auto-load - ); - console.log("\nSender ATA:", senderAccount.parsed.address.toBase58()); - console.log("Sender balance:", senderAccount.parsed.amount.toString()); + // === RECEIVE: Get/create sender's ATA === + // This creates the ATA and loads any cold balance to hot + const senderAccount = await getOrCreateAtaInterface( + rpc, + payer, + mint, + sender, // Signer enables auto-load + ); + console.log("\nSender ATA:", senderAccount.parsed.address.toBase58()); + console.log("Sender balance:", senderAccount.parsed.amount.toString()); - // === RECEIVE: Get/create recipient's ATA === - const recipientAccount = await getOrCreateAtaInterface( - rpc, - payer, - mint, - recipient - ); - console.log("\nRecipient ATA:", recipientAccount.parsed.address.toBase58()); - console.log("Recipient balance before:", recipientAccount.parsed.amount.toString()); + // === RECEIVE: Get/create recipient's ATA === + const recipientAccount = await getOrCreateAtaInterface( + rpc, + payer, + mint, + recipient, + ); + console.log("\nRecipient ATA:", recipientAccount.parsed.address.toBase58()); + console.log( + "Recipient balance before:", + recipientAccount.parsed.amount.toString(), + ); - // === SEND: Transfer tokens === - const sourceAta = getAssociatedTokenAddressInterface(mint, sender.publicKey); - const destinationAta = getAssociatedTokenAddressInterface(mint, recipient.publicKey); + // === SEND: Transfer tokens === + const sourceAta = getAssociatedTokenAddressInterface( + mint, + sender.publicKey, + ); + const destinationAta = getAssociatedTokenAddressInterface( + mint, + recipient.publicKey, + ); - const signature = await transferInterface( - rpc, - payer, - sourceAta, - mint, - destinationAta, - sender, - bn(500) - ); + const signature = await transferInterface( + rpc, + payer, + sourceAta, + mint, + destinationAta, + sender, + bn(500), + ); - console.log("\nTransferred 500 tokens"); - console.log("Transaction:", signature); + console.log("\nTransferred 500 tokens"); + console.log("Transaction:", signature); - // Check final balances - const senderFinal = await getOrCreateAtaInterface(rpc, payer, mint, sender.publicKey); - const recipientFinal = await getOrCreateAtaInterface(rpc, payer, mint, recipient.publicKey); + // Check final balances + const senderFinal = await getOrCreateAtaInterface( + rpc, + payer, + mint, + sender.publicKey, + ); + const recipientFinal = await getOrCreateAtaInterface( + rpc, + payer, + mint, + recipient.publicKey, + ); - console.log("\n=== Final Balances ==="); - console.log("Sender:", senderFinal.parsed.amount.toString()); - console.log("Recipient:", recipientFinal.parsed.amount.toString()); + console.log("\n=== Final Balances ==="); + console.log("Sender:", senderFinal.parsed.amount.toString()); + console.log("Recipient:", recipientFinal.parsed.amount.toString()); } main().catch(console.error); diff --git a/toolkits/payments-and-wallets/tsconfig.json b/toolkits/payments-and-wallets/tsconfig.json index c2c463d..de8ab73 100644 --- a/toolkits/payments-and-wallets/tsconfig.json +++ b/toolkits/payments-and-wallets/tsconfig.json @@ -1,15 +1,15 @@ { - "compilerOptions": { - "target": "ES2020", - "module": "Node16", - "moduleResolution": "Node16", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "outDir": "./dist" - }, - "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] + "compilerOptions": { + "target": "ES2020", + "module": "Node16", + "moduleResolution": "Node16", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "outDir": "./dist" + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] } diff --git a/toolkits/payments-and-wallets/unwrap-to-spl.ts b/toolkits/payments-and-wallets/unwrap-to-spl.ts deleted file mode 100644 index 9fb1f40..0000000 --- a/toolkits/payments-and-wallets/unwrap-to-spl.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { createMint, mintTo } from "@lightprotocol/compressed-token"; -import { - getOrCreateAtaInterface, - unwrap, -} from "@lightprotocol/compressed-token/unified"; -import { - createAssociatedTokenAccount, - getAccount, -} from "@solana/spl-token"; - -async function main() { - const rpc = createRpc(); - - // Setup - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // Create test mint - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); - - // Mint and load to c-token ATA - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - const ctokenAccount = await getOrCreateAtaInterface(rpc, payer, mint, owner); - console.log("C-token ATA:", ctokenAccount.parsed.address.toBase58()); - console.log("C-token balance:", ctokenAccount.parsed.amount.toString()); - - // Create destination SPL ATA (must exist before unwrap) - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("\nSPL ATA:", splAta.toBase58()); - - // === UNWRAP: c-token → SPL === - // Off-ramp for CEX withdrawal - const signature = await unwrap( - rpc, - payer, - splAta, - owner, - mint, - bn(500) - ); - - console.log("\n=== Unwrapped 500 tokens ==="); - console.log("Transaction:", signature); - - // Check SPL balance - const splBalance = await getAccount(rpc, splAta); - console.log("\nSPL balance (ready for CEX):", splBalance.amount.toString()); -} - -main().catch(console.error); diff --git a/toolkits/payments-and-wallets/unwrap.ts b/toolkits/payments-and-wallets/unwrap.ts new file mode 100644 index 0000000..e1026a0 --- /dev/null +++ b/toolkits/payments-and-wallets/unwrap.ts @@ -0,0 +1,67 @@ +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc, bn } from "@lightprotocol/stateless.js"; +import { createMint, mintTo } from "@lightprotocol/compressed-token"; +import { + getOrCreateAtaInterface, + unwrap, +} from "@lightprotocol/compressed-token/unified"; +import { createAssociatedTokenAccount, getAccount } from "@solana/spl-token"; + +async function main() { + const rpc = createRpc(); + + // Setup + const payer = Keypair.generate(); + const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); + await rpc.confirmTransaction(airdropSig, "confirmed"); + + const owner = Keypair.generate(); + const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig2, "confirmed"); + + // Create test mint + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const { mint } = await createMint( + rpc, + payer, + mintAuthority.publicKey, + 9, + mintKeypair, + ); + console.log("Mint:", mint.toBase58()); + + // Mint and load to c-token ATA + await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); + const ctokenAccount = await getOrCreateAtaInterface( + rpc, + payer, + mint, + owner, + ); + console.log("C-token ATA:", ctokenAccount.parsed.address.toBase58()); + console.log("C-token balance:", ctokenAccount.parsed.amount.toString()); + + // Create destination SPL ATA (must exist before unwrap) + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + owner.publicKey, + ); + console.log("\nSPL ATA:", splAta.toBase58()); + + // === UNWRAP: c-token → SPL === + // Off-ramp for CEX withdrawal + const signature = await unwrap(rpc, payer, splAta, owner, mint, bn(500)); + + console.log("\n=== Unwrapped 500 tokens ==="); + console.log("Transaction:", signature); + + // Check SPL balance + const splBalance = await getAccount(rpc, splAta); + console.log("\nSPL balance (ready for CEX):", splBalance.amount.toString()); +} + +main().catch(console.error); diff --git a/toolkits/payments-and-wallets/wrap-from-spl.ts b/toolkits/payments-and-wallets/wrap-from-spl.ts deleted file mode 100644 index 6830ecc..0000000 --- a/toolkits/payments-and-wallets/wrap-from-spl.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Keypair } from "@solana/web3.js"; -import { createRpc, bn } from "@lightprotocol/stateless.js"; -import { - createMint, - mintTo, - decompress, - wrap, - getAssociatedTokenAddressInterface, - createAtaInterfaceIdempotent, -} from "@lightprotocol/compressed-token"; -import { createAssociatedTokenAccount, getAccount } from "@solana/spl-token"; - -async function main() { - const rpc = createRpc(); - - // Setup - const payer = Keypair.generate(); - const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); - await rpc.confirmTransaction(airdropSig, "confirmed"); - - const owner = Keypair.generate(); - const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); - await rpc.confirmTransaction(airdropSig2, "confirmed"); - - // Create test mint - const mintAuthority = Keypair.generate(); - const mintKeypair = Keypair.generate(); - const { mint } = await createMint( - rpc, - payer, - mintAuthority.publicKey, - 9, - mintKeypair - ); - console.log("Mint:", mint.toBase58()); - - // Create SPL ATA and fund it - // (Simulates receiving tokens from CEX) - const splAta = await createAssociatedTokenAccount( - rpc, - payer, - mint, - owner.publicKey - ); - console.log("SPL ATA:", splAta.toBase58()); - - // Mint compressed, then decompress to SPL (simulates CEX deposit) - await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); - await decompress(rpc, payer, mint, bn(1000), owner, splAta); - - const splBalanceBefore = await getAccount(rpc, splAta); - console.log("SPL balance before wrap:", splBalanceBefore.amount.toString()); - - // Create c-token ATA (destination) - const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); - await createAtaInterfaceIdempotent(rpc, payer, mint, owner.publicKey); - console.log("C-token ATA:", ctokenAta.toBase58()); - - // === WRAP: SPL → c-token === - // On-ramp from CEX - const signature = await wrap( - rpc, - payer, - splAta, - ctokenAta, - owner, - mint, - bn(500) - ); - - console.log("\n=== Wrapped 500 tokens ==="); - console.log("Transaction:", signature); - - // Check balances after - const splBalanceAfter = await getAccount(rpc, splAta); - console.log("\nSPL balance after:", splBalanceAfter.amount.toString()); - console.log("C-token ATA now has 500 tokens ready for payments"); -} - -main().catch(console.error); diff --git a/toolkits/payments-and-wallets/wrap.ts b/toolkits/payments-and-wallets/wrap.ts new file mode 100644 index 0000000..fd9f090 --- /dev/null +++ b/toolkits/payments-and-wallets/wrap.ts @@ -0,0 +1,81 @@ +import "dotenv/config"; +import { Keypair } from "@solana/web3.js"; +import { createRpc, bn } from "@lightprotocol/stateless.js"; +import { + createMint, + mintTo, + decompress, + wrap, + getAssociatedTokenAddressInterface, + createAtaInterfaceIdempotent, +} from "@lightprotocol/compressed-token"; +import { createAssociatedTokenAccount, getAccount } from "@solana/spl-token"; + +async function main() { + const rpc = createRpc(); + + // Setup + const payer = Keypair.generate(); + const airdropSig = await rpc.requestAirdrop(payer.publicKey, 10e9); + await rpc.confirmTransaction(airdropSig, "confirmed"); + + const owner = Keypair.generate(); + const airdropSig2 = await rpc.requestAirdrop(owner.publicKey, 1e9); + await rpc.confirmTransaction(airdropSig2, "confirmed"); + + // Create test mint + const mintAuthority = Keypair.generate(); + const mintKeypair = Keypair.generate(); + const { mint } = await createMint( + rpc, + payer, + mintAuthority.publicKey, + 9, + mintKeypair, + ); + console.log("Mint:", mint.toBase58()); + + // Create SPL ATA and fund it + // (Simulates receiving tokens from CEX) + const splAta = await createAssociatedTokenAccount( + rpc, + payer, + mint, + owner.publicKey, + ); + console.log("SPL ATA:", splAta.toBase58()); + + // Mint compressed, then decompress to SPL (simulates CEX deposit) + await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000)); + await decompress(rpc, payer, mint, bn(1000), owner, splAta); + + const splBalanceBefore = await getAccount(rpc, splAta); + console.log("SPL balance before wrap:", splBalanceBefore.amount.toString()); + + // Create c-token ATA (destination) + const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey); + await createAtaInterfaceIdempotent(rpc, payer, mint, owner.publicKey); + console.log("C-token ATA:", ctokenAta.toBase58()); + + // === WRAP: SPL → c-token === + // On-ramp from CEX + const signature = await wrap( + rpc, + payer, + splAta, + ctokenAta, + owner, + mint, + bn(500), + ); + + console.log("\n=== Wrapped 500 tokens ==="); + console.log("Transaction:", signature); + + // Check balances after + const splBalanceAfter = await getAccount(rpc, splAta); + console.log("\nSPL balance after:", splBalanceAfter.amount.toString()); + console.log("C-token ATA now has 500 tokens ready for payments"); +} + +main().catch(console.error); diff --git a/toolkits/streaming-tokens/README.mdx b/toolkits/streaming-tokens/README.mdx index 7166adc..5d2bbdc 100644 --- a/toolkits/streaming-tokens/README.mdx +++ b/toolkits/streaming-tokens/README.mdx @@ -18,7 +18,7 @@ const LIGHT_SYSTEM_PROGRAM: &str = "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7" #[tokio::main] async fn main() -> anyhow::Result<()> { let config = LaserstreamConfig { - api_key: std::env::var("HELIUS_API_KEY")?, + api_key: std::env::var("API_KEY")?, endpoint: "https://laserstream-mainnet-ewr.helius-rpc.com".to_string(), }; diff --git a/toolkits/streaming-tokens/src/main.rs b/toolkits/streaming-tokens/src/main.rs index 346df8c..c696f84 100644 --- a/toolkits/streaming-tokens/src/main.rs +++ b/toolkits/streaming-tokens/src/main.rs @@ -7,7 +7,7 @@ const CTOKEN_PROGRAM_ID: &str = "cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"; async fn main() -> anyhow::Result<()> { dotenvy::dotenv().ok(); - let api_key = std::env::var("HELIUS_API_KEY")?; + let api_key = std::env::var("API_KEY")?; let endpoint = "https://laserstream-devnet-ewr.helius-rpc.com".to_string(); let config = LaserstreamConfig::new(endpoint, api_key);