diff --git a/token/js/cli/main.js b/token/js/cli/main.js index 5067c6a2dff3af..7e605cecde5900 100644 --- a/token/js/cli/main.js +++ b/token/js/cli/main.js @@ -9,13 +9,16 @@ import { createMint, createAccount, transfer, + transfer2, approveRevoke, - invalidApprove, failOnApproveOverspend, setAuthority, mintTo, + mintTo2, multisig, burn, + burn2, + freezeThawAccount, closeAccount, nativeToken, } from './token-test'; @@ -29,18 +32,24 @@ async function main() { await createAccount(); console.log('Run test: mintTo'); await mintTo(); + console.log('Run test: mintTo2'); + await mintTo2(); console.log('Run test: transfer'); await transfer(); + console.log('Run test: transfer2'); + await transfer2(); console.log('Run test: approveRevoke'); await approveRevoke(); - console.log('Run test: invalidApprove'); - await invalidApprove(); console.log('Run test: failOnApproveOverspend'); await failOnApproveOverspend(); console.log('Run test: setAuthority'); await setAuthority(); console.log('Run test: burn'); await burn(); + console.log('Run test: burn2'); + await burn2(); + console.log('Run test: freezeThawAccount'); + await freezeThawAccount(); console.log('Run test: closeAccount'); await closeAccount(); console.log('Run test: multisig'); diff --git a/token/js/cli/token-test.js b/token/js/cli/token-test.js index 0d584778052ea3..98d1f9c99f2590 100644 --- a/token/js/cli/token-test.js +++ b/token/js/cli/token-test.js @@ -28,9 +28,9 @@ function assert(condition, message) { } } -async function didThrow(func, args): Promise { +async function didThrow(obj, func, args): Promise { try { - await func.apply(args); + await func.apply(testToken, args); } catch (e) { return true; } @@ -125,7 +125,7 @@ export async function createMint(): Promise { connection, payer, testMintAuthority.publicKey, - null, + testMintAuthority.publicKey, 2, programId, ); @@ -139,7 +139,11 @@ export async function createMint(): Promise { assert(mintInfo.supply.toNumber() === 0); assert(mintInfo.decimals === 2); assert(mintInfo.isInitialized === true); - assert(mintInfo.freezeAuthority === null); + if (mintInfo.freezeAuthority !== null) { + assert(mintInfo.freezeAuthority.equals(testMintAuthority.publicKey)); + } else { + assert(mintInfo.freezeAuthority !== null); + } } export async function createAccount(): Promise { @@ -168,6 +172,26 @@ export async function mintTo(): Promise { assert(accountInfo.amount.toNumber() === 1000); } +export async function mintTo2(): Promise { + assert( + await didThrow(testToken, testToken.mintTo2, [ + testAccount, + testMintAuthority, + [], + 1000, + 1, + ]), + ); + + await testToken.mintTo2(testAccount, testMintAuthority, [], 1000, 2); + + const mintInfo = await testToken.getMintInfo(); + assert(mintInfo.supply.toNumber() === 2000); + + const accountInfo = await testToken.getAccountInfo(testAccount); + assert(accountInfo.amount.toNumber() === 2000); +} + export async function transfer(): Promise { const destOwner = new Account(); const dest = await testToken.createAccount(destOwner.publicKey); @@ -175,13 +199,40 @@ export async function transfer(): Promise { await testToken.transfer(testAccount, dest, testAccountOwner, [], 100); const mintInfo = await testToken.getMintInfo(); - assert(mintInfo.supply.toNumber() === 1000); + assert(mintInfo.supply.toNumber() === 2000); + + let destAccountInfo = await testToken.getAccountInfo(dest); + assert(destAccountInfo.amount.toNumber() === 100); + + let testAccountInfo = await testToken.getAccountInfo(testAccount); + assert(testAccountInfo.amount.toNumber() === 1900); +} + +export async function transfer2(): Promise { + const destOwner = new Account(); + const dest = await testToken.createAccount(destOwner.publicKey); + + assert( + await didThrow(testToken, testToken.transfer2, [ + testAccount, + dest, + testAccountOwner, + [], + 100, + 1, + ]), + ); + + await testToken.transfer2(testAccount, dest, testAccountOwner, [], 100, 2); + + const mintInfo = await testToken.getMintInfo(); + assert(mintInfo.supply.toNumber() === 2000); let destAccountInfo = await testToken.getAccountInfo(dest); assert(destAccountInfo.amount.toNumber() === 100); let testAccountInfo = await testToken.getAccountInfo(testAccount); - assert(testAccountInfo.amount.toNumber() === 900); + assert(testAccountInfo.amount.toNumber() === 1800); } export async function approveRevoke(): Promise { @@ -206,18 +257,6 @@ export async function approveRevoke(): Promise { } } -export async function invalidApprove(): Promise { - const owner = new Account(); - const account1 = await testToken.createAccount(owner.publicKey); - const account2 = await testToken.createAccount(owner.publicKey); - const delegate = new Account(); - - // account2 is not a delegate account of account1 - assert(didThrow(testToken.approve, [account1, account2, owner, [], 123])); - // account1Delegate is not a delegate account of account2 - assert(didThrow(testToken.approve, [account2, delegate, owner, [], 123])); -} - export async function failOnApproveOverspend(): Promise { const owner = new Account(); const account1 = await testToken.createAccount(owner.publicKey); @@ -232,7 +271,7 @@ export async function failOnApproveOverspend(): Promise { assert(account1Info.amount.toNumber() == 10); assert(account1Info.delegatedAmount.toNumber() == 2); if (account1Info.delegate === null) { - throw new Error('deleage should not be null'); + throw new Error('delegate should not be null'); } else { assert(account1Info.delegate.equals(delegate.publicKey)); } @@ -250,7 +289,15 @@ export async function failOnApproveOverspend(): Promise { assert(account1Info.delegate === null); assert(account1Info.delegatedAmount.toNumber() == 0); - assert(didThrow(testToken.transfer, [account1, account2, delegate, [], 1])); + assert( + await didThrow(testToken, testToken.transfer, [ + account1, + account2, + delegate, + [], + 1, + ]), + ); } export async function setAuthority(): Promise { @@ -263,8 +310,8 @@ export async function setAuthority(): Promise { [], ); assert( - didThrow(testToken.setAuthority, [ - testAccountOwner, + await didThrow(testToken, testToken.setAuthority, [ + testAccount, newOwner.publicKey, 'AccountOwner', testAccountOwner, @@ -290,6 +337,53 @@ export async function burn(): Promise { assert(accountInfo.amount.toNumber() == amount - 1); } +export async function burn2(): Promise { + let accountInfo = await testToken.getAccountInfo(testAccount); + const amount = accountInfo.amount.toNumber(); + + assert( + await didThrow(testToken, testToken.burn2, [ + testAccount, + testAccountOwner, + [], + 1, + 1, + ]), + ); + + await testToken.burn2(testAccount, testAccountOwner, [], 1, 2); + + accountInfo = await testToken.getAccountInfo(testAccount); + assert(accountInfo.amount.toNumber() == amount - 1); +} + +export async function freezeThawAccount(): Promise { + let accountInfo = await testToken.getAccountInfo(testAccount); + const amount = accountInfo.amount.toNumber(); + + await testToken.freezeAccount(testAccount, testMintAuthority, []); + + const destOwner = new Account(); + const dest = await testToken.createAccount(destOwner.publicKey); + + assert( + await didThrow(testToken, testToken.transfer, [ + testAccount, + dest, + testAccountOwner, + [], + 100, + ]), + ); + + await testToken.thawAccount(testAccount, testMintAuthority, []); + + await testToken.transfer(testAccount, dest, testAccountOwner, [], 100); + + let testAccountInfo = await testToken.getAccountInfo(testAccount); + assert(testAccountInfo.amount.toNumber() === amount - 100); +} + export async function closeAccount(): Promise { const closeAuthority = new Account(); @@ -312,7 +406,12 @@ export async function closeAccount(): Promise { // Check that accounts with non-zero token balance cannot be closed assert( - didThrow(testToken.closeAccount, [testAccount, dest, closeAuthority, []]), + await didThrow(testToken, testToken.closeAccount, [ + testAccount, + dest, + closeAuthority, + [], + ]), ); const connection = await getConnection(); diff --git a/token/js/client/token.js b/token/js/client/token.js index 117cb0a2f13c0f..5516b7fc3dc372 100644 --- a/token/js/client/token.js +++ b/token/js/client/token.js @@ -324,12 +324,12 @@ export class Token { * Creates and initializes a token. * * @param connection The connection to use - * @param owner User account that will own the returned account - * @param supply Initial supply to mint + * @param payer Fee payer for transaction + * @param mintAuthority Account or multisig that will control minting + * @param freezeAuthority Optional account or multisig that can freeze token accounts * @param decimals Location of the decimal place * @param programId Optional token programId, uses the system programId by default - * @return Token object for the newly minted token, Public key of the account - * holding the total amount of new tokens + * @return Token object for the newly minted token */ static async createMint( connection: Connection, @@ -432,6 +432,8 @@ export class Token { * * This account may then be used for multisignature verification * + * @param m Number of required signatures + * @param signers Full set of signers * @return Public key of the new multisig account */ async createMultisig( @@ -623,24 +625,24 @@ export class Token { * * @param source Source account * @param destination Destination account - * @param authority Owner of the source account - * @param multiSigners Signing accounts if `authority` is a multiSig + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig * @param amount Number of tokens to transfer */ async transfer( source: PublicKey, destination: PublicKey, - authority: any, + owner: any, multiSigners: Array, amount: number | u64, ): Promise { let ownerPublicKey; let signers; - if (isAccount(authority)) { - ownerPublicKey = authority.publicKey; - signers = [authority]; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; } else { - ownerPublicKey = authority; + ownerPublicKey = owner; signers = multiSigners; } return await sendAndConfirmTransaction( @@ -788,9 +790,9 @@ export class Token { * Mint new tokens * * @param dest Public key of the account to mint to - * @param authority Owner of the mint + * @param authority Minting authority * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount ammount to mint + * @param amount Amount to mint */ async mintTo( dest: PublicKey, @@ -829,9 +831,9 @@ export class Token { * Burn tokens * * @param account Account to burn tokens from - * @param owner Public key account owner - * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount amount to burn + * @param owner Account owner + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Amount to burn */ async burn( account: PublicKey, @@ -871,8 +873,8 @@ export class Token { * * @param account Account to close * @param dest Account to receive the remaining balance of the closed account - * @param authority Account which is allowed to close the account - * @param multiSigners Signing accounts if `owner` is a multiSig + * @param authority Authority which is allowed to close the account + * @param multiSigners Signing accounts if `authority` is a multiSig */ async closeAccount( account: PublicKey, @@ -906,13 +908,273 @@ export class Token { ); } + /** + * Freeze account + * + * @param account Account to freeze + * @param authority The mint freeze authority + * @param multiSigners Signing accounts if `authority` is a multiSig + */ + async freezeAccount( + account: PublicKey, + authority: any, + multiSigners: Array, + ): Promise { + let authorityPublicKey; + let signers; + if (isAccount(authority)) { + authorityPublicKey = authority.publicKey; + signers = [authority]; + } else { + authorityPublicKey = authority; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'FreezeAccount', + this.connection, + new Transaction().add( + Token.createFreezeAccountInstruction( + this.programId, + account, + this.publicKey, + authorityPublicKey, + multiSigners, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Thaw account + * + * @param account Account to thaw + * @param authority The mint freeze authority + * @param multiSigners Signing accounts if `authority` is a multiSig + */ + async thawAccount( + account: PublicKey, + authority: any, + multiSigners: Array, + ): Promise { + let authorityPublicKey; + let signers; + if (isAccount(authority)) { + authorityPublicKey = authority.publicKey; + signers = [authority]; + } else { + authorityPublicKey = authority; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'ThawAccount', + this.connection, + new Transaction().add( + Token.createThawAccountInstruction( + this.programId, + account, + this.publicKey, + authorityPublicKey, + multiSigners, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Transfer tokens to another account, asserting the token mint and decimals + * + * @param source Source account + * @param destination Destination account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Number of tokens to transfer + * @param decimals Number of decimals in transfer amount + */ + async transfer2( + source: PublicKey, + destination: PublicKey, + owner: any, + multiSigners: Array, + amount: number | u64, + decimals: number, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + return await sendAndConfirmTransaction( + 'Transfer2', + this.connection, + new Transaction().add( + Token.createTransfer2Instruction( + this.programId, + source, + this.publicKey, + destination, + ownerPublicKey, + multiSigners, + amount, + decimals, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Grant a third-party permission to transfer up the specified number of tokens from an account, + * asserting the token mint and decimals + * + * @param account Public key of the account + * @param delegate Account authorized to perform a transfer tokens from the source account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Maximum number of tokens the delegate may transfer + * @param decimals Number of decimals in approve amount + */ + async approve2( + account: PublicKey, + delegate: PublicKey, + owner: any, + multiSigners: Array, + amount: number | u64, + decimals: number, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'Approve2', + this.connection, + new Transaction().add( + Token.createApprove2Instruction( + this.programId, + account, + this.publicKey, + delegate, + ownerPublicKey, + multiSigners, + amount, + decimals, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Mint new tokens, asserting the token mint and decimals + * + * @param dest Public key of the account to mint to + * @param authority Minting authority + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount Amount to mint + * @param decimals Number of decimals in amount to mint + */ + async mintTo2( + dest: PublicKey, + authority: any, + multiSigners: Array, + amount: number, + decimals: number, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(authority)) { + ownerPublicKey = authority.publicKey; + signers = [authority]; + } else { + ownerPublicKey = authority; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'MintTo2', + this.connection, + new Transaction().add( + Token.createMintTo2Instruction( + this.programId, + this.publicKey, + dest, + ownerPublicKey, + multiSigners, + amount, + decimals, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Burn tokens, asserting the token mint and decimals + * + * @param account Account to burn tokens from + * @param owner Account owner + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Amount to burn + * @param decimals Number of decimals in amount to burn + */ + async burn2( + account: PublicKey, + owner: any, + multiSigners: Array, + amount: number, + decimals: number, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'Burn2', + this.connection, + new Transaction().add( + Token.createBurn2Instruction( + this.programId, + this.publicKey, + account, + ownerPublicKey, + multiSigners, + amount, + decimals, + ), + ), + this.payer, + ...signers, + ); + } + /** * Construct an init mint instruction * * @param programId SPL Token program account * @param mint Token mint account - * @param token New token account - * @param owner Owner of the new token account + * @param decimals Number of decimals in token account amounts + * @param mintAuthority Minting authority + * @param freezeAuthority Optional authority that can freeze token accounts */ static createInitMintInstruction( programId: PublicKey, @@ -993,6 +1255,7 @@ export class Token { /** * Construct a Transfer instruction * + * @param programId SPL Token program account * @param source Source account * @param destination Destination account * @param owner Owner of the source account @@ -1051,6 +1314,7 @@ export class Token { /** * Construct an Approve instruction * + * @param programId SPL Token program account * @param account Public key of the account * @param delegate Account authorized to perform a transfer of tokens from the source account * @param owner Owner of the source account @@ -1104,13 +1368,12 @@ export class Token { } /** - * Construct an Approve instruction + * Construct a Revoke instruction * + * @param programId SPL Token program account * @param account Public key of the account - * @param delegate Account authorized to perform a transfer of tokens from the source account * @param owner Owner of the source account * @param multiSigners Signing accounts if `owner` is a multiSig - * @param amount Maximum number of tokens the delegate may transfer */ static createRevokeInstruction( programId: PublicKey, @@ -1152,6 +1415,7 @@ export class Token { /** * Construct a SetAuthority instruction * + * @param programId SPL Token program account * @param account Public key of the account * @param newAuthority New authority of the account * @param authorityType Type of authority to set @@ -1211,10 +1475,12 @@ export class Token { /** * Construct a MintTo instruction * + * @param programId SPL Token program account + * @param mint Public key of the mint * @param dest Public key of the account to mint to * @param authority The mint authority * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount amount to mint + * @param amount Amount to mint */ static createMintToInstruction( programId: PublicKey, @@ -1269,6 +1535,7 @@ export class Token { /** * Construct a Burn instruction * + * @param programId SPL Token program account * @param mint Mint for the account * @param account Account to burn tokens from * @param owner Owner of the account @@ -1326,10 +1593,12 @@ export class Token { } /** - * Construct a Burn instruction + * Construct a Close instruction * - * @param account Account to burn tokens from - * @param owner account owner + * @param programId SPL Token program account + * @param account Account to close + * @param dest Account to receive the remaining balance of the closed account + * @param authority Account Close authority * @param multiSigners Signing accounts if `owner` is a multiSig */ static createCloseAccountInstruction( @@ -1371,4 +1640,358 @@ export class Token { data, }); } + + /** + * Construct a Freeze instruction + * + * @param programId SPL Token program account + * @param account Account to freeze + * @param mint Mint account + * @param authority Mint freeze authority + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + static createFreezeAccountInstruction( + programId: PublicKey, + account: PublicKey, + mint: PublicKey, + authority: PublicKey, + multiSigners: Array, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 10, // FreezeAccount instruction + }, + data, + ); + + let keys = [ + {pubkey: account, isSigner: false, isWritable: true}, + {pubkey: mint, isSigner: false, isWritable: false}, + ]; + if (multiSigners.length === 0) { + keys.push({pubkey: authority, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: authority, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a Thaw instruction + * + * @param programId SPL Token program account + * @param account Account to thaw + * @param mint Mint account + * @param authority Mint freeze authority + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + static createThawAccountInstruction( + programId: PublicKey, + account: PublicKey, + mint: PublicKey, + authority: PublicKey, + multiSigners: Array, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 11, // ThawAccount instruction + }, + data, + ); + + let keys = [ + {pubkey: account, isSigner: false, isWritable: true}, + {pubkey: mint, isSigner: false, isWritable: false}, + ]; + if (multiSigners.length === 0) { + keys.push({pubkey: authority, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: authority, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a Transfer2 instruction + * + * @param programId SPL Token program account + * @param source Source account + * @param mint Mint account + * @param destination Destination account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount Number of tokens to transfer + * @param decimals Number of decimals in transfer amount + */ + static createTransfer2Instruction( + programId: PublicKey, + source: PublicKey, + mint: PublicKey, + destination: PublicKey, + owner: PublicKey, + multiSigners: Array, + amount: number | u64, + decimals: number, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + BufferLayout.u8('decimals'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 12, // Transfer2 instruction + amount: new u64(amount).toBuffer(), + decimals, + }, + data, + ); + + let keys = [ + {pubkey: source, isSigner: false, isWritable: true}, + {pubkey: mint, isSigner: false, isWritable: false}, + {pubkey: destination, isSigner: false, isWritable: true}, + ]; + if (multiSigners.length === 0) { + keys.push({ + pubkey: owner, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct an Approve2 instruction + * + * @param programId SPL Token program account + * @param account Public key of the account + * @param mint Mint account + * @param delegate Account authorized to perform a transfer of tokens from the source account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Maximum number of tokens the delegate may transfer + * @param decimals Number of decimals in approve amount + */ + static createApprove2Instruction( + programId: PublicKey, + account: PublicKey, + mint: PublicKey, + delegate: PublicKey, + owner: PublicKey, + multiSigners: Array, + amount: number | u64, + decimals: number, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + BufferLayout.u8('decimals'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 13, // Approve2 instruction + amount: new u64(amount).toBuffer(), + decimals, + }, + data, + ); + + let keys = [ + {pubkey: account, isSigner: false, isWritable: true}, + {pubkey: mint, isSigner: false, isWritable: false}, + {pubkey: delegate, isSigner: false, isWritable: false}, + ]; + if (multiSigners.length === 0) { + keys.push({pubkey: owner, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a MintTo2 instruction + * + * @param programId SPL Token program account + * @param mint Public key of the mint + * @param dest Public key of the account to mint to + * @param authority The mint authority + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount Amount to mint + * @param decimals Number of decimals in amount to mint + */ + static createMintTo2Instruction( + programId: PublicKey, + mint: PublicKey, + dest: PublicKey, + authority: PublicKey, + multiSigners: Array, + amount: number, + decimals: number, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + BufferLayout.u8('decimals'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 14, // MintTo2 instruction + amount: new u64(amount).toBuffer(), + decimals, + }, + data, + ); + + let keys = [ + {pubkey: mint, isSigner: false, isWritable: true}, + {pubkey: dest, isSigner: false, isWritable: true}, + ]; + if (multiSigners.length === 0) { + keys.push({ + pubkey: authority, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({pubkey: authority, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a Burn2 instruction + * + * @param programId SPL Token program account + * @param mint Mint for the account + * @param account Account to burn tokens from + * @param owner Owner of the account + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount amount to burn + */ + static createBurn2Instruction( + programId: PublicKey, + mint: PublicKey, + account: PublicKey, + owner: PublicKey, + multiSigners: Array, + amount: number, + decimals: number, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + BufferLayout.u8('decimals'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 15, // Burn2 instruction + amount: new u64(amount).toBuffer(), + decimals, + }, + data, + ); + + let keys = [ + {pubkey: account, isSigner: false, isWritable: true}, + {pubkey: mint, isSigner: false, isWritable: true}, + ]; + if (multiSigners.length === 0) { + keys.push({ + pubkey: owner, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } } diff --git a/token/js/package.json b/token/js/package.json index 9050937d4772b6..dbfabbe8808465 100644 --- a/token/js/package.json +++ b/token/js/package.json @@ -1,6 +1,6 @@ { "name": "@solana/spl-token", - "version": "0.0.7", + "version": "0.0.8", "description": "SPL Token JavaScript API", "license": "MIT", "author": "Solana Maintainers ",