diff --git a/docs/content/docs/token-wrap.mdx b/docs/content/docs/token-wrap.mdx index e80584a..5ee90cd 100644 --- a/docs/content/docs/token-wrap.mdx +++ b/docs/content/docs/token-wrap.mdx @@ -31,7 +31,7 @@ address of the original *unwrapped* token mint. This allows anyone to easily det corresponding to a wrapped token, facilitating unwrapping. 2. **`Wrap`:** This operation accepts deposits of unwrapped tokens and mints wrapped tokens. - * Unwrapped tokens are transferred from the user's account to an escrow account owned by the wrapped mint authority (different for every mint). Any unwrapped token account whose owner is a PDA controlled by the Token Wrap program can be used. + * Unwrapped tokens are transferred from the user's account to a specific escrow Associated Token Account (ATA). This ATA is for the unwrapped mint, and its authority is a Program Derived Address (PDA) controlled by the Token Wrap program (unique for each wrapped mint). * An equivalent amount of wrapped tokens is minted to the user's wrapped token account. 3. **`Unwrap`:** This operation burns wrapped tokens and releases unwrapped token deposits. @@ -136,15 +136,20 @@ To create a new wrapped token mint, first you need to identify the unwrapped tok ```typescript import { address, + appendTransactionMessageInstructions, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, + createTransactionMessage, getSignatureFromTransaction, + pipe, sendAndConfirmTransactionFactory, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; - import { createMintTx } from '@solana-program/token-wrap'; + import { createMint } from '@solana-program/token-wrap'; // Replace these consts with your own const PRIVATE_KEY_PAIR = new Uint8Array([242, 30, 38, 177, 152, 71, ... ]); @@ -158,28 +163,32 @@ To create a new wrapped token mint, first you need to identify the unwrapped tok const payer = await createKeyPairSignerFromBytes(PRIVATE_KEY_PAIR); const { value: blockhash } = await rpc.getLatestBlockhash().send(); - const createMintMessage = await createMintTx({ + const createMintHelper = await createMint({ rpc, - blockhash, unwrappedMint: UNWRAPPED_MINT_ADDRESS, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, payer, idempotent: true, }); - const signedCreateMintTx = await signTransactionMessageWithSigners(createMintMessage.tx); - await sendAndConfirm(signedCreateMintTx, { commitment: 'confirmed' }); - const createMintSignature = getSignatureFromTransaction(signedCreateMintTx); + const createMintTx = await pipe( + createTransactionMessage({ version: 0 }), + tx => setTransactionMessageFeePayerSigner(payer, tx), + tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), + tx => appendTransactionMessageInstructions(createMintHelper.ixs, tx), + tx => signTransactionMessageWithSigners(tx), + ); + await sendAndConfirm(createMintTx, { commitment: 'confirmed' }); + const createMintSignature = getSignatureFromTransaction(createMintTx); console.log('======== Create Mint Successful ========'); - console.log('Wrapped Mint:', createMintMessage.wrappedMint); - console.log('Backpointer:', createMintMessage.backpointer); - console.log('Funded wrapped mint lamports:', createMintMessage.fundedWrappedMintLamports); - console.log('Funded backpointer lamports:', createMintMessage.fundedBackpointerLamports); + console.log('Wrapped Mint:', createMintHelper.wrappedMint); + console.log('Backpointer:', createMintHelper.backpointer); + console.log('Funded wrapped mint lamports:', createMintHelper.fundedWrappedMintLamports); + console.log('Funded backpointer lamports:', createMintHelper.fundedBackpointerLamports); console.log('Signature:', createMintSignature); } void main(); - ``` @@ -199,6 +208,7 @@ To interact with wrapped tokens, you need to know the PDAs (Program Derived Addr Wrapped mint address: B8HbxGU4npjgjMX5xJFR2FYkgvAHdZqyVb8MyFvdsuNM Wrapped mint authority: 8WdYPmtq8c6ZfmHMZUwCQL2E8qVHEV8rG9MXkyax3joR Wrapped backpointer address: CNjr898vsBdzWxrJApMSAQac4A7o7qLRcSseTb56X7C9 + Unwrapped escrow address: QrzXtFZedQmg8AGu6AnUkPgmsLnR9ErsjNRLdCrRVWw ``` @@ -210,7 +220,8 @@ To interact with wrapped tokens, you need to know the PDAs (Program Derived Addr findWrappedMintAuthorityPda, findWrappedMintPda, } from '@solana-program/token-wrap'; - import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; + import { findAssociatedTokenPda, TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; + import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token'; const UNWRAPPED_MINT_ADDRESS = address('5StBUZ2w8ShDN9iF7NkGpDNNH2wv9jK7zhArmVRpwrCt'); @@ -222,9 +233,16 @@ To interact with wrapped tokens, you need to know the PDAs (Program Derived Addr const [backpointer] = await findBackpointerPda({ wrappedMint }); const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({ wrappedMint }); + const [escrowAccount] = await findAssociatedTokenPda({ + owner: wrappedMintAuthority, + mint: UNWRAPPED_MINT_ADDRESS, + tokenProgram: TOKEN_PROGRAM_ADDRESS, // unwrapped token program address + }); + console.log('WRAPPED_MINT_ADDRESS', wrappedMint); console.log('BACKPOINTER', backpointer); console.log('WRAPPED_MINT_AUTHORITY', wrappedMintAuthority); + console.log('ESCROW_ACCOUNT', escrowAccount); } void main(); @@ -234,7 +252,8 @@ To interact with wrapped tokens, you need to know the PDAs (Program Derived Addr ### Create escrow account -Before wrapping tokens, you need to create an account to hold the unwrapped tokens. The escrow account's owner must be the correct PDA (see `find-pdas` command above). There is also a helper to create this account: +Before wrapping tokens, if you are the first to do so for this wrapped mint, you may need to initialize the escrow account to custody the unwrapped tokens. +The account must be an ATA whose owner is the mint authority PDA (see `find-pdas` command above). There is also a helper to initialize this account: @@ -256,18 +275,24 @@ Before wrapping tokens, you need to create an account to hold the unwrapped toke ```typescript import { address, + appendTransactionMessageInstructions, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, + createTransactionMessage, + getSignatureFromTransaction, + pipe, sendAndConfirmTransactionFactory, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; - import { createEscrowAccountTx } from '@solana-program/token-wrap'; + import { createEscrowAccount } from '@solana-program/token-wrap'; // Replace these consts with your own const PRIVATE_KEY_PAIR = new Uint8Array([242, 30, 38, 177, 152, 71, ... ]); - const UNWRAPPED_MINT_ADDRESS = address('Cp3dvsXSiWJiA5AyfNdDdJ1Drw91yWdQwx5nnmcHKVi6'); + const UNWRAPPED_MINT_ADDRESS = address('FAbYm8kdDsyc6csvTXPMBwCJDjTVkZcvrnyVVTSF74hU'); async function main() { const rpc = createSolanaRpc('http://127.0.0.1:8899'); @@ -277,22 +302,34 @@ Before wrapping tokens, you need to create an account to hold the unwrapped toke const payer = await createKeyPairSignerFromBytes(PRIVATE_KEY_PAIR); const { value: blockhash } = await rpc.getLatestBlockhash().send(); - const createEscrowMessage = await createEscrowAccountTx({ + // Create escrow account that with hold unwrapped tokens + const createEscrowHelper = await createEscrowAccount({ rpc, - blockhash, payer, unwrappedMint: UNWRAPPED_MINT_ADDRESS, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, }); - const signedCreateEscrowTx = await signTransactionMessageWithSigners(createEscrowMessage.tx); - await sendAndConfirm(signedCreateEscrowTx, { commitment: 'confirmed' }); - - console.log('ESCROW_ADDRESS', createEscrowMessage.keyPair.address); + if (createEscrowHelper.kind === 'instructions_to_create') { + const createEscrowTx = await pipe( + createTransactionMessage({ version: 0 }), + tx => setTransactionMessageFeePayerSigner(payer, tx), + tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), + tx => appendTransactionMessageInstructions(createEscrowHelper.ixs, tx), + tx => signTransactionMessageWithSigners(tx), + ); + await sendAndConfirm(createEscrowTx, { commitment: 'confirmed' }); + const createEscrowSignature = getSignatureFromTransaction(createEscrowTx); + + console.log('======== Create Escrow Successful ========'); + console.log('Escrow address:', createEscrowHelper.address); + console.log('Signature:', createEscrowSignature); + } else { + console.log('======== Escrow already exists, skipping creation ========'); + } } void main(); ``` - @@ -304,10 +341,9 @@ Escrows unwrapped tokens and mints wrapped tokens to recipient account. ```console $ UNWRAPPED_TOKEN_ACCOUNT=DKFjYKEFS4tkXjamwkuiGf555Lww3eRSWwNTbue9x14 - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $WRAPPED_TOKEN_PROGRAM 100 + $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $WRAPPED_TOKEN_PROGRAM 100 Wrapping 100 tokens from mint BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e Unwrapped mint address: BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e @@ -323,7 +359,7 @@ Escrows unwrapped tokens and mints wrapped tokens to recipient account. You can specify a recipient token account with the `--recipient-token-account` option. If not provided, the associated token account of the fee payer will be used or created if it doesn't exist. ```console - $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $WRAPPED_TOKEN_PROGRAM 100 \ + $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $WRAPPED_TOKEN_PROGRAM 100 \ --recipient-token-account $RECIPIENT_WRAPPED_TOKEN_ACCOUNT ``` @@ -332,21 +368,26 @@ Escrows unwrapped tokens and mints wrapped tokens to recipient account. ```typescript import { address, + appendTransactionMessageInstructions, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, + createTransactionMessage, getSignatureFromTransaction, + pipe, sendAndConfirmTransactionFactory, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; - import { singleSignerWrapTx } from '@solana-program/token-wrap'; + import { singleSignerWrap } from '@solana-program/token-wrap'; // Replace these consts with your own const PRIVATE_KEY_PAIR = new Uint8Array([242, 30, 38, 177, 152, 71, ... ]); - const ESCROW_ACCOUNT = address('4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3'); - const UNWRAPPED_TOKEN_ACCOUNT = address('CbuRmvG3frMoPFnsKfC2t8jTUHFjtnrKZBt2aqdqH4PG'); - const RECIPIENT = address('HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt'); + const UNWRAPPED_MINT_ADDRESS = address('FAbYm8kdDsyc6csvTXPMBwCJDjTVkZcvrnyVVTSF74hU'); + const UNWRAPPED_TOKEN_ACCOUNT = address('4dSPDdFuTbKTuJDDtTd8SUdbH6QY42hpTPRi6RRzzsPF'); + const RECIPIENT_WRAPPED_TOKEN_ACCOUNT = address('BJYNChMx8tfc3wjUmSXWYJDe55QsgHUXFfL9PbgV6wa9'); const AMOUNT_TO_WRAP = 100n; async function main() { @@ -357,25 +398,31 @@ Escrows unwrapped tokens and mints wrapped tokens to recipient account. const payer = await createKeyPairSignerFromBytes(PRIVATE_KEY_PAIR); const { value: blockhash } = await rpc.getLatestBlockhash().send(); - const wrapMessage = await singleSignerWrapTx({ + // Execute wrap + const wrapHelper = await singleSignerWrap({ rpc, - blockhash, payer, unwrappedTokenAccount: UNWRAPPED_TOKEN_ACCOUNT, - escrowAccount: ESCROW_ACCOUNT, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, - recipientWrappedTokenAccount: RECIPIENT, amount: AMOUNT_TO_WRAP, + unwrappedMint: UNWRAPPED_MINT_ADDRESS, + recipientWrappedTokenAccount: RECIPIENT_WRAPPED_TOKEN_ACCOUNT, }); - const signedWrapTx = await signTransactionMessageWithSigners(wrapMessage.tx); - await sendAndConfirm(signedWrapTx, { commitment: 'confirmed' }); - const wrapSignature = getSignatureFromTransaction(signedWrapTx); + const wrapTx = await pipe( + createTransactionMessage({ version: 0 }), + tx => setTransactionMessageFeePayerSigner(payer, tx), + tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), + tx => appendTransactionMessageInstructions(wrapHelper.ixs, tx), + tx => signTransactionMessageWithSigners(tx), + ); + await sendAndConfirm(wrapTx, { commitment: 'confirmed' }); + const wrapSignature = getSignatureFromTransaction(wrapTx); console.log('======== Wrap Successful ========'); - console.log('Wrap amount:', wrapMessage.amount); - console.log('Recipient account:', wrapMessage.recipientWrappedTokenAccount); - console.log('Escrow Account:', wrapMessage.escrowAccount); + console.log('Wrap amount:', wrapHelper.amount); + console.log('Recipient account:', wrapHelper.recipientWrappedTokenAccount); + console.log('Escrow Account:', wrapHelper.escrowAccount); console.log('Signature:', wrapSignature); } @@ -416,11 +463,10 @@ An example wrapping tokens whose origin is a token account owned by an SPL Token $ MULTISIG_ADDRESS=mgnqjedikMKaRtS5wrhVttuA12JaPXiqY619Gfef5eh $ RECIPIENT_ACCOUNT=HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt $ UNWRAPPED_MINT=BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ UNWRAPPED_TOKEN_PROGRAM=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $WRAPPED_TOKEN_PROGRAM 23 \ + $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $WRAPPED_TOKEN_PROGRAM 23 \ --transfer-authority $MULTISIG_ADDRESS \ --recipient-token-account $RECIPIENT_ACCOUNT \ --unwrapped-mint $UNWRAPPED_MINT \ @@ -451,11 +497,10 @@ An example wrapping tokens whose origin is a token account owned by an SPL Token $ MULTISIG_ADDRESS=mgnqjedikMKaRtS5wrhVttuA12JaPXiqY619Gfef5eh $ RECIPIENT_ACCOUNT=HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt $ UNWRAPPED_MINT=BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ UNWRAPPED_TOKEN_PROGRAM=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $WRAPPED_TOKEN_PROGRAM 23 \ + $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $WRAPPED_TOKEN_PROGRAM 23 \ --transfer-authority $MULTISIG_ADDRESS \ --recipient-token-account $RECIPIENT_ACCOUNT \ --unwrapped-mint $UNWRAPPED_MINT \ @@ -487,11 +532,10 @@ An example wrapping tokens whose origin is a token account owned by an SPL Token $ MULTISIG_ADDRESS=mgnqjedikMKaRtS5wrhVttuA12JaPXiqY619Gfef5eh $ RECIPIENT_ACCOUNT=HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt $ UNWRAPPED_MINT=BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ UNWRAPPED_TOKEN_PROGRAM=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $WRAPPED_TOKEN_PROGRAM 23 \ + $ spl-token-wrap wrap $UNWRAPPED_TOKEN_ACCOUNT $WRAPPED_TOKEN_PROGRAM 23 \ --transfer-authority $MULTISIG_ADDRESS \ --recipient-token-account $RECIPIENT_ACCOUNT \ --unwrapped-mint $UNWRAPPED_MINT \ @@ -533,21 +577,23 @@ An example wrapping tokens whose origin is a token account owned by an SPL Token } from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; import { - findWrappedMintAuthorityPda, - multisigOfflineSignWrapTx, combinedMultisigTx, + findWrappedMintAuthorityPda, + findWrappedMintPda, + multisigOfflineSignWrap, } from '@solana-program/token-wrap'; + import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token'; // Replace these consts with your own const PAYER_KEYPAIR_BYTES = new Uint8Array([242, 30, 38, 177, 152, 71, ... ]); + + // Create using CLI: spl-token create-multisig 2 $SIGNER_1_PUBKEY $SIGNER_2_PUBKEY const MULTISIG_SPL_TOKEN = address('2XBevFsu4pnZpB9PewYKAJHNyx9dFQf3MaiGBszF5fm8'); const SIGNER_A_KEYPAIR_BYTES = new Uint8Array([210, 190, 232, 169, 113, 107, ... ]); const SIGNER_B_KEYPAIR_BYTES = new Uint8Array([37, 161, 191, 225, 59, 192, ... ]); - const WRAPPED_MINT_ADDRESS = address('B8HbxGU4npjgjMX5xJFR2FYkgvAHdZqyVb8MyFvdsuNM'); - const UNWRAPPED_MINT_ADDRESS = address('E8r9ixwg7QYr6xCh4tSdHErZ6CUxQhVGHqF5bRoZXyyV'); - const UNWRAPPED_TOKEN_ACCOUNT = address('DGNyuKAWP3susy6XMbVsYHy2AMrrKmh8pXM3WpQUeyL2'); // Must be owned by multisig account - const UNWRAPPED_TOKEN_PROGRAM = address('TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'); - const ESCROW = address('4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3'); + + const UNWRAPPED_MINT_ADDRESS = address('F2qGWupzMUQnGfX8e25XZps8d9AGdVde8hLQT2pxsb4M'); + const UNWRAPPED_TOKEN_ACCOUNT = address('94Y9pxekEm59b67PQQwvjb7wbwz689wDZ3dAwhCtJpPS'); // Must be owned by MULTISIG_SPL_TOKEN const RECIPIENT = address('HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt'); const AMOUNT_TO_WRAP = 100n; @@ -559,59 +605,60 @@ An example wrapping tokens whose origin is a token account owned by an SPL Token const payer = await createKeyPairSignerFromBytes(PAYER_KEYPAIR_BYTES); const { value: blockhash } = await rpc.getLatestBlockhash().send(); - const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({ wrappedMint: WRAPPED_MINT_ADDRESS }); + const [wrappedMint] = await findWrappedMintPda({ + unwrappedMint: UNWRAPPED_MINT_ADDRESS, + wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, + }); + const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({ wrappedMint }); const signerA = await createKeyPairSignerFromBytes(SIGNER_A_KEYPAIR_BYTES); const signerB = await createKeyPairSignerFromBytes(SIGNER_B_KEYPAIR_BYTES); // Two signers and the payer sign the transaction independently - const wrapTxA = multisigOfflineSignWrapTx({ + const wrapTxA = await multisigOfflineSignWrap({ payer: createNoopSigner(payer.address), unwrappedTokenAccount: UNWRAPPED_TOKEN_ACCOUNT, - escrowAccount: ESCROW, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, amount: AMOUNT_TO_WRAP, unwrappedMint: UNWRAPPED_MINT_ADDRESS, recipientWrappedTokenAccount: RECIPIENT, transferAuthority: MULTISIG_SPL_TOKEN, - wrappedMint: WRAPPED_MINT_ADDRESS, + wrappedMint, wrappedMintAuthority, - unwrappedTokenProgram: UNWRAPPED_TOKEN_PROGRAM, + unwrappedTokenProgram: TOKEN_PROGRAM_ADDRESS, multiSigners: [signerA, createNoopSigner(signerB.address)], blockhash, }); const signedWrapTxA = await partiallySignTransactionMessageWithSigners(wrapTxA); - const wrapTxB = multisigOfflineSignWrapTx({ + const wrapTxB = await multisigOfflineSignWrap({ payer: createNoopSigner(payer.address), unwrappedTokenAccount: UNWRAPPED_TOKEN_ACCOUNT, - escrowAccount: ESCROW, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, amount: AMOUNT_TO_WRAP, unwrappedMint: UNWRAPPED_MINT_ADDRESS, recipientWrappedTokenAccount: RECIPIENT, transferAuthority: MULTISIG_SPL_TOKEN, - wrappedMint: WRAPPED_MINT_ADDRESS, + wrappedMint, wrappedMintAuthority, - unwrappedTokenProgram: UNWRAPPED_TOKEN_PROGRAM, + unwrappedTokenProgram: TOKEN_PROGRAM_ADDRESS, multiSigners: [createNoopSigner(signerA.address), signerB], blockhash, }); const signedWrapTxB = await partiallySignTransactionMessageWithSigners(wrapTxB); - const wrapTxC = multisigOfflineSignWrapTx({ + const wrapTxC = await multisigOfflineSignWrap({ payer, unwrappedTokenAccount: UNWRAPPED_TOKEN_ACCOUNT, - escrowAccount: ESCROW, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, amount: AMOUNT_TO_WRAP, unwrappedMint: UNWRAPPED_MINT_ADDRESS, recipientWrappedTokenAccount: RECIPIENT, transferAuthority: MULTISIG_SPL_TOKEN, - wrappedMint: WRAPPED_MINT_ADDRESS, + wrappedMint, wrappedMintAuthority, - unwrappedTokenProgram: UNWRAPPED_TOKEN_PROGRAM, + unwrappedTokenProgram: TOKEN_PROGRAM_ADDRESS, multiSigners: [createNoopSigner(signerA.address), createNoopSigner(signerB.address)], blockhash, }); @@ -647,11 +694,10 @@ Burns wrapped tokens and releases unwrapped tokens from escrow. ```console - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ WRAPPED_TOKEN_ACCOUNT=HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt $ UNWRAPPED_TOKEN_RECIPIENT=DKFjYKEFS4tkXjamwkuiGf555Lww3eRSWwNTbue9x14 - $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 50 + $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 50 Unwrapping 50 tokens from mint BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e Unwrapped token program: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA @@ -667,20 +713,24 @@ Burns wrapped tokens and releases unwrapped tokens from escrow. ```typescript import { address, + appendTransactionMessageInstructions, createKeyPairSignerFromBytes, createSolanaRpc, createSolanaRpcSubscriptions, + createTransactionMessage, getSignatureFromTransaction, + pipe, sendAndConfirmTransactionFactory, + setTransactionMessageFeePayerSigner, + setTransactionMessageLifetimeUsingBlockhash, signTransactionMessageWithSigners, } from '@solana/kit'; - import { singleSignerUnwrapTx } from '@solana-program/token-wrap'; + import { singleSignerUnwrap } from '@solana-program/token-wrap'; // Replace these consts with your own const PRIVATE_KEY_PAIR = new Uint8Array([242, 30, 38, 177, 152, 71, ... ]); - const WRAPPED_TOKEN_ACCOUNT = address('HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt'); - const ESCROW = address('4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3'); - const RECIPIENT = address('DKFjYKEFS4tkXjamwkuiGf555Lww3eRSWwNTbue9x14'); + const UNWRAPPED_TOKEN_ACCOUNT = address('4dSPDdFuTbKTuJDDtTd8SUdbH6QY42hpTPRi6RRzzsPF'); + const UNWRAP_SOURCE = address('BJYNChMx8tfc3wjUmSXWYJDe55QsgHUXFfL9PbgV6wa9'); const AMOUNT_TO_WRAP = 100n; async function main() { @@ -691,23 +741,27 @@ Burns wrapped tokens and releases unwrapped tokens from escrow. const payer = await createKeyPairSignerFromBytes(PRIVATE_KEY_PAIR); const { value: blockhash } = await rpc.getLatestBlockhash().send(); - const unwrapMessage = await singleSignerUnwrapTx({ + const unwrapHelper = await singleSignerUnwrap({ rpc, - blockhash, payer, - wrappedTokenAccount: WRAPPED_TOKEN_ACCOUNT, - unwrappedEscrow: ESCROW, + wrappedTokenAccount: UNWRAP_SOURCE, amount: AMOUNT_TO_WRAP, - recipientUnwrappedToken: RECIPIENT, + recipientUnwrappedToken: UNWRAPPED_TOKEN_ACCOUNT, }); - const signedUnwrapTx = await signTransactionMessageWithSigners(unwrapMessage.tx); - await sendAndConfirm(signedUnwrapTx, { commitment: 'confirmed' }); - const unwrapSignature = getSignatureFromTransaction(signedUnwrapTx); + const unwrapTx = await pipe( + createTransactionMessage({ version: 0 }), + tx => setTransactionMessageFeePayerSigner(payer, tx), + tx => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx), + tx => appendTransactionMessageInstructions(unwrapHelper.ixs, tx), + tx => signTransactionMessageWithSigners(tx), + ); + await sendAndConfirm(unwrapTx, { commitment: 'confirmed' }); + const unwrapSignature = getSignatureFromTransaction(unwrapTx); console.log('======== Unwrap Successful ========'); - console.log('Unwrapped amount:', unwrapMessage.amount); - console.log('Recipient account:', unwrapMessage.recipientUnwrappedToken); + console.log('Unwrapped amount:', unwrapHelper.amount); + console.log('Recipient account:', unwrapHelper.recipientUnwrappedToken); console.log('Signature:', unwrapSignature); } @@ -748,11 +802,10 @@ An example unwrapping tokens whose origin is a token account owned by an SPL Tok $ MULTISIG_ADDRESS=FFQvYvhaWnHeGsCMfixccUMdnXPgDrkG3KkGzpfBHFPb # note this should have the same program-id as wrapped token account $ UNWRAPPED_TOKEN_RECIPIENT=DKFjYKEFS4tkXjamwkuiGf555Lww3eRSWwNTbue9x14 $ UNWRAPPED_MINT=BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ UNWRAPPED_TOKEN_PROGRAM=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 5 \ + $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 5 \ --transfer-authority $MULTISIG_ADDRESS \ --fee-payer $FEE_PAYER \ --unwrapped-mint $UNWRAPPED_MINT \ @@ -777,11 +830,10 @@ An example unwrapping tokens whose origin is a token account owned by an SPL Tok $ MULTISIG_ADDRESS=FFQvYvhaWnHeGsCMfixccUMdnXPgDrkG3KkGzpfBHFPb $ UNWRAPPED_TOKEN_RECIPIENT=DKFjYKEFS4tkXjamwkuiGf555Lww3eRSWwNTbue9x14 $ UNWRAPPED_MINT=BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ UNWRAPPED_TOKEN_PROGRAM=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 5 \ + $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 5 \ --transfer-authority $MULTISIG_ADDRESS \ --fee-payer $FEE_PAYER \ --unwrapped-mint $UNWRAPPED_MINT \ @@ -807,11 +859,10 @@ An example unwrapping tokens whose origin is a token account owned by an SPL Tok $ MULTISIG_ADDRESS=FFQvYvhaWnHeGsCMfixccUMdnXPgDrkG3KkGzpfBHFPb $ UNWRAPPED_TOKEN_RECIPIENT=DKFjYKEFS4tkXjamwkuiGf555Lww3eRSWwNTbue9x14 $ UNWRAPPED_MINT=BVpjjYmSgSPZbFGTXe52NYXApsDNQJRe2qQF1hQft85e - $ ESCROW_ACCOUNT=4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3 $ UNWRAPPED_TOKEN_PROGRAM=TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA $ WRAPPED_TOKEN_PROGRAM=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb - $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $ESCROW_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 5 \ + $ spl-token-wrap unwrap $WRAPPED_TOKEN_ACCOUNT $UNWRAPPED_TOKEN_RECIPIENT 5 \ --transfer-authority $MULTISIG_ADDRESS \ --fee-payer $FEE_PAYER \ --unwrapped-mint $UNWRAPPED_MINT \ @@ -851,22 +902,25 @@ An example unwrapping tokens whose origin is a token account owned by an SPL Tok } from '@solana/kit'; import { TOKEN_2022_PROGRAM_ADDRESS } from '@solana-program/token-2022'; import { - findWrappedMintAuthorityPda, combinedMultisigTx, - multisigOfflineSignUnwrap + findWrappedMintAuthorityPda, + findWrappedMintPda, + multisigOfflineSignUnwrap, } from '@solana-program/token-wrap'; + import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token'; // Replace these consts with your own const PAYER_KEYPAIR_BYTES = new Uint8Array([242, 30, 38, 177, 152, 71, ... ]); + + const MULTISIG_SPL_TOKEN = address('2XBevFsu4pnZpB9PewYKAJHNyx9dFQf3MaiGBszF5fm8'); const MULTISIG_SPL_TOKEN_2022 = address('BSdexGFqwmDGeXe4pBXVbQnqrEH5trmo9W3wqoXUQY5Y'); + const SIGNER_A_KEYPAIR_BYTES = new Uint8Array([210, 190, 232, 169, 113, 107, ... ]); const SIGNER_B_KEYPAIR_BYTES = new Uint8Array([37, 161, 191, 225, 59, 192, ... ]); - const WRAPPED_MINT_ADDRESS = address('B8HbxGU4npjgjMX5xJFR2FYkgvAHdZqyVb8MyFvdsuNM'); - const UNWRAPPED_MINT_ADDRESS = address('E8r9ixwg7QYr6xCh4tSdHErZ6CUxQhVGHqF5bRoZXyyV'); - const UNWRAPPED_TOKEN_ACCOUNT = address('DGNyuKAWP3susy6XMbVsYHy2AMrrKmh8pXM3WpQUeyL2'); // Must be owned by multisig account - const UNWRAPPED_TOKEN_PROGRAM = address('TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'); - const ESCROW = address('4NoeQJKuH8fu1Pqk5k8BJpNu4wA7T8K6QABJxjTWoHs3'); - const RECIPIENT = address('HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt'); + + const UNWRAPPED_MINT_ADDRESS = address('F2qGWupzMUQnGfX8e25XZps8d9AGdVde8hLQT2pxsb4M'); + const UNWRAPPED_TOKEN_ACCOUNT = address('94Y9pxekEm59b67PQQwvjb7wbwz689wDZ3dAwhCtJpPS'); // Must be owned by MULTISIG_SPL_TOKEN + const WRAPPED_TOKEN_ACCOUNT = address('HKHfad5Rx7Vv1iWzPiQhx3cnXpbVfDonYRRo1e16x5Bt'); const AMOUNT_TO_WRAP = 100n; async function main() { @@ -877,61 +931,62 @@ An example unwrapping tokens whose origin is a token account owned by an SPL Tok const payer = await createKeyPairSignerFromBytes(PAYER_KEYPAIR_BYTES); const { value: blockhash } = await rpc.getLatestBlockhash().send(); - const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({ wrappedMint: WRAPPED_MINT_ADDRESS }); + const [wrappedMint] = await findWrappedMintPda({ + unwrappedMint: UNWRAPPED_MINT_ADDRESS, + wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, + }); + const [wrappedMintAuthority] = await findWrappedMintAuthorityPda({ wrappedMint }); const signerA = await createKeyPairSignerFromBytes(SIGNER_A_KEYPAIR_BYTES); const signerB = await createKeyPairSignerFromBytes(SIGNER_B_KEYPAIR_BYTES); - const { value: unwrapBlockhash } = await rpc.getLatestBlockhash().send(); + // Two signers and the payer sign the transaction independently - const unwrapTxA = multisigOfflineSignUnwrap({ + const unwrapTxA = await multisigOfflineSignUnwrap({ payer: createNoopSigner(payer.address), - unwrappedEscrow: ESCROW, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, amount: AMOUNT_TO_WRAP, unwrappedMint: UNWRAPPED_MINT_ADDRESS, - wrappedTokenAccount: RECIPIENT, + wrappedTokenAccount: WRAPPED_TOKEN_ACCOUNT, recipientUnwrappedToken: UNWRAPPED_TOKEN_ACCOUNT, transferAuthority: MULTISIG_SPL_TOKEN_2022, - wrappedMint: WRAPPED_MINT_ADDRESS, + wrappedMint, wrappedMintAuthority, - unwrappedTokenProgram: UNWRAPPED_TOKEN_PROGRAM, + unwrappedTokenProgram: TOKEN_PROGRAM_ADDRESS, multiSigners: [signerA, createNoopSigner(signerB.address)], - blockhash: unwrapBlockhash, + blockhash, }); const signedUnwrapTxA = await partiallySignTransactionMessageWithSigners(unwrapTxA); - const unwrapTxB = multisigOfflineSignUnwrap({ + const unwrapTxB = await multisigOfflineSignUnwrap({ payer: createNoopSigner(payer.address), - unwrappedEscrow: ESCROW, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, amount: AMOUNT_TO_WRAP, unwrappedMint: UNWRAPPED_MINT_ADDRESS, - wrappedTokenAccount: RECIPIENT, + wrappedTokenAccount: WRAPPED_TOKEN_ACCOUNT, recipientUnwrappedToken: UNWRAPPED_TOKEN_ACCOUNT, transferAuthority: MULTISIG_SPL_TOKEN_2022, - wrappedMint: WRAPPED_MINT_ADDRESS, + wrappedMint, wrappedMintAuthority, - unwrappedTokenProgram: UNWRAPPED_TOKEN_PROGRAM, + unwrappedTokenProgram: TOKEN_PROGRAM_ADDRESS, multiSigners: [createNoopSigner(signerA.address), signerB], - blockhash: unwrapBlockhash, + blockhash, }); const signedUnwrapTxB = await partiallySignTransactionMessageWithSigners(unwrapTxB); - const unwrapTxC = multisigOfflineSignUnwrap({ - payer: payer, - unwrappedEscrow: ESCROW, + const unwrapTxC = await multisigOfflineSignUnwrap({ + payer, wrappedTokenProgram: TOKEN_2022_PROGRAM_ADDRESS, amount: AMOUNT_TO_WRAP, unwrappedMint: UNWRAPPED_MINT_ADDRESS, - wrappedTokenAccount: RECIPIENT, + wrappedTokenAccount: WRAPPED_TOKEN_ACCOUNT, recipientUnwrappedToken: UNWRAPPED_TOKEN_ACCOUNT, transferAuthority: MULTISIG_SPL_TOKEN_2022, - wrappedMint: WRAPPED_MINT_ADDRESS, + wrappedMint, wrappedMintAuthority, - unwrappedTokenProgram: UNWRAPPED_TOKEN_PROGRAM, + unwrappedTokenProgram: TOKEN_PROGRAM_ADDRESS, multiSigners: [createNoopSigner(signerA.address), createNoopSigner(signerB.address)], - blockhash: unwrapBlockhash, + blockhash, }); const signedUnwrapTxC = await partiallySignTransactionMessageWithSigners(unwrapTxC);