solana-program-library/token/js/client/token.js

2288 lines
59 KiB
JavaScript

/**
* @flow
*/
import {Buffer} from 'buffer';
import assert from 'assert';
import BN from 'bn.js';
import * as BufferLayout from 'buffer-layout';
import {
Account,
PublicKey,
SystemProgram,
Transaction,
TransactionInstruction,
SYSVAR_RENT_PUBKEY,
} from '@solana/web3.js';
import type {
Connection,
Commitment,
TransactionSignature,
} from '@solana/web3.js';
import * as Layout from './layout';
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
export const TOKEN_PROGRAM_ID: PublicKey = new PublicKey(
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
);
export const ASSOCIATED_TOKEN_PROGRAM_ID: PublicKey = new PublicKey(
'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL',
);
const FAILED_TO_FIND_ACCOUNT = 'Failed to find account';
const INVALID_ACCOUNT_OWNER = 'Invalid account owner';
/**
* Unfortunately, BufferLayout.encode uses an `instanceof` check for `Buffer`
* which fails when using `publicKey.toBuffer()` directly because the bundled `Buffer`
* class in `@solana/web3.js` is different from the bundled `Buffer` class in this package
*/
function pubkeyToBuffer(publicKey: PublicKey): typeof Buffer {
return Buffer.from(publicKey.toBuffer());
}
/**
* 64-bit value
*/
export class u64 extends BN {
/**
* Convert to Buffer representation
*/
toBuffer(): typeof Buffer {
const a = super.toArray().reverse();
const b = Buffer.from(a);
if (b.length === 8) {
return b;
}
assert(b.length < 8, 'u64 too large');
const zeroPad = Buffer.alloc(8);
b.copy(zeroPad);
return zeroPad;
}
/**
* Construct a u64 from Buffer representation
*/
static fromBuffer(buffer: typeof Buffer): u64 {
assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`);
return new u64(
[...buffer]
.reverse()
.map(i => `00${i.toString(16)}`.slice(-2))
.join(''),
16,
);
}
}
function isAccount(accountOrPublicKey: any): boolean {
return 'publicKey' in accountOrPublicKey;
}
type AuthorityType =
| 'MintTokens'
| 'FreezeAccount'
| 'AccountOwner'
| 'CloseAccount';
const AuthorityTypeCodes = {
MintTokens: 0,
FreezeAccount: 1,
AccountOwner: 2,
CloseAccount: 3,
};
// The address of the special mint for wrapped native token.
export const NATIVE_MINT: PublicKey = new PublicKey(
'So11111111111111111111111111111111111111112',
);
/**
* Information about the mint
*/
type MintInfo = {|
/**
* Optional authority used to mint new tokens. The mint authority may only be provided during
* mint creation. If no mint authority is present then the mint has a fixed supply and no
* further tokens may be minted.
*/
mintAuthority: null | PublicKey,
/**
* Total supply of tokens
*/
supply: u64,
/**
* Number of base 10 digits to the right of the decimal place
*/
decimals: number,
/**
* Is this mint initialized
*/
isInitialized: boolean,
/**
* Optional authority to freeze token accounts
*/
freezeAuthority: null | PublicKey,
|};
export const MintLayout: typeof BufferLayout.Structure = BufferLayout.struct([
BufferLayout.u32('mintAuthorityOption'),
Layout.publicKey('mintAuthority'),
Layout.uint64('supply'),
BufferLayout.u8('decimals'),
BufferLayout.u8('isInitialized'),
BufferLayout.u32('freezeAuthorityOption'),
Layout.publicKey('freezeAuthority'),
]);
/**
* Information about an account
*/
type AccountInfo = {|
/**
* The address of this account
*/
address: PublicKey,
/**
* The mint associated with this account
*/
mint: PublicKey,
/**
* Owner of this account
*/
owner: PublicKey,
/**
* Amount of tokens this account holds
*/
amount: u64,
/**
* The delegate for this account
*/
delegate: null | PublicKey,
/**
* The amount of tokens the delegate authorized to the delegate
*/
delegatedAmount: u64,
/**
* Is this account initialized
*/
isInitialized: boolean,
/**
* Is this account frozen
*/
isFrozen: boolean,
/**
* Is this a native token account
*/
isNative: boolean,
/**
* If this account is a native token, it must be rent-exempt. This
* value logs the rent-exempt reserve which must remain in the balance
* until the account is closed.
*/
rentExemptReserve: null | u64,
/**
* Optional authority to close the account
*/
closeAuthority: null | PublicKey,
|};
/**
* @private
*/
export const AccountLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[
Layout.publicKey('mint'),
Layout.publicKey('owner'),
Layout.uint64('amount'),
BufferLayout.u32('delegateOption'),
Layout.publicKey('delegate'),
BufferLayout.u8('state'),
BufferLayout.u32('isNativeOption'),
Layout.uint64('isNative'),
Layout.uint64('delegatedAmount'),
BufferLayout.u32('closeAuthorityOption'),
Layout.publicKey('closeAuthority'),
],
);
/**
* Information about an multisig
*/
type MultisigInfo = {|
/**
* The number of signers required
*/
m: number,
/**
* Number of possible signers, corresponds to the
* number of `signers` that are valid.
*/
n: number,
/**
* Is this mint initialized
*/
initialized: boolean,
/**
* The signers
*/
signer1: PublicKey,
signer2: PublicKey,
signer3: PublicKey,
signer4: PublicKey,
signer5: PublicKey,
signer6: PublicKey,
signer7: PublicKey,
signer8: PublicKey,
signer9: PublicKey,
signer10: PublicKey,
signer11: PublicKey,
|};
/**
* @private
*/
const MultisigLayout = BufferLayout.struct([
BufferLayout.u8('m'),
BufferLayout.u8('n'),
BufferLayout.u8('is_initialized'),
Layout.publicKey('signer1'),
Layout.publicKey('signer2'),
Layout.publicKey('signer3'),
Layout.publicKey('signer4'),
Layout.publicKey('signer5'),
Layout.publicKey('signer6'),
Layout.publicKey('signer7'),
Layout.publicKey('signer8'),
Layout.publicKey('signer9'),
Layout.publicKey('signer10'),
Layout.publicKey('signer11'),
]);
/**
* An ERC20-like Token
*/
export class Token {
/**
* @private
*/
connection: Connection;
/**
* The public key identifying this mint
*/
publicKey: PublicKey;
/**
* Program Identifier for the Token program
*/
programId: PublicKey;
/**
* Program Identifier for the Associated Token program
*/
associatedProgramId: PublicKey;
/**
* Fee payer
*/
payer: Account;
/**
* Create a Token object attached to the specific mint
*
* @param connection The connection to use
* @param token Public key of the mint
* @param programId token programId
* @param payer Payer of fees
*/
constructor(
connection: Connection,
publicKey: PublicKey,
programId: PublicKey,
payer: Account,
) {
Object.assign(this, {
connection,
publicKey,
programId,
payer,
// Hard code is ok; Overriding is needed only for tests
associatedProgramId: ASSOCIATED_TOKEN_PROGRAM_ID,
});
}
/**
* Get the minimum balance for the mint to be rent exempt
*
* @return Number of lamports required
*/
static async getMinBalanceRentForExemptMint(
connection: Connection,
): Promise<number> {
return await connection.getMinimumBalanceForRentExemption(MintLayout.span);
}
/**
* Get the minimum balance for the account to be rent exempt
*
* @return Number of lamports required
*/
static async getMinBalanceRentForExemptAccount(
connection: Connection,
): Promise<number> {
return await connection.getMinimumBalanceForRentExemption(
AccountLayout.span,
);
}
/**
* Get the minimum balance for the multsig to be rent exempt
*
* @return Number of lamports required
*/
static async getMinBalanceRentForExemptMultisig(
connection: Connection,
): Promise<number> {
return await connection.getMinimumBalanceForRentExemption(
MultisigLayout.span,
);
}
/**
* Create and initialize a token.
*
* @param connection The connection to use
* @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
*/
static async createMint(
connection: Connection,
payer: Account,
mintAuthority: PublicKey,
freezeAuthority: PublicKey | null,
decimals: number,
programId: PublicKey,
): Promise<Token> {
const mintAccount = new Account();
const token = new Token(
connection,
mintAccount.publicKey,
programId,
payer,
);
// Allocate memory for the account
const balanceNeeded = await Token.getMinBalanceRentForExemptMint(
connection,
);
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: mintAccount.publicKey,
lamports: balanceNeeded,
space: MintLayout.span,
programId,
}),
);
transaction.add(
Token.createInitMintInstruction(
programId,
mintAccount.publicKey,
decimals,
mintAuthority,
freezeAuthority,
),
);
// Send the two instructions
await sendAndConfirmTransaction(
'createAccount and InitializeMint',
connection,
transaction,
payer,
mintAccount,
);
return token;
}
/**
* Create and initialize a new account.
*
* This account may then be used as a `transfer()` or `approve()` destination
*
* @param owner User account that will own the new account
* @return Public key of the new empty account
*/
async createAccount(owner: PublicKey): Promise<PublicKey> {
// Allocate memory for the account
const balanceNeeded = await Token.getMinBalanceRentForExemptAccount(
this.connection,
);
const newAccount = new Account();
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: this.payer.publicKey,
newAccountPubkey: newAccount.publicKey,
lamports: balanceNeeded,
space: AccountLayout.span,
programId: this.programId,
}),
);
const mintPublicKey = this.publicKey;
transaction.add(
Token.createInitAccountInstruction(
this.programId,
mintPublicKey,
newAccount.publicKey,
owner,
),
);
// Send the two instructions
await sendAndConfirmTransaction(
'createAccount and InitializeAccount',
this.connection,
transaction,
this.payer,
newAccount,
);
return newAccount.publicKey;
}
/**
* Create and initialize the associated account.
*
* This account may then be used as a `transfer()` or `approve()` destination
*
* @param owner User account that will own the new account
* @return Public key of the new associated account
*/
async createAssociatedTokenAccount(owner: PublicKey): Promise<PublicKey> {
const associatedAddress = await Token.getAssociatedTokenAddress(
this.associatedProgramId,
this.programId,
this.publicKey,
owner,
);
return this.createAssociatedTokenAccountInternal(owner, associatedAddress);
}
async createAssociatedTokenAccountInternal(
owner: PublicKey,
associatedAddress: PublicKey,
): Promise<PublicKey> {
await sendAndConfirmTransaction(
'CreateAssociatedTokenAccount',
this.connection,
new Transaction().add(
Token.createAssociatedTokenAccountInstruction(
this.associatedProgramId,
this.programId,
this.publicKey,
associatedAddress,
owner,
this.payer.publicKey,
),
),
this.payer,
);
return associatedAddress;
}
/**
* Retrieve the associated account or create one if not found.
*
* This account may then be used as a `transfer()` or `approve()` destination
*
* @param owner User account that will own the new account
* @return The new associated account
*/
async getOrCreateAssociatedAccountInfo(
owner: PublicKey,
): Promise<AccountInfo> {
const associatedAddress = await Token.getAssociatedTokenAddress(
this.associatedProgramId,
this.programId,
this.publicKey,
owner,
);
// This is the optimum logic, considering TX fee, client-side computation,
// RPC roundtrips and guaranteed idempotent.
// Sadly we can't do this atomically;
try {
return await this.getAccountInfo(associatedAddress);
} catch (err) {
// INVALID_ACCOUNT_OWNER can be possible if the associatedAddress has
// already been received some lamports (= became system accounts).
// Assuming program derived addressing is safe, this is the only case
// for the INVALID_ACCOUNT_OWNER in this code-path
if (
err.message === FAILED_TO_FIND_ACCOUNT ||
err.message === INVALID_ACCOUNT_OWNER
) {
// as this isn't atomic, it's possible others can create associated
// accounts meanwhile
try {
await this.createAssociatedTokenAccountInternal(
owner,
associatedAddress,
);
} catch (err) {
// ignore all errors; for now there is no API compatible way to
// selectively ignore the expected instruction error if the
// associated account is existing already.
}
// Now this should always succeed
return await this.getAccountInfo(associatedAddress);
} else {
throw err;
}
}
}
/**
* Create and initialize a new account on the special native token mint.
*
* In order to be wrapped, the account must have a balance of native tokens
* when it is initialized with the token program.
*
* This function sends lamports to the new account before initializing it.
*
* @param connection A solana web3 connection
* @param programId The token program ID
* @param owner The owner of the new token account
* @param payer The source of the lamports to initialize, and payer of the initialization fees.
* @param amount The amount of lamports to wrap
* @return {Promise<PublicKey>} The new token account
*/
static async createWrappedNativeAccount(
connection: Connection,
programId: PublicKey,
owner: PublicKey,
payer: Account,
amount: number,
): Promise<PublicKey> {
// Allocate memory for the account
const balanceNeeded = await Token.getMinBalanceRentForExemptAccount(
connection,
);
// Create a new account
const newAccount = new Account();
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: newAccount.publicKey,
lamports: balanceNeeded,
space: AccountLayout.span,
programId,
}),
);
// Send lamports to it (these will be wrapped into native tokens by the token program)
transaction.add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: newAccount.publicKey,
lamports: amount,
}),
);
// Assign the new account to the native token mint.
// the account will be initialized with a balance equal to the native token balance.
// (i.e. amount)
transaction.add(
Token.createInitAccountInstruction(
programId,
NATIVE_MINT,
newAccount.publicKey,
owner,
),
);
// Send the three instructions
await sendAndConfirmTransaction(
'createAccount, transfer, and initializeAccount',
connection,
transaction,
payer,
newAccount,
);
return newAccount.publicKey;
}
/**
* Create and initialize a new multisig.
*
* 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(
m: number,
signers: Array<PublicKey>,
): Promise<PublicKey> {
const multisigAccount = new Account();
// Allocate memory for the account
const balanceNeeded = await Token.getMinBalanceRentForExemptMultisig(
this.connection,
);
const transaction = new Transaction();
transaction.add(
SystemProgram.createAccount({
fromPubkey: this.payer.publicKey,
newAccountPubkey: multisigAccount.publicKey,
lamports: balanceNeeded,
space: MultisigLayout.span,
programId: this.programId,
}),
);
// create the new account
let keys = [
{pubkey: multisigAccount.publicKey, isSigner: false, isWritable: true},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
];
signers.forEach(signer =>
keys.push({pubkey: signer, isSigner: false, isWritable: false}),
);
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
BufferLayout.u8('m'),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 2, // InitializeMultisig instruction
m,
},
data,
);
transaction.add({
keys,
programId: this.programId,
data,
});
// Send the two instructions
await sendAndConfirmTransaction(
'createAccount and InitializeMultisig',
this.connection,
transaction,
this.payer,
multisigAccount,
);
return multisigAccount.publicKey;
}
/**
* Retrieve mint information
*/
async getMintInfo(): Promise<MintInfo> {
const info = await this.connection.getAccountInfo(this.publicKey);
if (info === null) {
throw new Error('Failed to find mint account');
}
if (!info.owner.equals(this.programId)) {
throw new Error(`Invalid mint owner: ${JSON.stringify(info.owner)}`);
}
if (info.data.length != MintLayout.span) {
throw new Error(`Invalid mint size`);
}
const data = Buffer.from(info.data);
const mintInfo = MintLayout.decode(data);
if (mintInfo.mintAuthorityOption === 0) {
mintInfo.mintAuthority = null;
} else {
mintInfo.mintAuthority = new PublicKey(mintInfo.mintAuthority);
}
mintInfo.supply = u64.fromBuffer(mintInfo.supply);
mintInfo.isInitialized = mintInfo.isInitialized != 0;
if (mintInfo.freezeAuthorityOption === 0) {
mintInfo.freezeAuthority = null;
} else {
mintInfo.freezeAuthority = new PublicKey(mintInfo.freezeAuthority);
}
return mintInfo;
}
/**
* Retrieve account information
*
* @param account Public key of the account
*/
async getAccountInfo(
account: PublicKey,
commitment?: Commitment,
): Promise<AccountInfo> {
const info = await this.connection.getAccountInfo(account, commitment);
if (info === null) {
throw new Error(FAILED_TO_FIND_ACCOUNT);
}
if (!info.owner.equals(this.programId)) {
throw new Error(INVALID_ACCOUNT_OWNER);
}
if (info.data.length != AccountLayout.span) {
throw new Error(`Invalid account size`);
}
const data = Buffer.from(info.data);
const accountInfo = AccountLayout.decode(data);
accountInfo.address = account;
accountInfo.mint = new PublicKey(accountInfo.mint);
accountInfo.owner = new PublicKey(accountInfo.owner);
accountInfo.amount = u64.fromBuffer(accountInfo.amount);
if (accountInfo.delegateOption === 0) {
accountInfo.delegate = null;
accountInfo.delegatedAmount = new u64();
} else {
accountInfo.delegate = new PublicKey(accountInfo.delegate);
accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount);
}
accountInfo.isInitialized = accountInfo.state !== 0;
accountInfo.isFrozen = accountInfo.state === 2;
if (accountInfo.isNativeOption === 1) {
accountInfo.rentExemptReserve = u64.fromBuffer(accountInfo.isNative);
accountInfo.isNative = true;
} else {
accountInfo.rentExemptReserve = null;
accountInfo.isNative = false;
}
if (accountInfo.closeAuthorityOption === 0) {
accountInfo.closeAuthority = null;
} else {
accountInfo.closeAuthority = new PublicKey(accountInfo.closeAuthority);
}
if (!accountInfo.mint.equals(this.publicKey)) {
throw new Error(
`Invalid account mint: ${JSON.stringify(
accountInfo.mint,
)} !== ${JSON.stringify(this.publicKey)}`,
);
}
return accountInfo;
}
/**
* Retrieve Multisig information
*
* @param multisig Public key of the account
*/
async getMultisigInfo(multisig: PublicKey): Promise<MultisigInfo> {
const info = await this.connection.getAccountInfo(multisig);
if (info === null) {
throw new Error('Failed to find multisig');
}
if (!info.owner.equals(this.programId)) {
throw new Error(`Invalid multisig owner`);
}
if (info.data.length != MultisigLayout.span) {
throw new Error(`Invalid multisig size`);
}
const data = Buffer.from(info.data);
const multisigInfo = MultisigLayout.decode(data);
multisigInfo.signer1 = new PublicKey(multisigInfo.signer1);
multisigInfo.signer2 = new PublicKey(multisigInfo.signer2);
multisigInfo.signer3 = new PublicKey(multisigInfo.signer3);
multisigInfo.signer4 = new PublicKey(multisigInfo.signer4);
multisigInfo.signer5 = new PublicKey(multisigInfo.signer5);
multisigInfo.signer6 = new PublicKey(multisigInfo.signer6);
multisigInfo.signer7 = new PublicKey(multisigInfo.signer7);
multisigInfo.signer8 = new PublicKey(multisigInfo.signer8);
multisigInfo.signer9 = new PublicKey(multisigInfo.signer9);
multisigInfo.signer10 = new PublicKey(multisigInfo.signer10);
multisigInfo.signer11 = new PublicKey(multisigInfo.signer11);
return multisigInfo;
}
/**
* Transfer tokens to another account
*
* @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
*/
async transfer(
source: PublicKey,
destination: PublicKey,
owner: any,
multiSigners: Array<Account>,
amount: number | u64,
): Promise<TransactionSignature> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
return await sendAndConfirmTransaction(
'Transfer',
this.connection,
new Transaction().add(
Token.createTransferInstruction(
this.programId,
source,
destination,
ownerPublicKey,
multiSigners,
amount,
),
),
this.payer,
...signers,
);
}
/**
* Grant a third-party permission to transfer up the specified number of tokens from an account
*
* @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
*/
async approve(
account: PublicKey,
delegate: PublicKey,
owner: any,
multiSigners: Array<Account>,
amount: number | u64,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'Approve',
this.connection,
new Transaction().add(
Token.createApproveInstruction(
this.programId,
account,
delegate,
ownerPublicKey,
multiSigners,
amount,
),
),
this.payer,
...signers,
);
}
/**
* Remove approval for the transfer of any remaining tokens
*
* @param account Public key of the account
* @param owner Owner of the source account
* @param multiSigners Signing accounts if `owner` is a multiSig
*/
async revoke(
account: PublicKey,
owner: any,
multiSigners: Array<Account>,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'Revoke',
this.connection,
new Transaction().add(
Token.createRevokeInstruction(
this.programId,
account,
ownerPublicKey,
multiSigners,
),
),
this.payer,
...signers,
);
}
/**
* Assign a new authority to the account
*
* @param account Public key of the account
* @param newAuthority New authority of the account
* @param authorityType Type of authority to set
* @param currentAuthority Current authority of the account
* @param multiSigners Signing accounts if `currentAuthority` is a multiSig
*/
async setAuthority(
account: PublicKey,
newAuthority: PublicKey | null,
authorityType: AuthorityType,
currentAuthority: any,
multiSigners: Array<Account>,
): Promise<void> {
let currentAuthorityPublicKey: PublicKey;
let signers;
if (isAccount(currentAuthority)) {
currentAuthorityPublicKey = currentAuthority.publicKey;
signers = [currentAuthority];
} else {
currentAuthorityPublicKey = currentAuthority;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'SetAuthority',
this.connection,
new Transaction().add(
Token.createSetAuthorityInstruction(
this.programId,
account,
newAuthority,
authorityType,
currentAuthorityPublicKey,
multiSigners,
),
),
this.payer,
...signers,
);
}
/**
* Mint new tokens
*
* @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
*/
async mintTo(
dest: PublicKey,
authority: any,
multiSigners: Array<Account>,
amount: number | u64,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(authority)) {
ownerPublicKey = authority.publicKey;
signers = [authority];
} else {
ownerPublicKey = authority;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'MintTo',
this.connection,
new Transaction().add(
Token.createMintToInstruction(
this.programId,
this.publicKey,
dest,
ownerPublicKey,
multiSigners,
amount,
),
),
this.payer,
...signers,
);
}
/**
* Burn tokens
*
* @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
*/
async burn(
account: PublicKey,
owner: any,
multiSigners: Array<Account>,
amount: number | u64,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'Burn',
this.connection,
new Transaction().add(
Token.createBurnInstruction(
this.programId,
this.publicKey,
account,
ownerPublicKey,
multiSigners,
amount,
),
),
this.payer,
...signers,
);
}
/**
* Close account
*
* @param account Account to close
* @param dest Account to receive the remaining balance of the closed account
* @param authority Authority which is allowed to close the account
* @param multiSigners Signing accounts if `authority` is a multiSig
*/
async closeAccount(
account: PublicKey,
dest: PublicKey,
authority: any,
multiSigners: Array<Account>,
): Promise<void> {
let authorityPublicKey;
let signers;
if (isAccount(authority)) {
authorityPublicKey = authority.publicKey;
signers = [authority];
} else {
authorityPublicKey = authority;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'CloseAccount',
this.connection,
new Transaction().add(
Token.createCloseAccountInstruction(
this.programId,
account,
dest,
authorityPublicKey,
multiSigners,
),
),
this.payer,
...signers,
);
}
/**
* 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<Account>,
): Promise<void> {
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<Account>,
): Promise<void> {
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 transferChecked(
source: PublicKey,
destination: PublicKey,
owner: any,
multiSigners: Array<Account>,
amount: number | u64,
decimals: number,
): Promise<TransactionSignature> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
return await sendAndConfirmTransaction(
'TransferChecked',
this.connection,
new Transaction().add(
Token.createTransferCheckedInstruction(
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 approveChecked(
account: PublicKey,
delegate: PublicKey,
owner: any,
multiSigners: Array<Account>,
amount: number | u64,
decimals: number,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'ApproveChecked',
this.connection,
new Transaction().add(
Token.createApproveCheckedInstruction(
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 mintToChecked(
dest: PublicKey,
authority: any,
multiSigners: Array<Account>,
amount: number | u64,
decimals: number,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(authority)) {
ownerPublicKey = authority.publicKey;
signers = [authority];
} else {
ownerPublicKey = authority;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'MintToChecked',
this.connection,
new Transaction().add(
Token.createMintToCheckedInstruction(
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 burnChecked(
account: PublicKey,
owner: any,
multiSigners: Array<Account>,
amount: number | u64,
decimals: number,
): Promise<void> {
let ownerPublicKey;
let signers;
if (isAccount(owner)) {
ownerPublicKey = owner.publicKey;
signers = [owner];
} else {
ownerPublicKey = owner;
signers = multiSigners;
}
await sendAndConfirmTransaction(
'BurnChecked',
this.connection,
new Transaction().add(
Token.createBurnCheckedInstruction(
this.programId,
this.publicKey,
account,
ownerPublicKey,
multiSigners,
amount,
decimals,
),
),
this.payer,
...signers,
);
}
/**
* Construct an InitializeMint instruction
*
* @param programId SPL Token program account
* @param mint Token mint 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,
mint: PublicKey,
decimals: number,
mintAuthority: PublicKey,
freezeAuthority: PublicKey | null,
): TransactionInstruction {
let keys = [
{pubkey: mint, isSigner: false, isWritable: true},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
];
const commandDataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
BufferLayout.u8('decimals'),
Layout.publicKey('mintAuthority'),
BufferLayout.u8('option'),
Layout.publicKey('freezeAuthority'),
]);
let data = Buffer.alloc(1024);
{
const encodeLength = commandDataLayout.encode(
{
instruction: 0, // InitializeMint instruction
decimals,
mintAuthority: pubkeyToBuffer(mintAuthority),
option: freezeAuthority === null ? 0 : 1,
freezeAuthority: pubkeyToBuffer(freezeAuthority || new PublicKey(0)),
},
data,
);
data = data.slice(0, encodeLength);
}
return new TransactionInstruction({
keys,
programId,
data,
});
}
/**
* Construct an InitializeAccount instruction
*
* @param programId SPL Token program account
* @param mint Token mint account
* @param account New account
* @param owner Owner of the new account
*/
static createInitAccountInstruction(
programId: PublicKey,
mint: PublicKey,
account: PublicKey,
owner: PublicKey,
): TransactionInstruction {
const keys = [
{pubkey: account, isSigner: false, isWritable: true},
{pubkey: mint, isSigner: false, isWritable: false},
{pubkey: owner, isSigner: false, isWritable: false},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
];
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 1, // InitializeAccount instruction
},
data,
);
return new TransactionInstruction({
keys,
programId,
data,
});
}
/**
* 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
* @param multiSigners Signing accounts if `authority` is a multiSig
* @param amount Number of tokens to transfer
*/
static createTransferInstruction(
programId: PublicKey,
source: PublicKey,
destination: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
amount: number | u64,
): TransactionInstruction {
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
Layout.uint64('amount'),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 3, // Transfer instruction
amount: new u64(amount).toBuffer(),
},
data,
);
let keys = [
{pubkey: source, isSigner: false, isWritable: true},
{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 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
* @param multiSigners Signing accounts if `owner` is a multiSig
* @param amount Maximum number of tokens the delegate may transfer
*/
static createApproveInstruction(
programId: PublicKey,
account: PublicKey,
delegate: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
amount: number | u64,
): TransactionInstruction {
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
Layout.uint64('amount'),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 4, // Approve instruction
amount: new u64(amount).toBuffer(),
},
data,
);
let keys = [
{pubkey: account, isSigner: false, isWritable: true},
{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 Revoke instruction
*
* @param programId SPL Token program account
* @param account Public key of the account
* @param owner Owner of the source account
* @param multiSigners Signing accounts if `owner` is a multiSig
*/
static createRevokeInstruction(
programId: PublicKey,
account: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
): TransactionInstruction {
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 5, // Approve instruction
},
data,
);
let keys = [{pubkey: account, 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 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
* @param currentAuthority Current authority of the specified type
* @param multiSigners Signing accounts if `currentAuthority` is a multiSig
*/
static createSetAuthorityInstruction(
programId: PublicKey,
account: PublicKey,
newAuthority: PublicKey | null,
authorityType: AuthorityType,
currentAuthority: PublicKey,
multiSigners: Array<Account>,
): TransactionInstruction {
const commandDataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
BufferLayout.u8('authorityType'),
BufferLayout.u8('option'),
Layout.publicKey('newAuthority'),
]);
let data = Buffer.alloc(1024);
{
const encodeLength = commandDataLayout.encode(
{
instruction: 6, // SetAuthority instruction
authorityType: AuthorityTypeCodes[authorityType],
option: newAuthority === null ? 0 : 1,
newAuthority: pubkeyToBuffer(newAuthority || new PublicKey(0)),
},
data,
);
data = data.slice(0, encodeLength);
}
let keys = [{pubkey: account, isSigner: false, isWritable: true}];
if (multiSigners.length === 0) {
keys.push({pubkey: currentAuthority, isSigner: true, isWritable: false});
} else {
keys.push({pubkey: currentAuthority, 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 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
*/
static createMintToInstruction(
programId: PublicKey,
mint: PublicKey,
dest: PublicKey,
authority: PublicKey,
multiSigners: Array<Account>,
amount: number | u64,
): TransactionInstruction {
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
Layout.uint64('amount'),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 7, // MintTo instruction
amount: new u64(amount).toBuffer(),
},
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 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
* @param multiSigners Signing accounts if `authority` is a multiSig
* @param amount amount to burn
*/
static createBurnInstruction(
programId: PublicKey,
mint: PublicKey,
account: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
amount: number | u64,
): TransactionInstruction {
const dataLayout = BufferLayout.struct([
BufferLayout.u8('instruction'),
Layout.uint64('amount'),
]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 8, // Burn instruction
amount: new u64(amount).toBuffer(),
},
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,
});
}
/**
* Construct a Close instruction
*
* @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(
programId: PublicKey,
account: PublicKey,
dest: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
): TransactionInstruction {
const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]);
const data = Buffer.alloc(dataLayout.span);
dataLayout.encode(
{
instruction: 9, // CloseAccount instruction
},
data,
);
let keys = [
{pubkey: account, isSigner: false, isWritable: true},
{pubkey: dest, 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 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<Account>,
): 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<Account>,
): 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 TransferChecked 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 createTransferCheckedInstruction(
programId: PublicKey,
source: PublicKey,
mint: PublicKey,
destination: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
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, // TransferChecked 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 ApproveChecked 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 createApproveCheckedInstruction(
programId: PublicKey,
account: PublicKey,
mint: PublicKey,
delegate: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
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, // ApproveChecked 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 MintToChecked 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 createMintToCheckedInstruction(
programId: PublicKey,
mint: PublicKey,
dest: PublicKey,
authority: PublicKey,
multiSigners: Array<Account>,
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: 14, // MintToChecked 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 BurnChecked 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 createBurnCheckedInstruction(
programId: PublicKey,
mint: PublicKey,
account: PublicKey,
owner: PublicKey,
multiSigners: Array<Account>,
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: 15, // BurnChecked 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,
});
}
/**
* Get the address for the associated token account
*
* @param associatedProgramId SPL Associated Token program account
* @param programId SPL Token program account
* @param mint Token mint account
* @param owner Owner of the new account
* @return Public key of the associated token account
*/
static async getAssociatedTokenAddress(
associatedProgramId: PublicKey,
programId: PublicKey,
mint: PublicKey,
owner: PublicKey,
): Promise<PublicKey> {
return (
await PublicKey.findProgramAddress(
[owner.toBuffer(), programId.toBuffer(), mint.toBuffer()],
associatedProgramId,
)
)[0];
}
/**
* Construct the AssociatedTokenProgram instruction to create the associated
* token account
*
* @param associatedProgramId SPL Associated Token program account
* @param programId SPL Token program account
* @param mint Token mint account
* @param associatedAccount New associated account
* @param owner Owner of the new account
* @param payer Payer of fees
*/
static createAssociatedTokenAccountInstruction(
associatedProgramId: PublicKey,
programId: PublicKey,
mint: PublicKey,
associatedAccount: PublicKey,
owner: PublicKey,
payer: PublicKey,
): TransactionInstruction {
const data = Buffer.alloc(0);
let keys = [
{pubkey: payer, isSigner: true, isWritable: true},
{pubkey: associatedAccount, isSigner: false, isWritable: true},
{pubkey: owner, isSigner: false, isWritable: false},
{pubkey: mint, isSigner: false, isWritable: false},
{pubkey: SystemProgram.programId, isSigner: false, isWritable: false},
{pubkey: programId, isSigner: false, isWritable: false},
{pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false},
];
return new TransactionInstruction({
keys,
programId: associatedProgramId,
data,
});
}
}