solana-program-library/token/js/cli/token-test.js

462 lines
15 KiB
JavaScript
Raw Normal View History

2020-05-29 15:00:47 -07:00
// @flow
import fs from 'mz/fs';
import {Account, Connection, BpfLoader, PublicKey} from '@solana/web3.js';
2020-05-29 15:00:47 -07:00
import semver from 'semver';
import {Token, TokenAmount} from '../client/token';
import {url} from '../url';
2020-05-29 15:00:47 -07:00
import {newAccountWithLamports} from '../client/util/new-account-with-lamports';
import {sleep} from '../client/util/sleep';
2020-07-06 07:49:20 -07:00
import {Store} from '../client/util/store';
2020-05-29 15:00:47 -07:00
// Loaded token program's program id
let programId: PublicKey;
// A token created by the next test and used by all subsequent tests
2020-07-06 07:49:20 -07:00
let mintOwner: Account;
2020-07-06 16:36:27 -07:00
let testToken: Token;
// Initial token account
let testAccountOwner: Account;
let testAccount: PublicKey;
2020-05-29 15:00:47 -07:00
2020-07-06 16:36:27 -07:00
// A mintable token used by multiple tests
let mintableOwner: Account;
let testMintableToken: Token;
// Initial token account
let testMintableAccountOwner: Account;
let testMintableAccount: PublicKey;
2020-05-29 15:00:47 -07:00
function assert(condition, message) {
if (!condition) {
console.log(Error().stack + ':token-test.js');
throw message || 'Assertion failed';
}
}
async function didThrow(func, args): Promise<boolean> {
try {
await func.apply(args);
} catch (e) {
return true;
}
return false;
}
let connection;
async function getConnection(): Promise<Connection> {
if (connection) return connection;
let newConnection = new Connection(url, 'recent', );
2020-05-29 15:00:47 -07:00
const version = await newConnection.getVersion();
// commitment params are only supported >= 0.21.0
const solanaCoreVersion = version['solana-core'].split(' ')[0];
if (semver.gte(solanaCoreVersion, '0.21.0')) {
newConnection = new Connection(url, 'recent');
}
// eslint-disable-next-line require-atomic-updates
connection = newConnection;
console.log('Connection to cluster established:', url, version);
return connection;
}
2020-07-06 07:49:20 -07:00
async function loadProgram(connection: Connection, path: string): Promise<PublicKey> {
2020-05-29 15:00:47 -07:00
const NUM_RETRIES = 500; /* allow some number of retries */
const data = await fs.readFile(path
2020-05-29 15:00:47 -07:00
);
2020-07-06 07:49:20 -07:00
const { feeCalculator } = await connection.getRecentBlockhash();
2020-05-29 15:00:47 -07:00
const balanceNeeded =
feeCalculator.lamportsPerSignature *
2020-07-06 07:49:20 -07:00
(BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES) +
2020-05-29 15:00:47 -07:00
(await connection.getMinimumBalanceForRentExemption(data.length));
const from = await newAccountWithLamports(connection, balanceNeeded);
const program_account = new Account();
2020-07-06 07:49:20 -07:00
console.log('Loading program:', path);
await BpfLoader.load(connection, from, program_account, data);
2020-07-06 07:49:20 -07:00
return program_account.publicKey;
}
async function GetPrograms(connection: Connection): Promise<PublicKey> {
const store = new Store();
let tokenProgramId = null;
try {
const config = await store.load('config.json');
console.log('Using pre-loaded Token program');
2020-07-06 16:36:27 -07:00
console.log(' Note: To reload program remove client/util/store/config.json');
2020-07-06 07:49:20 -07:00
tokenProgramId = new PublicKey(config.tokenProgramId);
} catch (err) {
tokenProgramId = await loadProgram(connection, '../target/bpfel-unknown-unknown/release/spl_token.so');
await store.save('config.json', {
tokenProgramId: tokenProgramId.toString(),
});
}
return tokenProgramId;
}
export async function loadTokenProgram(): Promise<void> {
const connection = await getConnection();
programId = await GetPrograms(connection);
console.log('Token Program ID', programId.toString());
2020-05-29 15:00:47 -07:00
}
2020-07-06 07:49:20 -07:00
export async function createMint(): Promise<void> {
2020-05-29 15:00:47 -07:00
const connection = await getConnection();
const payer = await newAccountWithLamports(connection, 100000000000 /* wag */);
2020-07-06 07:49:20 -07:00
mintOwner = new Account();
testAccountOwner = new Account();
2020-07-06 07:49:20 -07:00
[testToken, testAccount] = await Token.createMint(
2020-05-29 15:00:47 -07:00
connection,
payer,
2020-07-06 07:49:20 -07:00
mintOwner.publicKey,
testAccountOwner.publicKey,
2020-05-29 15:00:47 -07:00
new TokenAmount(10000),
2,
programId,
2020-06-04 22:47:17 -07:00
false,
2020-05-29 15:00:47 -07:00
);
2020-07-06 07:49:20 -07:00
const mintInfo = await testToken.getMintInfo();
assert(mintInfo.decimals == 2);
assert(mintInfo.owner == null);
const accountInfo = await testToken.getAccountInfo(testAccount);
2020-07-06 07:49:20 -07:00
assert(accountInfo.mint.equals(testToken.publicKey));
assert(accountInfo.owner.equals(testAccountOwner.publicKey));
assert(accountInfo.amount.toNumber() == 10000);
2020-07-06 07:49:20 -07:00
assert(accountInfo.delegate == null);
assert(accountInfo.delegatedAmount.toNumber() == 0);
2020-05-29 15:00:47 -07:00
}
2020-07-01 01:40:36 -07:00
export async function createAccount(): Promise<void> {
2020-07-06 16:36:27 -07:00
const destOwner = new Account();
const account = await testToken.createAccount(destOwner.publicKey);
const accountInfo = await testToken.getAccountInfo(account);
2020-07-06 07:49:20 -07:00
assert(accountInfo.mint.equals(testToken.publicKey));
assert(accountInfo.owner.equals(destOwner.publicKey));
assert(accountInfo.amount.toNumber() == 0);
2020-07-06 07:49:20 -07:00
assert(accountInfo.delegate == null);
2020-05-29 15:00:47 -07:00
}
export async function transfer(): Promise<void> {
2020-07-06 16:36:27 -07:00
const destOwner = new Account();
2020-07-01 01:40:36 -07:00
const dest = await testToken.createAccount(destOwner.publicKey);
2020-05-29 15:00:47 -07:00
2020-07-06 16:36:27 -07:00
await testToken.transfer(testAccount, dest, testAccountOwner, [], 123);
2020-05-29 15:00:47 -07:00
await sleep(500);
2020-07-01 01:40:36 -07:00
let destAccountInfo = await testToken.getAccountInfo(dest);
assert(destAccountInfo.amount.toNumber() == 123);
2020-05-29 15:00:47 -07:00
}
export async function approveRevoke(): Promise<void> {
if (programId == null) {
console.log('test skipped, requires "load token program" to succeed');
return;
}
2020-07-06 07:49:20 -07:00
const delegate = new PublicKey();
2020-05-29 15:00:47 -07:00
await testToken.approve(
testAccount,
2020-05-29 15:00:47 -07:00
delegate,
2020-07-06 07:49:20 -07:00
testAccountOwner,
2020-07-06 16:36:27 -07:00
[],
2020-05-29 15:00:47 -07:00
456,
);
2020-07-06 07:49:20 -07:00
let testAccountInfo = await testToken.getAccountInfo(testAccount);
assert(testAccountInfo.delegatedAmount.toNumber() == 456);
if (testAccountInfo.delegate === null) {
throw new Error('deleage should not be null');
2020-05-29 15:00:47 -07:00
} else {
2020-07-06 07:49:20 -07:00
assert(testAccountInfo.delegate.equals(delegate));
2020-05-29 15:00:47 -07:00
}
2020-07-07 11:23:23 -07:00
await testToken.revoke(testAccount, testAccountOwner, []);
2020-07-06 07:49:20 -07:00
testAccountInfo = await testToken.getAccountInfo(testAccount);
assert(testAccountInfo.delegatedAmount.toNumber() == 0);
if (testAccountInfo.delegate != null) {
throw new Error('delegate should be null');
2020-05-29 15:00:47 -07:00
}
}
export async function invalidApprove(): Promise<void> {
2020-07-06 16:36:27 -07:00
const owner = new Account();
2020-07-01 01:40:36 -07:00
const account1 = await testToken.createAccount(owner.publicKey);
const account2 = await testToken.createAccount(owner.publicKey);
2020-07-06 07:49:20 -07:00
const delegate = new Account();
2020-05-29 15:00:47 -07:00
// account2 is not a delegate account of account1
2020-07-06 16:36:27 -07:00
assert(didThrow(testToken.approve, [account1, account2, owner, [], 123]));
2020-05-29 15:00:47 -07:00
// account1Delegate is not a delegate account of account2
2020-07-06 16:36:27 -07:00
assert(didThrow(testToken.approve, [account2, delegate, owner, [], 123]));
2020-05-29 15:00:47 -07:00
}
export async function failOnApproveOverspend(): Promise<void> {
2020-07-06 16:36:27 -07:00
const owner = new Account();
2020-07-01 01:40:36 -07:00
const account1 = await testToken.createAccount(owner.publicKey);
const account2 = await testToken.createAccount(owner.publicKey);
2020-07-06 07:49:20 -07:00
const delegate = new Account();
2020-05-29 15:00:47 -07:00
await testToken.transfer(
testAccount,
2020-05-29 15:00:47 -07:00
account1,
2020-07-06 07:49:20 -07:00
testAccountOwner,
2020-07-06 16:36:27 -07:00
[],
2020-05-29 15:00:47 -07:00
10,
);
2020-07-06 16:36:27 -07:00
await testToken.approve(account1, delegate.publicKey, owner, [], 2);
2020-05-29 15:00:47 -07:00
2020-07-06 07:49:20 -07:00
let account1Info = await testToken.getAccountInfo(account1);
assert(account1Info.amount.toNumber() == 10);
assert(account1Info.delegatedAmount.toNumber() == 2);
if (account1Info.delegate === null) {
throw new Error('deleage should not be null');
} else {
assert(account1Info.delegate.equals(delegate.publicKey));
}
2020-05-29 15:00:47 -07:00
2020-07-06 16:36:27 -07:00
await testToken.transfer(account1, account2, delegate, [], 1);
2020-05-29 15:00:47 -07:00
2020-07-06 07:49:20 -07:00
account1Info = await testToken.getAccountInfo(account1);
assert(account1Info.amount.toNumber() == 9);
assert(account1Info.delegatedAmount.toNumber() == 1);
2020-05-29 15:00:47 -07:00
2020-07-06 16:36:27 -07:00
await testToken.transfer(account1, account2, delegate, [], 1);
2020-05-29 15:00:47 -07:00
2020-07-06 07:49:20 -07:00
account1Info = await testToken.getAccountInfo(account1);
assert(account1Info.amount.toNumber() == 8);
assert(account1Info.delegate === null);
assert(account1Info.delegatedAmount.toNumber() == 0);
2020-05-29 15:00:47 -07:00
2020-07-06 16:36:27 -07:00
assert(didThrow(testToken.transfer, [account1, account2, delegate, [], 1]));
2020-05-29 15:00:47 -07:00
}
export async function setOwner(): Promise<void> {
2020-07-06 16:36:27 -07:00
const owner = new Account();
const newOwner = new Account();
2020-07-01 01:40:36 -07:00
const owned = await testToken.createAccount(owner.publicKey);
2020-05-29 15:00:47 -07:00
2020-07-06 16:36:27 -07:00
await testToken.setOwner(owned, newOwner.publicKey, owner, []);
assert(didThrow(testToken.setOwner, [owned, newOwner.publicKey, owner, []]));
await testToken.setOwner(owned, owner.publicKey,newOwner, []);
2020-06-04 22:47:17 -07:00
}
export async function mintTo(): Promise<void> {
const connection = await getConnection();
const payer = await newAccountWithLamports(connection, 100000000000 /* wag */);
2020-07-06 16:36:27 -07:00
mintableOwner = new Account();
testMintableAccountOwner = new Account();
[testMintableToken, testMintableAccount] = await Token.createMint(
2020-06-04 22:47:17 -07:00
connection,
payer,
2020-07-06 16:36:27 -07:00
mintableOwner.publicKey,
testMintableAccountOwner.publicKey,
2020-06-04 22:47:17 -07:00
new TokenAmount(10000),
2,
programId,
true,
);
{
2020-07-06 16:36:27 -07:00
const mintInfo = await testMintableToken.getMintInfo();
2020-07-06 07:49:20 -07:00
assert(mintInfo.decimals == 2);
if (mintInfo.owner === null) {
2020-06-04 22:47:17 -07:00
throw new Error('owner should not be null');
} else {
2020-07-06 16:36:27 -07:00
assert(mintInfo.owner.equals(mintableOwner.publicKey));
2020-06-04 22:47:17 -07:00
}
2020-07-06 16:36:27 -07:00
const accountInfo = await testMintableToken.getAccountInfo(testMintableAccount);
assert(accountInfo.mint.equals(testMintableToken.publicKey));
assert(accountInfo.owner.equals(testMintableAccountOwner.publicKey));
assert(accountInfo.amount.toNumber() == 10000);
2020-07-06 07:49:20 -07:00
assert(accountInfo.delegate == null);
assert(accountInfo.delegatedAmount.toNumber() == 0);
2020-06-04 22:47:17 -07:00
}
2020-07-06 16:36:27 -07:00
const dest = await testMintableToken.createAccount(testMintableAccountOwner.publicKey);
await testMintableToken.mintTo(dest, mintableOwner, [], 42);
2020-06-04 22:47:17 -07:00
{
2020-07-06 16:36:27 -07:00
const mintInfo = await testMintableToken.getMintInfo();
2020-07-06 07:49:20 -07:00
assert(mintInfo.decimals == 2);
if (mintInfo.owner === null) {
2020-06-04 22:47:17 -07:00
throw new Error('owner should not be null');
} else {
2020-07-06 16:36:27 -07:00
assert(mintInfo.owner.equals(mintableOwner.publicKey));
2020-06-04 22:47:17 -07:00
}
2020-07-06 16:36:27 -07:00
const accountInfo = await testMintableToken.getAccountInfo(dest);
assert(accountInfo.mint.equals(testMintableToken.publicKey));
assert(accountInfo.owner.equals(testMintableAccountOwner.publicKey));
assert(accountInfo.amount.toNumber() == 42);
2020-07-06 07:49:20 -07:00
assert(accountInfo.delegate == null);
assert(accountInfo.delegatedAmount.toNumber() == 0);
2020-06-04 22:47:17 -07:00
}
2020-05-29 15:00:47 -07:00
}
2020-06-05 08:38:48 -07:00
export async function burn(): Promise<void> {
let accountInfo = await testToken.getAccountInfo(testAccount);
const amount = accountInfo.amount.toNumber();
2020-06-05 08:38:48 -07:00
2020-07-06 16:36:27 -07:00
await testToken.burn(testAccount, testAccountOwner, [], 1);
2020-06-05 08:38:48 -07:00
await sleep(500);
accountInfo = await testToken.getAccountInfo(testAccount);
assert(accountInfo.amount.toNumber() == amount - 1);
2020-06-05 08:38:48 -07:00
}
2020-07-06 16:36:27 -07:00
export async function multisig(): Promise<void> {
const m = 2;
const n = 5;
let signerAccounts = [];
for (var i = 0; i < n; i++) {
signerAccounts.push(new Account());
}
let signerPublicKeys = [];
signerAccounts.forEach(account => signerPublicKeys.push(account.publicKey));
const multisig = await testToken.createMultisig(m, signerPublicKeys);
const multisigInfo = await testToken.getMultisigInfo(multisig);
assert(multisigInfo.m === m);
assert(multisigInfo.n === n);
assert(multisigInfo.signer1.equals(signerPublicKeys[0]));
assert(multisigInfo.signer2.equals(signerPublicKeys[1]));
assert(multisigInfo.signer3.equals(signerPublicKeys[2]));
assert(multisigInfo.signer4.equals(signerPublicKeys[3]));
assert(multisigInfo.signer5.equals(signerPublicKeys[4]));
const multisigOwnedAccount = await testToken.createAccount(multisig);
const finalDest = await testToken.createAccount(multisig);
await testToken.transfer(testAccount, multisigOwnedAccount, testAccountOwner, [], 2);
// Transfer via multisig
await testToken.transfer(multisigOwnedAccount, finalDest, multisig, signerAccounts, 1);
await sleep(500);
let accountInfo = await testToken.getAccountInfo(finalDest);
assert(accountInfo.amount.toNumber() == 1);
// Approve via multisig
{
const delegate = new PublicKey();
await testToken.approve(
multisigOwnedAccount,
delegate,
multisig, signerAccounts,
1,
);
const accountInfo = await testToken.getAccountInfo(multisigOwnedAccount);
assert(accountInfo.delegate != null);
if (accountInfo.delegate != null) {
assert(accountInfo.delegate.equals(delegate));
assert(accountInfo.delegatedAmount.toNumber() == 1);
}
}
// MintTo via multisig
{
let accountInfo = await testMintableToken.getAccountInfo(testMintableAccount);
const initialAmount = accountInfo.amount.toNumber();
await testMintableToken.setOwner(testMintableToken.publicKey, multisig, mintableOwner, []);
await testMintableToken.mintTo(testMintableAccount, multisig, signerAccounts, 42);
accountInfo = await testMintableToken.getAccountInfo(testMintableAccount);
assert(accountInfo.amount.toNumber() == initialAmount + 42);
}
// SetOwner of mint via multisig
{
await testMintableToken.setOwner(testMintableToken.publicKey, mintableOwner.publicKey, multisig, signerAccounts);
const mintInfo = await testMintableToken.getMintInfo();
assert(mintInfo.owner != null);
if (mintInfo.owner != null) {
assert(mintInfo.owner.equals(mintableOwner.publicKey));
}
}
// SetOwner of account via multisig
{
const newOwner = new PublicKey();
await testToken.setOwner(multisigOwnedAccount, newOwner, multisig, signerAccounts);
const accountInfo = await testToken.getAccountInfo(multisigOwnedAccount);
assert(accountInfo.owner.equals(newOwner));
}
}
2020-07-09 17:11:00 -07:00
export async function closeAccount(): Promise<void> {
const connection = await getConnection();
const owner = new Account();
const close = await testToken.createAccount(owner.publicKey);
let close_balance;
let info = await connection.getAccountInfo(close);
if (info != null) {
close_balance = info.lamports;
} else {
throw new Error('Account not found');
}
const balanceNeeded =
await connection.getMinimumBalanceForRentExemption(0);
const dest = await newAccountWithLamports(connection, balanceNeeded);
info = await connection.getAccountInfo(dest.publicKey);
if (info != null) {
assert(info.lamports == balanceNeeded);
} else {
throw new Error('Account not found');
}
await testToken.closeAccount(close, dest.publicKey, owner, []);
info = await connection.getAccountInfo(close);
if (info != null) {
throw new Error('Account not closed');
}
info = await connection.getAccountInfo(dest.publicKey);
if (info != null) {
assert(info.lamports == balanceNeeded + close_balance);
} else {
throw new Error('Account not found');
}
}
export async function nativeToken(): Promise<void> {
const connection = await getConnection();
const mintPublicKey = new PublicKey('So11111111111111111111111111111111111111111');
const payer = await newAccountWithLamports(connection, 100000000000 /* wag */);
const token = new Token(connection, mintPublicKey, programId, payer);
const owner = new Account();
const native = await token.createAccount(owner.publicKey);
let accountInfo = await token.getAccountInfo(native);
assert(accountInfo.isNative);
let balance;
let info = await connection.getAccountInfo(native);
if (info != null) {
balance = info.lamports;
} else {
throw new Error('Account not found');
}
const balanceNeeded =
await connection.getMinimumBalanceForRentExemption(0);
const dest = await newAccountWithLamports(connection, balanceNeeded);
await token.closeAccount(native, dest.publicKey, owner, []);
info = await connection.getAccountInfo(native);
if (info != null) {
throw new Error('Account not burned');
}
info = await connection.getAccountInfo(dest.publicKey);
if (info != null) {
assert(info.lamports == balanceNeeded + balance);
} else {
throw new Error('Account not found');
}
}