fix: token program moved to its own repo (#502)
This commit is contained in:
parent
c31ca1fc1b
commit
b4922e8aef
@ -233,92 +233,6 @@ declare module '@solana/web3.js' {
|
|||||||
serialize(): Buffer;
|
serialize(): Buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === src/token-program.js ===
|
|
||||||
declare export class TokenAmount extends BN {
|
|
||||||
toBuffer(): Buffer;
|
|
||||||
fromBuffer(buffer: Buffer): TokenAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare export type TokenInfo = {|
|
|
||||||
supply: TokenAmount,
|
|
||||||
decimals: number,
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
|};
|
|
||||||
declare export type TokenAccountInfo = {|
|
|
||||||
token: PublicKey,
|
|
||||||
owner: PublicKey,
|
|
||||||
amount: TokenAmount,
|
|
||||||
source: null | PublicKey,
|
|
||||||
originalAmount: TokenAmount,
|
|
||||||
|};
|
|
||||||
declare type TokenAndPublicKey = [Token, PublicKey];
|
|
||||||
|
|
||||||
declare export class Token {
|
|
||||||
programId: PublicKey;
|
|
||||||
token: PublicKey;
|
|
||||||
|
|
||||||
static createNewToken(
|
|
||||||
connection: Connection,
|
|
||||||
owner: Account,
|
|
||||||
supply: TokenAmount,
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
decimals: number,
|
|
||||||
programId?: PublicKey,
|
|
||||||
): Promise<TokenAndPublicKey>;
|
|
||||||
|
|
||||||
constructor(connection: Connection, token: PublicKey): Token;
|
|
||||||
newAccount(owner: Account, source?: PublicKey): Promise<PublicKey>;
|
|
||||||
tokenInfo(): Promise<TokenInfo>;
|
|
||||||
accountInfo(account: PublicKey): Promise<TokenAccountInfo>;
|
|
||||||
transfer(
|
|
||||||
owner: Account,
|
|
||||||
source: PublicKey,
|
|
||||||
destination: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): Promise<TransactionSignature>;
|
|
||||||
approve(
|
|
||||||
owner: Account,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): Promise<void>;
|
|
||||||
revoke(
|
|
||||||
owner: Account,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
): Promise<void>;
|
|
||||||
setOwner(
|
|
||||||
owner: Account,
|
|
||||||
account: PublicKey,
|
|
||||||
newOwner: PublicKey,
|
|
||||||
): Promise<void>;
|
|
||||||
|
|
||||||
transferInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
source: PublicKey,
|
|
||||||
destination: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): Promise<TransactionInstruction>;
|
|
||||||
approveInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): TransactionInstruction;
|
|
||||||
revokeInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
): TransactionInstruction;
|
|
||||||
setOwnerInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
account: PublicKey,
|
|
||||||
newOwner: PublicKey,
|
|
||||||
): TransactionInstruction;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === src/loader.js ===
|
// === src/loader.js ===
|
||||||
declare export class Loader {
|
declare export class Loader {
|
||||||
static load(
|
static load(
|
||||||
|
919
web3.js/package-lock.json
generated
919
web3.js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,6 @@ export {Connection} from './connection';
|
|||||||
export {Loader} from './loader';
|
export {Loader} from './loader';
|
||||||
export {PublicKey} from './publickey';
|
export {PublicKey} from './publickey';
|
||||||
export {SystemInstruction, SystemProgram} from './system-program';
|
export {SystemInstruction, SystemProgram} from './system-program';
|
||||||
export {Token, TokenAmount} from './token-program';
|
|
||||||
export {Transaction, TransactionInstruction} from './transaction';
|
export {Transaction, TransactionInstruction} from './transaction';
|
||||||
export {VALIDATOR_INFO_KEY, ValidatorInfo} from './validator-info';
|
export {VALIDATOR_INFO_KEY, ValidatorInfo} from './validator-info';
|
||||||
export {VOTE_ACCOUNT_KEY, VoteAccount} from './vote-account';
|
export {VOTE_ACCOUNT_KEY, VoteAccount} from './vote-account';
|
||||||
|
@ -1,603 +0,0 @@
|
|||||||
/**
|
|
||||||
* @flow
|
|
||||||
*/
|
|
||||||
|
|
||||||
import assert from 'assert';
|
|
||||||
import BN from 'bn.js';
|
|
||||||
import * as BufferLayout from 'buffer-layout';
|
|
||||||
|
|
||||||
import * as Layout from './layout';
|
|
||||||
import {Account} from './account';
|
|
||||||
import {PublicKey} from './publickey';
|
|
||||||
import {SystemProgram} from './system-program';
|
|
||||||
import {Transaction, TransactionInstruction} from './transaction';
|
|
||||||
import type {TransactionSignature} from './transaction';
|
|
||||||
import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction';
|
|
||||||
import type {Connection} from './connection';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Some amount of tokens
|
|
||||||
*/
|
|
||||||
export class TokenAmount extends BN {
|
|
||||||
/**
|
|
||||||
* Convert to Buffer representation
|
|
||||||
*/
|
|
||||||
toBuffer(): Buffer {
|
|
||||||
const a = super.toArray().reverse();
|
|
||||||
const b = Buffer.from(a);
|
|
||||||
if (b.length === 8) {
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
assert(b.length < 8, 'TokenAmount too large');
|
|
||||||
|
|
||||||
const zeroPad = Buffer.alloc(8);
|
|
||||||
b.copy(zeroPad);
|
|
||||||
return zeroPad;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a TokenAmount from Buffer representation
|
|
||||||
*/
|
|
||||||
static fromBuffer(buffer: Buffer): TokenAmount {
|
|
||||||
assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`);
|
|
||||||
return new BN(
|
|
||||||
[...buffer]
|
|
||||||
.reverse()
|
|
||||||
.map(i => `00${i.toString(16)}`.slice(-2))
|
|
||||||
.join(''),
|
|
||||||
16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Information about a token
|
|
||||||
*/
|
|
||||||
type TokenInfo = {|
|
|
||||||
/**
|
|
||||||
* Total supply of tokens
|
|
||||||
*/
|
|
||||||
supply: TokenAmount,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of base 10 digits to the right of the decimal place
|
|
||||||
*/
|
|
||||||
decimals: number,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Descriptive name of this token
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Symbol for this token
|
|
||||||
*/
|
|
||||||
symbol: string,
|
|
||||||
|};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
const TokenInfoLayout = BufferLayout.struct([
|
|
||||||
Layout.uint64('supply'),
|
|
||||||
BufferLayout.u8('decimals'),
|
|
||||||
Layout.rustString('name'),
|
|
||||||
Layout.rustString('symbol'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Information about a token account
|
|
||||||
*/
|
|
||||||
type TokenAccountInfo = {|
|
|
||||||
/**
|
|
||||||
* The kind of token this account holds
|
|
||||||
*/
|
|
||||||
token: PublicKey,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Owner of this account
|
|
||||||
*/
|
|
||||||
owner: PublicKey,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount of tokens this account holds
|
|
||||||
*/
|
|
||||||
amount: TokenAmount,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The source account for the tokens.
|
|
||||||
*
|
|
||||||
* If `source` is null, the source is this account.
|
|
||||||
* If `source` is not null, the `amount` of tokens in this account represent
|
|
||||||
* an allowance of tokens that may be transferred from the source account
|
|
||||||
*/
|
|
||||||
source: null | PublicKey,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Original amount of tokens this delegate account was authorized to spend
|
|
||||||
* If `source` is null, originalAmount is zero
|
|
||||||
*/
|
|
||||||
originalAmount: TokenAmount,
|
|
||||||
|};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
const TokenAccountInfoLayout = BufferLayout.struct([
|
|
||||||
Layout.publicKey('token'),
|
|
||||||
Layout.publicKey('owner'),
|
|
||||||
Layout.uint64('amount'),
|
|
||||||
BufferLayout.u8('sourceOption'),
|
|
||||||
Layout.publicKey('source'),
|
|
||||||
Layout.uint64('originalAmount'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
type TokenAndPublicKey = [Token, PublicKey]; // This type exists to workaround an esdoc parse error
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The built-in token program
|
|
||||||
*/
|
|
||||||
export const SYSTEM_TOKEN_PROGRAM_ID = new PublicKey(
|
|
||||||
'Token11111111111111111111111111111111111111',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ERC20-like Token
|
|
||||||
*/
|
|
||||||
export class Token {
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
connection: Connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The public key identifying this token
|
|
||||||
*/
|
|
||||||
token: PublicKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Program Identifier for the Token program
|
|
||||||
*/
|
|
||||||
programId: PublicKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Token object attached to the specific token
|
|
||||||
*
|
|
||||||
* @param connection The connection to use
|
|
||||||
* @param token Public key of the token
|
|
||||||
* @param programId Optional token programId, uses the system programId by default
|
|
||||||
*/
|
|
||||||
constructor(
|
|
||||||
connection: Connection,
|
|
||||||
token: PublicKey,
|
|
||||||
programId: PublicKey = SYSTEM_TOKEN_PROGRAM_ID,
|
|
||||||
) {
|
|
||||||
Object.assign(this, {connection, token, programId});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Token
|
|
||||||
*
|
|
||||||
* @param connection The connection to use
|
|
||||||
* @param owner User account that will own the returned Token Account
|
|
||||||
* @param supply Total supply of the new token
|
|
||||||
* @param name Descriptive name of this token
|
|
||||||
* @param symbol Symbol for this token
|
|
||||||
* @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 Token Account holding the total supply of new tokens
|
|
||||||
*/
|
|
||||||
static async createNewToken(
|
|
||||||
connection: Connection,
|
|
||||||
owner: Account,
|
|
||||||
supply: TokenAmount,
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
decimals: number,
|
|
||||||
programId: PublicKey = SYSTEM_TOKEN_PROGRAM_ID,
|
|
||||||
): Promise<TokenAndPublicKey> {
|
|
||||||
const tokenAccount = new Account();
|
|
||||||
const token = new Token(connection, tokenAccount.publicKey, programId);
|
|
||||||
const initialAccountPublicKey = await token.newAccount(owner, null);
|
|
||||||
|
|
||||||
let transaction;
|
|
||||||
|
|
||||||
const dataLayout = BufferLayout.struct([
|
|
||||||
BufferLayout.u32('instruction'),
|
|
||||||
Layout.uint64('supply'),
|
|
||||||
BufferLayout.u8('decimals'),
|
|
||||||
Layout.rustString('name'),
|
|
||||||
Layout.rustString('symbol'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let data = Buffer.alloc(1024);
|
|
||||||
{
|
|
||||||
const encodeLength = dataLayout.encode(
|
|
||||||
{
|
|
||||||
instruction: 0, // NewToken instruction
|
|
||||||
supply: supply.toBuffer(),
|
|
||||||
decimals,
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
data = data.slice(0, encodeLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate memory for the tokenAccount account
|
|
||||||
transaction = SystemProgram.createAccount(
|
|
||||||
owner.publicKey,
|
|
||||||
tokenAccount.publicKey,
|
|
||||||
1,
|
|
||||||
1 + data.length,
|
|
||||||
programId,
|
|
||||||
);
|
|
||||||
await sendAndConfirmTransaction(connection, transaction, owner);
|
|
||||||
|
|
||||||
transaction = new Transaction().add({
|
|
||||||
keys: [
|
|
||||||
{pubkey: tokenAccount.publicKey, isSigner: true, isDebitable: false},
|
|
||||||
{pubkey: initialAccountPublicKey, isSigner: false, isDebitable: true},
|
|
||||||
],
|
|
||||||
programId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
await sendAndConfirmTransaction(
|
|
||||||
connection,
|
|
||||||
transaction,
|
|
||||||
owner,
|
|
||||||
tokenAccount,
|
|
||||||
);
|
|
||||||
|
|
||||||
return [token, initialAccountPublicKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new and empty token account.
|
|
||||||
*
|
|
||||||
* This account may then be used as a `transfer()` or `approve()` destination
|
|
||||||
*
|
|
||||||
* @param owner User account that will own the new token account
|
|
||||||
* @param source If not null, create a delegate account that when authorized
|
|
||||||
* may transfer tokens from this `source` account
|
|
||||||
* @return Public key of the new empty token account
|
|
||||||
*/
|
|
||||||
async newAccount(
|
|
||||||
owner: Account,
|
|
||||||
source: null | PublicKey = null,
|
|
||||||
): Promise<PublicKey> {
|
|
||||||
const tokenAccount = new Account();
|
|
||||||
let transaction;
|
|
||||||
|
|
||||||
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
|
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
|
||||||
dataLayout.encode(
|
|
||||||
{
|
|
||||||
instruction: 1, // NewTokenAccount instruction
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Allocate memory for the token
|
|
||||||
transaction = SystemProgram.createAccount(
|
|
||||||
owner.publicKey,
|
|
||||||
tokenAccount.publicKey,
|
|
||||||
1,
|
|
||||||
1 + TokenAccountInfoLayout.span,
|
|
||||||
this.programId,
|
|
||||||
);
|
|
||||||
await sendAndConfirmTransaction(this.connection, transaction, owner);
|
|
||||||
|
|
||||||
// Initialize the token account
|
|
||||||
const keys = [
|
|
||||||
{pubkey: tokenAccount.publicKey, isSigner: true, isDebitable: true},
|
|
||||||
{pubkey: owner.publicKey, isSigner: false, isDebitable: false},
|
|
||||||
{pubkey: this.token, isSigner: false, isDebitable: false},
|
|
||||||
];
|
|
||||||
if (source) {
|
|
||||||
keys.push({pubkey: source, isSigner: false, isDebitable: false});
|
|
||||||
}
|
|
||||||
transaction = new Transaction().add({
|
|
||||||
keys,
|
|
||||||
programId: this.programId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
await sendAndConfirmTransaction(
|
|
||||||
this.connection,
|
|
||||||
transaction,
|
|
||||||
owner,
|
|
||||||
tokenAccount,
|
|
||||||
);
|
|
||||||
|
|
||||||
return tokenAccount.publicKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve token information
|
|
||||||
*/
|
|
||||||
async tokenInfo(): Promise<TokenInfo> {
|
|
||||||
const accountInfo = await this.connection.getAccountInfo(this.token);
|
|
||||||
if (!accountInfo.owner.equals(this.programId)) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid token owner: ${JSON.stringify(accountInfo.owner)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = Buffer.from(accountInfo.data);
|
|
||||||
|
|
||||||
if (data.readUInt8(0) !== 1) {
|
|
||||||
throw new Error(`Invalid token data`);
|
|
||||||
}
|
|
||||||
const tokenInfo = TokenInfoLayout.decode(data, 1);
|
|
||||||
tokenInfo.supply = TokenAmount.fromBuffer(tokenInfo.supply);
|
|
||||||
return tokenInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve account information
|
|
||||||
*
|
|
||||||
* @param account Public key of the token account
|
|
||||||
*/
|
|
||||||
async accountInfo(account: PublicKey): Promise<TokenAccountInfo> {
|
|
||||||
const accountInfo = await this.connection.getAccountInfo(account);
|
|
||||||
if (!accountInfo.owner.equals(this.programId)) {
|
|
||||||
throw new Error(`Invalid token account owner`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = Buffer.from(accountInfo.data);
|
|
||||||
if (data.readUInt8(0) !== 2) {
|
|
||||||
throw new Error(`Invalid token account data`);
|
|
||||||
}
|
|
||||||
const tokenAccountInfo = TokenAccountInfoLayout.decode(data, 1);
|
|
||||||
|
|
||||||
tokenAccountInfo.token = new PublicKey(tokenAccountInfo.token);
|
|
||||||
tokenAccountInfo.owner = new PublicKey(tokenAccountInfo.owner);
|
|
||||||
tokenAccountInfo.amount = TokenAmount.fromBuffer(tokenAccountInfo.amount);
|
|
||||||
if (tokenAccountInfo.sourceOption === 0) {
|
|
||||||
tokenAccountInfo.source = null;
|
|
||||||
tokenAccountInfo.originalAmount = new TokenAmount();
|
|
||||||
} else {
|
|
||||||
tokenAccountInfo.source = new PublicKey(tokenAccountInfo.source);
|
|
||||||
tokenAccountInfo.originalAmount = TokenAmount.fromBuffer(
|
|
||||||
tokenAccountInfo.originalAmount,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tokenAccountInfo.token.equals(this.token)) {
|
|
||||||
throw new Error(
|
|
||||||
`Invalid token account token: ${JSON.stringify(
|
|
||||||
tokenAccountInfo.token,
|
|
||||||
)} !== ${JSON.stringify(this.token)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return tokenAccountInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transfer tokens to another account
|
|
||||||
*
|
|
||||||
* @param owner Owner of the source token account
|
|
||||||
* @param source Source token account
|
|
||||||
* @param destination Destination token account
|
|
||||||
* @param amount Number of tokens to transfer
|
|
||||||
*/
|
|
||||||
async transfer(
|
|
||||||
owner: Account,
|
|
||||||
source: PublicKey,
|
|
||||||
destination: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): Promise<?TransactionSignature> {
|
|
||||||
return await sendAndConfirmTransaction(
|
|
||||||
this.connection,
|
|
||||||
new Transaction().add(
|
|
||||||
await this.transferInstruction(
|
|
||||||
owner.publicKey,
|
|
||||||
source,
|
|
||||||
destination,
|
|
||||||
amount,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
owner,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grant a third-party permission to transfer up the specified number of tokens from an account
|
|
||||||
*
|
|
||||||
* @param owner Owner of the source token account
|
|
||||||
* @param account Public key of the token account
|
|
||||||
* @param delegate Token account authorized to perform a transfer tokens from the source account
|
|
||||||
* @param amount Maximum number of tokens the delegate may transfer
|
|
||||||
*/
|
|
||||||
async approve(
|
|
||||||
owner: Account,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAndConfirmTransaction(
|
|
||||||
this.connection,
|
|
||||||
new Transaction().add(
|
|
||||||
this.approveInstruction(owner.publicKey, account, delegate, amount),
|
|
||||||
),
|
|
||||||
owner,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove approval for the transfer of any remaining tokens
|
|
||||||
*
|
|
||||||
* @param owner Owner of the source token account
|
|
||||||
* @param account Public key of the token account
|
|
||||||
* @param delegate Token account to revoke authorization from
|
|
||||||
*/
|
|
||||||
revoke(
|
|
||||||
owner: Account,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
): Promise<void> {
|
|
||||||
return this.approve(owner, account, delegate, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign a new owner to the account
|
|
||||||
*
|
|
||||||
* @param owner Owner of the token account
|
|
||||||
* @param account Public key of the token account
|
|
||||||
* @param newOwner New owner of the token account
|
|
||||||
*/
|
|
||||||
async setOwner(
|
|
||||||
owner: Account,
|
|
||||||
account: PublicKey,
|
|
||||||
newOwner: PublicKey,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAndConfirmTransaction(
|
|
||||||
this.connection,
|
|
||||||
new Transaction().add(
|
|
||||||
this.setOwnerInstruction(owner.publicKey, account, newOwner),
|
|
||||||
),
|
|
||||||
owner,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a Transfer instruction
|
|
||||||
*
|
|
||||||
* @param owner Owner of the source token account
|
|
||||||
* @param source Source token account
|
|
||||||
* @param destination Destination token account
|
|
||||||
* @param amount Number of tokens to transfer
|
|
||||||
*/
|
|
||||||
async transferInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
source: PublicKey,
|
|
||||||
destination: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): Promise<TransactionInstruction> {
|
|
||||||
const accountInfo = await this.accountInfo(source);
|
|
||||||
if (!owner.equals(accountInfo.owner)) {
|
|
||||||
throw new Error('Account owner mismatch');
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataLayout = BufferLayout.struct([
|
|
||||||
BufferLayout.u32('instruction'),
|
|
||||||
Layout.uint64('amount'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
|
||||||
dataLayout.encode(
|
|
||||||
{
|
|
||||||
instruction: 2, // Transfer instruction
|
|
||||||
amount: new TokenAmount(amount).toBuffer(),
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
const keys = [
|
|
||||||
{pubkey: owner, isSigner: true, isDebitable: false},
|
|
||||||
{pubkey: source, isSigner: false, isDebitable: true},
|
|
||||||
{pubkey: destination, isSigner: false, isDebitable: true},
|
|
||||||
];
|
|
||||||
if (accountInfo.source) {
|
|
||||||
keys.push({
|
|
||||||
pubkey: accountInfo.source,
|
|
||||||
isSigner: false,
|
|
||||||
isDebitable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return new TransactionInstruction({
|
|
||||||
keys,
|
|
||||||
programId: this.programId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an Approve instruction
|
|
||||||
*
|
|
||||||
* @param owner Owner of the source token account
|
|
||||||
* @param account Public key of the token account
|
|
||||||
* @param delegate Token account authorized to perform a transfer tokens from the source account
|
|
||||||
* @param amount Maximum number of tokens the delegate may transfer
|
|
||||||
*/
|
|
||||||
approveInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
amount: number | TokenAmount,
|
|
||||||
): TransactionInstruction {
|
|
||||||
const dataLayout = BufferLayout.struct([
|
|
||||||
BufferLayout.u32('instruction'),
|
|
||||||
Layout.uint64('amount'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
|
||||||
dataLayout.encode(
|
|
||||||
{
|
|
||||||
instruction: 3, // Approve instruction
|
|
||||||
amount: new TokenAmount(amount).toBuffer(),
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
return new TransactionInstruction({
|
|
||||||
keys: [
|
|
||||||
{pubkey: owner, isSigner: true, isDebitable: false},
|
|
||||||
{pubkey: account, isSigner: false, isDebitable: true},
|
|
||||||
{pubkey: delegate, isSigner: false, isDebitable: true},
|
|
||||||
],
|
|
||||||
programId: this.programId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct an Revoke instruction
|
|
||||||
*
|
|
||||||
* @param owner Owner of the source token account
|
|
||||||
* @param account Public key of the token account
|
|
||||||
* @param delegate Token account authorized to perform a transfer tokens from the source account
|
|
||||||
*/
|
|
||||||
revokeInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
account: PublicKey,
|
|
||||||
delegate: PublicKey,
|
|
||||||
): TransactionInstruction {
|
|
||||||
return this.approveInstruction(owner, account, delegate, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a SetOwner instruction
|
|
||||||
*
|
|
||||||
* @param owner Owner of the token account
|
|
||||||
* @param account Public key of the token account
|
|
||||||
* @param newOwner New owner of the token account
|
|
||||||
*/
|
|
||||||
setOwnerInstruction(
|
|
||||||
owner: PublicKey,
|
|
||||||
account: PublicKey,
|
|
||||||
newOwner: PublicKey,
|
|
||||||
): TransactionInstruction {
|
|
||||||
const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]);
|
|
||||||
|
|
||||||
const data = Buffer.alloc(dataLayout.span);
|
|
||||||
dataLayout.encode(
|
|
||||||
{
|
|
||||||
instruction: 4, // SetOwner instruction
|
|
||||||
},
|
|
||||||
data,
|
|
||||||
);
|
|
||||||
|
|
||||||
return new TransactionInstruction({
|
|
||||||
keys: [
|
|
||||||
{pubkey: owner, isSigner: true, isDebitable: false},
|
|
||||||
{pubkey: account, isSigner: false, isDebitable: true},
|
|
||||||
{pubkey: newOwner, isSigner: false, isDebitable: true},
|
|
||||||
],
|
|
||||||
programId: this.programId,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,609 +0,0 @@
|
|||||||
// @flow
|
|
||||||
|
|
||||||
import {Connection, PublicKey, Token, TokenAmount} from '../src';
|
|
||||||
import {SYSTEM_TOKEN_PROGRAM_ID} from '../src/token-program';
|
|
||||||
import {mockRpc, mockRpcEnabled} from './__mocks__/node-fetch';
|
|
||||||
import {url} from './url';
|
|
||||||
import {newAccountWithLamports} from './new-account-with-lamports';
|
|
||||||
import {mockGetRecentBlockhash} from './mockrpc/get-recent-blockhash';
|
|
||||||
import {sleep} from '../src/util/sleep';
|
|
||||||
|
|
||||||
// The default of 5 seconds is too slow for live testing sometimes
|
|
||||||
jest.setTimeout(60000);
|
|
||||||
|
|
||||||
function mockGetSignatureStatus(result: Object = {Ok: null}) {
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getSignatureStatus',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
function mockSendTransaction() {
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'sendTransaction',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result:
|
|
||||||
'3WE5w4B7v59x6qjyC4FbG2FEKYKQfvsJwqSxNVmtMjT8TQ31hsZieDHcSgqzxiAoTL56n2w5TncjqEKjLhtF4Vk',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A token created by the first test and used by all subsequent tests
|
|
||||||
let testToken: Token;
|
|
||||||
|
|
||||||
// Initial owner of the token supply
|
|
||||||
let initialOwner;
|
|
||||||
let initialOwnerTokenAccount: PublicKey;
|
|
||||||
|
|
||||||
test('create new token', async () => {
|
|
||||||
const connection = new Connection(url);
|
|
||||||
connection._disableBlockhashCaching = mockRpcEnabled;
|
|
||||||
|
|
||||||
initialOwner = await newAccountWithLamports(connection, 1024);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock SystemProgram.createAccount transaction for Token.createNewToken()
|
|
||||||
mockGetRecentBlockhash();
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
|
|
||||||
// mock Token.newAccount() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus(null);
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
|
|
||||||
// mock SystemProgram.createAccount transaction for Token.createNewToken()
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
|
|
||||||
// mock Token.createNewToken() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus(null);
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
[testToken, initialOwnerTokenAccount] = await Token.createNewToken(
|
|
||||||
connection,
|
|
||||||
initialOwner,
|
|
||||||
new TokenAmount(10000),
|
|
||||||
'Test token',
|
|
||||||
'TEST',
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.tokenInfo()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [testToken.token.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
1,
|
|
||||||
16,
|
|
||||||
39,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
10,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
84,
|
|
||||||
101,
|
|
||||||
115,
|
|
||||||
116,
|
|
||||||
32,
|
|
||||||
116,
|
|
||||||
111,
|
|
||||||
107,
|
|
||||||
101,
|
|
||||||
110,
|
|
||||||
4,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
84,
|
|
||||||
69,
|
|
||||||
83,
|
|
||||||
84,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenInfo = await testToken.tokenInfo();
|
|
||||||
|
|
||||||
expect(tokenInfo.supply.toNumber()).toBe(10000);
|
|
||||||
expect(tokenInfo.decimals).toBe(2);
|
|
||||||
expect(tokenInfo.name).toBe('Test token');
|
|
||||||
expect(tokenInfo.symbol).toBe('TEST');
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.accountInfo()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [initialOwnerTokenAccount.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
2,
|
|
||||||
...testToken.token.toBuffer(),
|
|
||||||
...initialOwner.publicKey.toBuffer(),
|
|
||||||
16,
|
|
||||||
39,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountInfo = await testToken.accountInfo(initialOwnerTokenAccount);
|
|
||||||
|
|
||||||
expect(accountInfo.token.equals(testToken.token)).toBe(true);
|
|
||||||
expect(accountInfo.owner.equals(initialOwner.publicKey)).toBe(true);
|
|
||||||
expect(accountInfo.amount.toNumber()).toBe(10000);
|
|
||||||
expect(accountInfo.source).toBe(null);
|
|
||||||
expect(accountInfo.originalAmount.toNumber()).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('create new token account', async () => {
|
|
||||||
const connection = new Connection(url);
|
|
||||||
connection._disableBlockhashCaching = mockRpcEnabled;
|
|
||||||
const destOwner = await newAccountWithLamports(connection);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock SystemProgram.createAccount transaction for Token.newAccount()
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
|
|
||||||
// mock Token.newAccount() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
const dest = await testToken.newAccount(destOwner);
|
|
||||||
{
|
|
||||||
// mock Token.accountInfo()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [dest.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
2,
|
|
||||||
...testToken.token.toBuffer(),
|
|
||||||
...destOwner.publicKey.toBuffer(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accountInfo = await testToken.accountInfo(dest);
|
|
||||||
|
|
||||||
expect(accountInfo.token.equals(testToken.token)).toBe(true);
|
|
||||||
expect(accountInfo.owner.equals(destOwner.publicKey)).toBe(true);
|
|
||||||
expect(accountInfo.amount.toNumber()).toBe(0);
|
|
||||||
expect(accountInfo.source).toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('transfer', async () => {
|
|
||||||
const connection = new Connection(url);
|
|
||||||
connection._disableBlockhashCaching = mockRpcEnabled;
|
|
||||||
const destOwner = await newAccountWithLamports(connection);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock SystemProgram.createAccount transaction for Token.newAccount()
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
|
|
||||||
// mock Token.newAccount() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
const dest = await testToken.newAccount(destOwner);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.transfer()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [initialOwnerTokenAccount.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
2,
|
|
||||||
...testToken.token.toBuffer(),
|
|
||||||
...initialOwner.publicKey.toBuffer(),
|
|
||||||
123,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
// mock Token.transfer() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
await testToken.transfer(initialOwner, initialOwnerTokenAccount, dest, 123);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.accountInfo()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [dest.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
2,
|
|
||||||
...testToken.token.toBuffer(),
|
|
||||||
...dest.toBuffer(),
|
|
||||||
123,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
await sleep(500);
|
|
||||||
|
|
||||||
const destAccountInfo = await testToken.accountInfo(dest);
|
|
||||||
expect(destAccountInfo.amount.toNumber()).toBe(123);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('approve/revoke', async () => {
|
|
||||||
const connection = new Connection(url);
|
|
||||||
connection._disableBlockhashCaching = mockRpcEnabled;
|
|
||||||
const delegateOwner = await newAccountWithLamports(connection);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock SystemProgram.createAccount transaction for Token.newAccount()
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
|
|
||||||
// mock Token.newAccount() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
const delegate = await testToken.newAccount(
|
|
||||||
delegateOwner,
|
|
||||||
initialOwnerTokenAccount,
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.approve() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
await testToken.approve(
|
|
||||||
initialOwner,
|
|
||||||
initialOwnerTokenAccount,
|
|
||||||
delegate,
|
|
||||||
456,
|
|
||||||
);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.accountInfo()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [delegate.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
2,
|
|
||||||
...testToken.token.toBuffer(),
|
|
||||||
...delegate.toBuffer(),
|
|
||||||
200,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
...initialOwnerTokenAccount.toBuffer(),
|
|
||||||
200,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
let delegateAccountInfo = await testToken.accountInfo(delegate);
|
|
||||||
|
|
||||||
expect(delegateAccountInfo.amount.toNumber()).toBe(456);
|
|
||||||
expect(delegateAccountInfo.originalAmount.toNumber()).toBe(456);
|
|
||||||
if (delegateAccountInfo.source === null) {
|
|
||||||
throw new Error('source should not be null');
|
|
||||||
} else {
|
|
||||||
expect(delegateAccountInfo.source.equals(initialOwnerTokenAccount)).toBe(
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.revoke() transaction
|
|
||||||
mockSendTransaction();
|
|
||||||
mockGetSignatureStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
await testToken.revoke(initialOwner, initialOwnerTokenAccount, delegate);
|
|
||||||
|
|
||||||
{
|
|
||||||
// mock Token.accountInfo()'s getAccountInfo
|
|
||||||
mockRpc.push([
|
|
||||||
url,
|
|
||||||
{
|
|
||||||
method: 'getAccountInfo',
|
|
||||||
params: [delegate.toBase58()],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
error: null,
|
|
||||||
result: {
|
|
||||||
owner: [...SYSTEM_TOKEN_PROGRAM_ID.toBuffer()],
|
|
||||||
lamports: 1,
|
|
||||||
data: [
|
|
||||||
2,
|
|
||||||
...testToken.token.toBuffer(),
|
|
||||||
...delegate.toBuffer(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
...initialOwnerTokenAccount.toBuffer(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
],
|
|
||||||
executable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
delegateAccountInfo = await testToken.accountInfo(delegate);
|
|
||||||
expect(delegateAccountInfo.amount.toNumber()).toBe(0);
|
|
||||||
expect(delegateAccountInfo.originalAmount.toNumber()).toBe(0);
|
|
||||||
if (delegateAccountInfo.source === null) {
|
|
||||||
throw new Error('source should not be null');
|
|
||||||
} else {
|
|
||||||
expect(delegateAccountInfo.source.equals(initialOwnerTokenAccount)).toBe(
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('invalid approve', async () => {
|
|
||||||
if (mockRpcEnabled) {
|
|
||||||
console.log('non-live test skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const connection = new Connection(url);
|
|
||||||
const owner = await newAccountWithLamports(connection);
|
|
||||||
|
|
||||||
const account1 = await testToken.newAccount(owner);
|
|
||||||
const account1Delegate = await testToken.newAccount(owner, account1);
|
|
||||||
const account2 = await testToken.newAccount(owner);
|
|
||||||
|
|
||||||
// account2 is not a delegate account of account1
|
|
||||||
await expect(
|
|
||||||
testToken.approve(owner, account1, account2, 123),
|
|
||||||
).rejects.toThrow();
|
|
||||||
|
|
||||||
// account1Delegate is not a delegate account of account2
|
|
||||||
await expect(
|
|
||||||
testToken.approve(owner, account2, account1Delegate, 123),
|
|
||||||
).rejects.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('fail on approve overspend', async () => {
|
|
||||||
if (mockRpcEnabled) {
|
|
||||||
console.log('non-live test skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const connection = new Connection(url);
|
|
||||||
const owner = await newAccountWithLamports(connection);
|
|
||||||
|
|
||||||
const account1 = await testToken.newAccount(owner);
|
|
||||||
const account1Delegate = await testToken.newAccount(owner, account1);
|
|
||||||
const account2 = await testToken.newAccount(owner);
|
|
||||||
|
|
||||||
await testToken.transfer(
|
|
||||||
initialOwner,
|
|
||||||
initialOwnerTokenAccount,
|
|
||||||
account1,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
|
|
||||||
await testToken.approve(owner, account1, account1Delegate, 2);
|
|
||||||
|
|
||||||
let delegateAccountInfo = await testToken.accountInfo(account1Delegate);
|
|
||||||
expect(delegateAccountInfo.amount.toNumber()).toBe(2);
|
|
||||||
expect(delegateAccountInfo.originalAmount.toNumber()).toBe(2);
|
|
||||||
|
|
||||||
await testToken.transfer(owner, account1Delegate, account2, 1);
|
|
||||||
|
|
||||||
delegateAccountInfo = await testToken.accountInfo(account1Delegate);
|
|
||||||
expect(delegateAccountInfo.amount.toNumber()).toBe(1);
|
|
||||||
expect(delegateAccountInfo.originalAmount.toNumber()).toBe(2);
|
|
||||||
|
|
||||||
await testToken.transfer(owner, account1Delegate, account2, 1);
|
|
||||||
|
|
||||||
delegateAccountInfo = await testToken.accountInfo(account1Delegate);
|
|
||||||
expect(delegateAccountInfo.amount.toNumber()).toBe(0);
|
|
||||||
expect(delegateAccountInfo.originalAmount.toNumber()).toBe(2);
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
testToken.transfer(owner, account1Delegate, account2, 1),
|
|
||||||
).rejects.toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('set owner', async () => {
|
|
||||||
if (mockRpcEnabled) {
|
|
||||||
console.log('non-live test skipped');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const connection = new Connection(url);
|
|
||||||
const owner = await newAccountWithLamports(connection);
|
|
||||||
const newOwner = await newAccountWithLamports(connection);
|
|
||||||
|
|
||||||
const account = await testToken.newAccount(owner);
|
|
||||||
|
|
||||||
await testToken.setOwner(owner, account, newOwner.publicKey);
|
|
||||||
await expect(
|
|
||||||
testToken.setOwner(owner, account, newOwner.publicKey),
|
|
||||||
).rejects.toThrow();
|
|
||||||
|
|
||||||
await testToken.setOwner(newOwner, account, owner.publicKey);
|
|
||||||
});
|
|
Loading…
x
Reference in New Issue
Block a user