token-swap js: Prepare package for npm (#475)

* Run `pretty` properly
* Add `build:program` like for spl-token
* Add TypeScript to CI
* snake_case -> camelCase variables
* Combine transactions on swap initialization
* Make instruction functions static
This commit is contained in:
Jon Cinque 2020-09-18 19:10:21 +02:00 committed by GitHub
parent e5ae9bbcb9
commit ba76d520eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 178 additions and 123 deletions

View File

@ -81,14 +81,14 @@ js_token_swap() {
cd token-swap/js
time npm install || exit $?
time npm run lint || exit $?
time npm run flow || exit $?
tsc module.d.ts || exit $?
npm run cluster:localnet || exit $?
npm run localnet:down
npm run localnet:update || exit $?
npm run localnet:up || exit $?
npm run start || exit $?
time npm run start || exit $?
npm run localnet:down
}
_ js_token_swap

View File

@ -0,0 +1 @@
/lib

View File

@ -34,7 +34,14 @@ Solana cluster logs are available with:
$ npm run localnet:logs
```
For more details on working with a local cluster, see the [full instructions](https://github.com/solana-labs/solana-web3.js#local-network).
For more details on working with a local cluster, see the [full
instructions](https://github.com/solana-labs/solana-web3.js#local-network).
### Build the on-chain program
```bash
$ npm run build:program
```
### Run the test client

View File

@ -2,14 +2,20 @@
import fs from 'mz/fs';
import semver from 'semver';
import { Account, Connection, BpfLoader, PublicKey, BPF_LOADER_PROGRAM_ID } from '@solana/web3.js';
import {
Account,
Connection,
BpfLoader,
PublicKey,
BPF_LOADER_PROGRAM_ID,
} from '@solana/web3.js';
import { Token } from '../../../token/js/client/token';
import { TokenSwap } from '../client/token-swap';
import { Store } from '../client/util/store';
import { newAccountWithLamports } from '../client/util/new-account-with-lamports';
import { url } from '../url';
import { sleep } from '../client/util/sleep';
import {Token} from '../../../token/js/client/token';
import {TokenSwap} from '../client/token-swap';
import {Store} from '../client/util/store';
import {newAccountWithLamports} from '../client/util/new-account-with-lamports';
import {url} from '../url';
import {sleep} from '../client/util/sleep';
// The following globals are created by `createTokenSwap` and used by subsequent tests
// Token swap
@ -45,7 +51,7 @@ let connection;
async function getConnection(): Promise<Connection> {
if (connection) return connection;
let newConnection = new Connection(url, 'recent',);
let newConnection = new Connection(url, 'recent');
const version = await newConnection.getVersion();
// commitment params are only supported >= 0.21.0
@ -60,39 +66,57 @@ async function getConnection(): Promise<Connection> {
return newConnection;
}
async function loadProgram(connection: Connection, path: string): Promise<PublicKey> {
async function loadProgram(
connection: Connection,
path: string,
): Promise<PublicKey> {
const NUM_RETRIES = 500; /* allow some number of retries */
const data = await fs.readFile(path
);
const { feeCalculator } = await connection.getRecentBlockhash();
const data = await fs.readFile(path);
const {feeCalculator} = await connection.getRecentBlockhash();
const balanceNeeded =
feeCalculator.lamportsPerSignature *
(BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES) +
(BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES) +
(await connection.getMinimumBalanceForRentExemption(data.length));
const from = await newAccountWithLamports(connection, balanceNeeded);
const program_account = new Account();
console.log('Loading program:', path);
await BpfLoader.load(connection, from, program_account, data, BPF_LOADER_PROGRAM_ID);
await BpfLoader.load(
connection,
from,
program_account,
data,
BPF_LOADER_PROGRAM_ID,
);
return program_account.publicKey;
}
async function GetPrograms(connection: Connection): Promise<[PublicKey, PublicKey]> {
async function GetPrograms(
connection: Connection,
): Promise<[PublicKey, PublicKey]> {
const store = new Store();
let tokenProgramId = null;
let tokenSwapProgramId = null;
try {
const config = await store.load('config.json');
console.log('Using pre-loaded Token and Token-swap programs');
console.log(' Note: To reload programs remove client/util/store/config.json');
console.log(
' Note: To reload programs remove client/util/store/config.json',
);
tokenProgramId = new PublicKey(config.tokenProgramId);
tokenSwapProgramId = new PublicKey(config.tokenSwapProgramId);
} catch (err) {
tokenProgramId = await loadProgram(connection, '../../target/bpfel-unknown-unknown/release/spl_token.so');
tokenSwapProgramId = await loadProgram(connection, '../../target/bpfel-unknown-unknown/release/spl_token_swap.so');
tokenProgramId = await loadProgram(
connection,
'../../target/bpfel-unknown-unknown/release/spl_token.so',
);
tokenSwapProgramId = await loadProgram(
connection,
'../../target/bpfel-unknown-unknown/release/spl_token_swap.so',
);
await store.save('config.json', {
tokenProgramId: tokenProgramId.toString(),
tokenSwapProgramId: tokenSwapProgramId.toString()
tokenSwapProgramId: tokenSwapProgramId.toString(),
});
}
return [tokenProgramId, tokenSwapProgramId];
@ -109,13 +133,16 @@ export async function loadPrograms(): Promise<void> {
export async function createTokenSwap(): Promise<void> {
const connection = await getConnection();
const [tokenProgramId, tokenSwapProgramId] = await GetPrograms(connection);
const payer = await newAccountWithLamports(connection, 100000000000 /* wag */);
const payer = await newAccountWithLamports(
connection,
100000000000 /* wag */,
);
owner = await newAccountWithLamports(connection, 100000000000 /* wag */);
const tokenSwapAccount = new Account();
[authority, nonce] = await PublicKey.findProgramAddress(
[tokenSwapAccount.publicKey.toBuffer()],
tokenSwapProgramId
tokenSwapProgramId,
);
console.log('creating pool mint');
@ -162,7 +189,10 @@ export async function createTokenSwap(): Promise<void> {
await mintB.mintTo(tokenAccountB, owner, [], BASE_AMOUNT);
console.log('creating token swap');
const swapPayer = await newAccountWithLamports(connection, 100000000000 /* wag */);
const swapPayer = await newAccountWithLamports(
connection,
100000000000 /* wag */,
);
tokenSwap = await TokenSwap.createTokenSwap(
connection,
swapPayer,
@ -176,7 +206,7 @@ export async function createTokenSwap(): Promise<void> {
nonce,
1,
4,
tokenSwapProgramId
tokenSwapProgramId,
);
console.log('getting token swap');
@ -192,26 +222,14 @@ export async function deposit(): Promise<void> {
console.log('Creating depositor token a account');
let userAccountA = await mintA.createAccount(owner.publicKey);
await mintA.mintTo(userAccountA, owner, [], USER_AMOUNT);
await mintA.approve(
userAccountA,
authority,
owner,
[],
USER_AMOUNT,
);
await mintA.approve(userAccountA, authority, owner, [], USER_AMOUNT);
console.log('Creating depositor token b account');
let userAccountB = await mintB.createAccount(owner.publicKey);
await mintB.mintTo(userAccountB, owner, [], USER_AMOUNT);
await mintB.approve(
userAccountB,
authority,
owner,
[],
USER_AMOUNT,
);
await mintB.approve(userAccountB, authority, owner, [], USER_AMOUNT);
console.log('Creating depositor pool token account');
let newAccountPool = await tokenPool.createAccount(owner.publicKey);
const [tokenProgramId,] = await GetPrograms(connection);
const [tokenProgramId] = await GetPrograms(connection);
console.log('Depositing into swap');
await tokenSwap.deposit(
@ -246,14 +264,8 @@ export async function withdraw(): Promise<void> {
let userAccountB = await mintB.createAccount(owner.publicKey);
console.log('Approving withdrawal from pool account');
await tokenPool.approve(
tokenAccountPool,
authority,
owner,
[],
USER_AMOUNT,
);
const [tokenProgramId,] = await GetPrograms(connection);
await tokenPool.approve(tokenAccountPool, authority, owner, [], USER_AMOUNT);
const [tokenProgramId] = await GetPrograms(connection);
console.log('Withdrawing pool tokens for A and B tokens');
await tokenSwap.withdraw(
@ -265,7 +277,7 @@ export async function withdraw(): Promise<void> {
userAccountA,
userAccountB,
tokenProgramId,
USER_AMOUNT
USER_AMOUNT,
);
let info = await tokenPool.getAccountInfo(tokenAccountPool);
@ -284,16 +296,10 @@ export async function swap(): Promise<void> {
console.log('Creating swap token a account');
let userAccountA = await mintA.createAccount(owner.publicKey);
await mintA.mintTo(userAccountA, owner, [], USER_AMOUNT);
await mintA.approve(
userAccountA,
authority,
owner,
[],
USER_AMOUNT,
);
await mintA.approve(userAccountA, authority, owner, [], USER_AMOUNT);
console.log('Creating swap token b account');
let userAccountB = await mintB.createAccount(owner.publicKey);
const [tokenProgramId,] = await GetPrograms(connection);
const [tokenProgramId] = await GetPrograms(connection);
console.log('Swapping');
await tokenSwap.swap(

View File

@ -82,15 +82,14 @@ type TokenSwapInfo = {|
feesNumerator: Numberu64,
/**
* Fee denominator
*/
* Fee denominator
*/
feesDenominator: Numberu64,
/**
/**
* Fee ratio applied to the input token amount prior to output calculation
*/
feeRatio: number,
|};
/**
@ -138,7 +137,12 @@ export class TokenSwap {
* @param programId Optional token programId, uses the system programId by default
* @param payer Payer of fees
*/
constructor(connection: Connection, tokenSwap: PublicKey, programId: PublicKey, payer: Account) {
constructor(
connection: Connection,
tokenSwap: PublicKey,
programId: PublicKey,
payer: Account,
) {
Object.assign(this, {connection, tokenSwap, programId, payer});
}
@ -188,26 +192,26 @@ export class TokenSwap {
programId: PublicKey,
): Promise<TokenSwap> {
let transaction;
const tokenSwap = new TokenSwap(connection, tokenSwapAccount.publicKey, programId, payer);
const tokenSwap = new TokenSwap(
connection,
tokenSwapAccount.publicKey,
programId,
payer,
);
// Allocate memory for the account
const balanceNeeded = await TokenSwap.getMinBalanceRentForExemptTokenSwap(
connection,
);
transaction = new Transaction();
transaction.add(SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: tokenSwapAccount.publicKey,
lamports: balanceNeeded,
space: TokenSwapLayout.span,
programId,
}));
await sendAndConfirmTransaction(
'createAccount',
connection,
transaction,
payer,
tokenSwapAccount,
transaction.add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: tokenSwapAccount.publicKey,
lamports: balanceNeeded,
space: TokenSwapLayout.span,
programId,
}),
);
let keys = [
@ -238,16 +242,17 @@ export class TokenSwap {
);
data = data.slice(0, encodeLength);
}
transaction = new Transaction().add({
transaction.add({
keys,
programId,
data,
});
await sendAndConfirmTransaction(
'InitializeSwap',
'createAccount and InitializeSwap',
connection,
transaction,
payer,
tokenSwapAccount,
);
return tokenSwap;
@ -277,9 +282,15 @@ export class TokenSwap {
tokenSwapInfo.tokenAccountA = new PublicKey(tokenSwapInfo.tokenAccountA);
tokenSwapInfo.tokenAccountB = new PublicKey(tokenSwapInfo.tokenAccountB);
tokenSwapInfo.tokenPool = new PublicKey(tokenSwapInfo.tokenPool);
tokenSwapInfo.feesNumerator = Numberu64.fromBuffer(tokenSwapInfo.feesNumerator);
tokenSwapInfo.feesDenominator = Numberu64.fromBuffer(tokenSwapInfo.feesDenominator);
tokenSwapInfo.feeRatio = tokenSwapInfo.feesNumerator.toNumber() / tokenSwapInfo.feesDenominator.toNumber();
tokenSwapInfo.feesNumerator = Numberu64.fromBuffer(
tokenSwapInfo.feesNumerator,
);
tokenSwapInfo.feesDenominator = Numberu64.fromBuffer(
tokenSwapInfo.feesDenominator,
);
tokenSwapInfo.feeRatio =
tokenSwapInfo.feesNumerator.toNumber() /
tokenSwapInfo.feesDenominator.toNumber();
return tokenSwapInfo;
}
@ -289,8 +300,8 @@ export class TokenSwap {
*
* @param authority Authority
* @param source Source account
* @param swap_source Base account to swap into, must be a source token
* @param swap_destination Base account to swap from, must be a destination token
* @param swapSource Base account to swap into, must be a source token
* @param swapDestination Base account to swap from, must be a destination token
* @param destination Destination token account
* @param tokenProgramId Token program id
* @param amount Amount to transfer from source account
@ -298,8 +309,8 @@ export class TokenSwap {
async swap(
authority: PublicKey,
source: PublicKey,
swap_source: PublicKey,
swap_destination: PublicKey,
swapSource: PublicKey,
swapDestination: PublicKey,
destination: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
@ -308,12 +319,14 @@ export class TokenSwap {
'swap',
this.connection,
new Transaction().add(
this.swapInstruction(
TokenSwap.swapInstruction(
this.tokenSwap,
authority,
source,
swap_source,
swap_destination,
swapSource,
swapDestination,
destination,
this.programId,
tokenProgramId,
amount,
),
@ -322,12 +335,14 @@ export class TokenSwap {
);
}
swapInstruction(
static swapInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
source: PublicKey,
swap_source: PublicKey,
swap_destination: PublicKey,
swapSource: PublicKey,
swapDestination: PublicKey,
destination: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction {
@ -346,17 +361,17 @@ export class TokenSwap {
);
const keys = [
{pubkey: this.tokenSwap, isSigner: false, isWritable: false},
{pubkey: tokenSwap, isSigner: false, isWritable: false},
{pubkey: authority, isSigner: false, isWritable: false},
{pubkey: source, isSigner: false, isWritable: true},
{pubkey: swap_source, isSigner: false, isWritable: true},
{pubkey: swap_destination, isSigner: false, isWritable: true},
{pubkey: swapSource, isSigner: false, isWritable: true},
{pubkey: swapDestination, isSigner: false, isWritable: true},
{pubkey: destination, isSigner: false, isWritable: true},
{pubkey: tokenProgramId, isSigner: false, isWritable: false},
];
return new TransactionInstruction({
keys,
programId: this.programId,
programId: swapProgramId,
data,
});
}
@ -389,7 +404,8 @@ export class TokenSwap {
'deposit',
this.connection,
new Transaction().add(
this.depositInstruction(
TokenSwap.depositInstruction(
this.tokenSwap,
authority,
sourceA,
sourceB,
@ -397,6 +413,7 @@ export class TokenSwap {
intoB,
poolToken,
poolAccount,
this.programId,
tokenProgramId,
amount,
),
@ -405,7 +422,8 @@ export class TokenSwap {
);
}
depositInstruction(
static depositInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
sourceA: PublicKey,
sourceB: PublicKey,
@ -413,6 +431,7 @@ export class TokenSwap {
intoB: PublicKey,
poolToken: PublicKey,
poolAccount: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction {
@ -431,7 +450,7 @@ export class TokenSwap {
);
const keys = [
{pubkey: this.tokenSwap, isSigner: false, isWritable: false},
{pubkey: tokenSwap, isSigner: false, isWritable: false},
{pubkey: authority, isSigner: false, isWritable: false},
{pubkey: sourceA, isSigner: false, isWritable: true},
{pubkey: sourceB, isSigner: false, isWritable: true},
@ -443,7 +462,7 @@ export class TokenSwap {
];
return new TransactionInstruction({
keys,
programId: this.programId,
programId: swapProgramId,
data,
});
}
@ -476,7 +495,8 @@ export class TokenSwap {
'withdraw',
this.connection,
new Transaction().add(
this.withdrawInstruction(
TokenSwap.withdrawInstruction(
this.tokenSwap,
authority,
poolMint,
sourcePoolAccount,
@ -484,6 +504,7 @@ export class TokenSwap {
fromB,
userAccountA,
userAccountB,
this.programId,
tokenProgramId,
amount,
),
@ -492,7 +513,8 @@ export class TokenSwap {
);
}
withdrawInstruction(
static withdrawInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
poolMint: PublicKey,
sourcePoolAccount: PublicKey,
@ -500,6 +522,7 @@ export class TokenSwap {
fromB: PublicKey,
userAccountA: PublicKey,
userAccountB: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction {
@ -518,7 +541,7 @@ export class TokenSwap {
);
const keys = [
{pubkey: this.tokenSwap, isSigner: false, isWritable: false},
{pubkey: tokenSwap, isSigner: false, isWritable: false},
{pubkey: authority, isSigner: false, isWritable: false},
{pubkey: poolMint, isSigner: false, isWritable: true},
{pubkey: sourcePoolAccount, isSigner: false, isWritable: true},
@ -527,11 +550,10 @@ export class TokenSwap {
{pubkey: userAccountA, isSigner: false, isWritable: true},
{pubkey: userAccountB, isSigner: false, isWritable: true},
{pubkey: tokenProgramId, isSigner: false, isWritable: false},
];
return new TransactionInstruction({
keys,
programId: this.programId,
programId: swapProgramId,
data,
});
}

View File

@ -46,19 +46,21 @@ declare module '@solana/spl-token-swap' {
swap(
authority: PublicKey,
source: PublicKey,
swap_source: PublicKey,
swap_destination: PublicKey,
swapSource: PublicKey,
swapDestination: PublicKey,
destination: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): Promise<TransactionSignature>
swapInstruction(
static swapInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
source: PublicKey,
swap_source: PublicKey,
swap_destination: PublicKey,
swapSource: PublicKey,
swapDestination: PublicKey,
destination: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction
@ -75,7 +77,8 @@ declare module '@solana/spl-token-swap' {
amount: number | Numberu64,
): Promise<TransactionSignature>
depositInstruction(
static depositInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
sourceA: PublicKey,
sourceB: PublicKey,
@ -83,6 +86,7 @@ declare module '@solana/spl-token-swap' {
intoB: PublicKey,
poolToken: PublicKey,
poolAccount: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction
@ -99,7 +103,8 @@ declare module '@solana/spl-token-swap' {
amount: number | Numberu64,
): Promise<TransactionSignature>
withdrawInstruction(
static withdrawInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
poolMint: PublicKey,
sourcePoolAccount: PublicKey,
@ -107,6 +112,7 @@ declare module '@solana/spl-token-swap' {
fromB: PublicKey,
userAccountA: PublicKey,
userAccountB: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction

View File

@ -23,7 +23,12 @@ declare module '@solana/spl-token-swap' {
|};
declare export class TokenSwap {
constructor(connection: Connection, tokenSwap: PublicKey, programId: PublicKey, payer: Account): TokenSwap;
constructor(
connection: Connection,
tokenSwap: PublicKey,
programId: PublicKey,
payer: Account,
): TokenSwap;
static getMinBalanceRentForExemptTokenSwap(
connection: Connection,
@ -46,22 +51,25 @@ declare module '@solana/spl-token-swap' {
): Promise<TokenSwap>;
getInfo(): Promise<TokenSwapInfo>;
swap(
authority: PublicKey,
source: PublicKey,
swap_source: PublicKey,
swap_destination: PublicKey,
swapSource: PublicKey,
swapDestination: PublicKey,
destination: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): Promise<TransactionSignature>;
swapInstruction(
static swapInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
source: PublicKey,
swap_source: PublicKey,
swap_destination: PublicKey,
swapSource: PublicKey,
swapDestination: PublicKey,
destination: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction;
@ -78,7 +86,8 @@ declare module '@solana/spl-token-swap' {
amount: number | Numberu64,
): Promise<TransactionSignature>;
depositInstruction(
static depositInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
sourceA: PublicKey,
sourceB: PublicKey,
@ -86,6 +95,7 @@ declare module '@solana/spl-token-swap' {
intoB: PublicKey,
poolToken: PublicKey,
poolAccount: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction;
@ -102,7 +112,8 @@ declare module '@solana/spl-token-swap' {
amount: number | Numberu64,
): Promise<TransactionSignature>;
withdrawInstruction(
static withdrawInstruction(
tokenSwap: PublicKey,
authority: PublicKey,
poolMint: PublicKey,
sourcePoolAccount: PublicKey,
@ -110,6 +121,7 @@ declare module '@solana/spl-token-swap' {
fromB: PublicKey,
userAccountA: PublicKey,
userAccountB: PublicKey,
swapProgramId: PublicKey,
tokenProgramId: PublicKey,
amount: number | Numberu64,
): TransactionInstruction;

View File

@ -1,5 +1,5 @@
{
"name": "spl-token-swap",
"name": "@solana/spl-token-swap",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,

View File

@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "SPL Token Swap JavaScript API",
"license": "MIT",
"author": "Solana Maintainers <maintainers@solana.com>",
"author": "Solana Maintainers <maintainers@solana.foundation>",
"homepage": "https://solana.com/",
"repository": {
"type": "git",
@ -31,6 +31,7 @@
"flow": "flow",
"flow:watch": "watch 'flow' . --wait=1 --ignoreDirectoryPattern=/doc/",
"lint:watch": "watch 'npm run lint:fix' . --wait=1",
"build:program": "rm client/util/store/config.json; ../../do.sh build token-swap",
"cluster:localnet": "rm -f .env",
"cluster:devnet": "cp cluster-devnet.env .env",
"cluster:testnet": "cp cluster-testnet.env .env",
@ -39,7 +40,7 @@
"localnet:up": "rm client/util/store/config.json; set -x; solana-localnet down; set -e; solana-localnet up",
"localnet:down": "solana-localnet down",
"localnet:logs": "solana-localnet logs -f",
"pretty": "prettier --write '{,src/**/}*.js'"
"pretty": "prettier --write '{,cli*/**/}*.js'"
},
"keywords": [],
"dependencies": {