From 5f56635fdef91e252632747697085fdcca78ce55 Mon Sep 17 00:00:00 2001 From: Yutaro Mori Date: Wed, 14 Oct 2020 19:37:58 +0900 Subject: [PATCH] Pass TokenSwap state into the constructor to simplify function calls (#586) * Pass TokenSwap state into the constructor to simplify function calls * WIP: Update program to return token_program_id * Add tokenProgramId to js TokenSwapInfo object * Address pr comments * Remove TokenSwapInfo and use loadTokenSwap as the primary way to fetch TokenSwap metadata * Fix module.d.ts * Address pr comments * Clarification on comments Co-authored-by: Yutaro Mori --- Cargo.lock | 10 +- token-swap/js/cli/token-swap-test.js | 40 ++- token-swap/js/client/token-swap.js | 358 ++++++++++++++------------- token-swap/js/client/util/account.js | 19 ++ token-swap/js/module.d.ts | 46 ++-- token-swap/js/module.flow.js | 46 ++-- token-swap/program/src/processor.rs | 1 + token-swap/program/src/state.rs | 41 ++- 8 files changed, 306 insertions(+), 255 deletions(-) create mode 100644 token-swap/js/client/util/account.js diff --git a/Cargo.lock b/Cargo.lock index 1a1c4f70748976..6dbf195cd09553 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -562,13 +562,12 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "2.1.0" -source = "git+https://github.com/garious/curve25519-dalek?rev=60efef3553d6bf3d7f3b09b5f97acd54d72529ff#60efef3553d6bf3d7f3b09b5f97acd54d72529ff" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" dependencies = [ - "borsh", "byteorder", "digest 0.8.1", "rand_core", - "serde", "subtle 2.2.3", "zeroize", ] @@ -576,12 +575,13 @@ dependencies = [ [[package]] name = "curve25519-dalek" version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" +source = "git+https://github.com/garious/curve25519-dalek?rev=60efef3553d6bf3d7f3b09b5f97acd54d72529ff#60efef3553d6bf3d7f3b09b5f97acd54d72529ff" dependencies = [ + "borsh", "byteorder", "digest 0.8.1", "rand_core", + "serde", "subtle 2.2.3", "zeroize", ] diff --git a/token-swap/js/cli/token-swap-test.js b/token-swap/js/cli/token-swap-test.js index 2936996c51c0a7..f4031a9591ebd7 100644 --- a/token-swap/js/cli/token-swap-test.js +++ b/token-swap/js/cli/token-swap-test.js @@ -194,24 +194,31 @@ export async function createTokenSwap(): Promise { swapPayer, tokenSwapAccount, authority, + nonce, tokenAccountA, tokenAccountB, tokenPool.publicKey, tokenAccountPool, + tokenSwapProgramId, tokenProgramId, - nonce, 1, 4, + ); + + console.log('loading token swap'); + const fetchedTokenSwap = await TokenSwap.loadTokenSwap( + connection, + tokenSwapAccount.publicKey, tokenSwapProgramId, + swapPayer, ); - console.log('getting token swap'); - const swapInfo = await tokenSwap.getInfo(); - assert(swapInfo.tokenAccountA.equals(tokenAccountA)); - assert(swapInfo.tokenAccountB.equals(tokenAccountB)); - assert(swapInfo.tokenPool.equals(tokenPool.publicKey)); - assert(1 == swapInfo.feesNumerator.toNumber()); - assert(4 == swapInfo.feesDenominator.toNumber()); + assert(fetchedTokenSwap.tokenProgramId.equals(tokenProgramId)); + assert(fetchedTokenSwap.tokenAccountA.equals(tokenAccountA)); + assert(fetchedTokenSwap.tokenAccountB.equals(tokenAccountB)); + assert(fetchedTokenSwap.poolToken.equals(tokenPool.publicKey)); + assert(1 == fetchedTokenSwap.feeNumerator.toNumber()); + assert(4 == fetchedTokenSwap.feeDenominator.toNumber()); } export async function deposit(): Promise { @@ -232,18 +239,12 @@ export async function deposit(): Promise { await mintB.approve(userAccountB, authority, owner, [], tokenB); console.log('Creating depositor pool token account'); const newAccountPool = await tokenPool.createAccount(owner.publicKey); - const [tokenProgramId] = await GetPrograms(connection); console.log('Depositing into swap'); await tokenSwap.deposit( - authority, userAccountA, userAccountB, - tokenAccountA, - tokenAccountB, - tokenPool.publicKey, newAccountPool, - tokenProgramId, POOL_TOKEN_AMOUNT, tokenA, tokenB, @@ -283,18 +284,12 @@ export async function withdraw(): Promise { [], POOL_TOKEN_AMOUNT, ); - const [tokenProgramId] = await GetPrograms(connection); console.log('Withdrawing pool tokens for A and B tokens'); await tokenSwap.withdraw( - authority, - tokenPool.publicKey, - tokenAccountPool, - tokenAccountA, - tokenAccountB, userAccountA, userAccountB, - tokenProgramId, + tokenAccountPool, POOL_TOKEN_AMOUNT, tokenA, tokenB, @@ -323,16 +318,13 @@ export async function swap(): Promise { await mintA.approve(userAccountA, authority, owner, [], SWAP_AMOUNT_IN); console.log('Creating swap token b account'); let userAccountB = await mintB.createAccount(owner.publicKey); - const [tokenProgramId] = await GetPrograms(connection); console.log('Swapping'); await tokenSwap.swap( - authority, userAccountA, tokenAccountA, tokenAccountB, userAccountB, - tokenProgramId, SWAP_AMOUNT_IN, SWAP_AMOUNT_OUT, ); diff --git a/token-swap/js/client/token-swap.js b/token-swap/js/client/token-swap.js index 4daf6dbf9ba5e0..12506045d541c4 100644 --- a/token-swap/js/client/token-swap.js +++ b/token-swap/js/client/token-swap.js @@ -16,6 +16,7 @@ import { import * as Layout from './layout'; import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction'; +import {loadAccount} from './util/account'; /** * Some amount of tokens @@ -53,78 +54,74 @@ export class Numberu64 extends BN { } /** - * Information about a token swap + * @private + */ +export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struct( + [ + BufferLayout.u8('isInitialized'), + BufferLayout.u8('nonce'), + Layout.publicKey('tokenProgramId'), + Layout.publicKey('tokenAccountA'), + Layout.publicKey('tokenAccountB'), + Layout.publicKey('tokenPool'), + Layout.uint64('feeNumerator'), + Layout.uint64('feeDenominator'), + ], +); + +/** + * A program to exchange tokens against a pool of liquidity */ -type TokenSwapInfo = {| +export class TokenSwap { /** - * Nonce. Used to generate the valid program address in the program + * @private */ - nonce: number, + connection: Connection; /** - * Token A. The Liquidity token is issued against this value. + * Program Identifier for the Swap program */ - tokenAccountA: PublicKey, + swapProgramId: PublicKey; /** - * Token B + * Program Identifier for the Token program */ - tokenAccountB: PublicKey, + tokenProgramId: PublicKey; + /** - * Pool tokens are issued when A or B tokens are deposited - * Pool tokens can be withdrawn back to the original A or B token + * The public key identifying this swap program */ - tokenPool: PublicKey, + tokenSwap: PublicKey; /** - * Fee numerator + * The public key for the liquidity pool token mint */ - feesNumerator: Numberu64, + poolToken: PublicKey; /** - * Fee denominator + * Authority */ - feesDenominator: Numberu64, + authority: PublicKey; /** - * Fee ratio applied to the input token amount prior to output calculation + * The public key for the first token account of the trading pair */ - feeRatio: number, -|}; - -/** - * @private - */ -export const TokenSwapLayout: typeof BufferLayout.Structure = BufferLayout.struct( - [ - BufferLayout.u8('isInitialized'), - BufferLayout.u8('nonce'), - Layout.publicKey('tokenAccountA'), - Layout.publicKey('tokenAccountB'), - Layout.publicKey('tokenPool'), - Layout.uint64('feesNumerator'), - Layout.uint64('feesDenominator'), - ], -); + tokenAccountA: PublicKey; -/** - * An ERC20-like Token - */ -export class TokenSwap { /** - * @private + * The public key for the second token account of the trading pair */ - connection: Connection; + tokenAccountB: PublicKey; /** - * The public key identifying this token + * Fee numerator */ - tokenSwap: PublicKey; + feeNumerator: Numberu64; /** - * Program Identifier for the Token Swap program + * Fee denominator */ - programId: PublicKey; + feeDenominator: Numberu64; /** * Fee payer @@ -135,17 +132,41 @@ export class TokenSwap { * 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 - * @param payer Payer of fees + * @param tokenSwap The token swap account + * @param swapProgramId The program ID of the token-swap program + * @param tokenProgramId The program ID of the token program + * @param poolToken The pool token + * @param authority The authority over the swap and accounts + * @param tokenAccountA: The token swap's Token A account + * @param tokenAccountB: The token swap's Token B account + * @param payer Pays for the transaction */ constructor( connection: Connection, tokenSwap: PublicKey, - programId: PublicKey, + swapProgramId: PublicKey, + tokenProgramId: PublicKey, + poolToken: PublicKey, + authority: PublicKey, + tokenAccountA: PublicKey, + tokenAccountB: PublicKey, + feeNumerator: Numberu64, + feeDenominator: Numberu64, payer: Account, ) { - Object.assign(this, {connection, tokenSwap, programId, payer}); + Object.assign(this, { + connection, + tokenSwap, + swapProgramId, + tokenProgramId, + poolToken, + authority, + tokenAccountA, + tokenAccountB, + feeNumerator, + feeDenominator, + payer, + }); } /** @@ -209,6 +230,46 @@ export class TokenSwap { }); } + static async loadTokenSwap( + connection: Connection, + address: PublicKey, + programId: PublicKey, + payer: Account, + ): Promise { + const data = await loadAccount(connection, address, programId); + const tokenSwapData = TokenSwapLayout.decode(data); + if (!tokenSwapData.isInitialized) { + throw new Error(`Invalid token swap state`); + } + + const [authority] = await PublicKey.findProgramAddress( + [address.toBuffer()], + programId, + ); + + const poolToken = new PublicKey(tokenSwapData.tokenPool); + const tokenAccountA = new PublicKey(tokenSwapData.tokenAccountA); + const tokenAccountB = new PublicKey(tokenSwapData.tokenAccountB); + const tokenProgramId = new PublicKey(tokenSwapData.tokenProgramId); + + const feeNumerator = Numberu64.fromBuffer(tokenSwapData.feeNumerator); + const feeDenominator = Numberu64.fromBuffer(tokenSwapData.feeDenominator); + + return new TokenSwap( + connection, + address, + programId, + tokenProgramId, + poolToken, + authority, + tokenAccountA, + tokenAccountB, + feeNumerator, + feeDenominator, + payer, + ); + } + /** * Create a new Token Swap * @@ -216,14 +277,15 @@ export class TokenSwap { * @param payer Pays for the transaction * @param tokenSwapAccount The token swap account * @param authority The authority over the swap and accounts - * @param tokenAccountA: The Swap's Token A account - * @param tokenAccountB: The Swap's Token B account - * @param tokenPool The pool token - * @param tokenAccountPool The pool token account - * @param tokenProgramId The program id of the token program + * @param nonce The nonce used to generate the authority + * @param tokenAccountA: The token swap's Token A account + * @param tokenAccountB: The token swap's Token B account + * @param poolToken The pool token + * @param tokenAccountPool The token swap's pool token account + * @param tokenProgramId The program ID of the token program + * @param swapProgramId The program ID of the token-swap program * @param feeNumerator Numerator of the fee ratio * @param feeDenominator Denominator of the fee ratio - * @param swapProgramId Program ID of the token-swap program * @return Token object for the newly minted token, Public key of the account holding the total supply of new tokens */ static async createTokenSwap( @@ -231,21 +293,28 @@ export class TokenSwap { payer: Account, tokenSwapAccount: Account, authority: PublicKey, + nonce: number, tokenAccountA: PublicKey, tokenAccountB: PublicKey, - tokenPool: PublicKey, + poolToken: PublicKey, tokenAccountPool: PublicKey, + swapProgramId: PublicKey, tokenProgramId: PublicKey, - nonce: number, feeNumerator: number, feeDenominator: number, - swapProgramId: PublicKey, ): Promise { let transaction; const tokenSwap = new TokenSwap( connection, tokenSwapAccount.publicKey, swapProgramId, + tokenProgramId, + poolToken, + authority, + tokenAccountA, + tokenAccountB, + new Numberu64(feeNumerator), + new Numberu64(feeDenominator), payer, ); @@ -270,7 +339,7 @@ export class TokenSwap { nonce, tokenAccountA, tokenAccountB, - tokenPool, + poolToken, tokenAccountPool, tokenProgramId, swapProgramId, @@ -291,60 +360,20 @@ export class TokenSwap { } /** - * Retrieve tokenSwap information - */ - async getInfo(): Promise { - const accountInfo = await this.connection.getAccountInfo(this.tokenSwap); - if (accountInfo === null) { - throw new Error('Failed to find token swap account'); - } - if (!accountInfo.owner.equals(this.programId)) { - throw new Error( - `Invalid token swap owner: ${JSON.stringify(accountInfo.owner)}`, - ); - } - - const data = Buffer.from(accountInfo.data); - const tokenSwapInfo = TokenSwapLayout.decode(data); - if (!tokenSwapInfo.isInitialized) { - throw new Error(`Invalid token swap state`); - } - // already properly filled in - // tokenSwapInfo.nonce = tokenSwapInfo.nonce; - 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(); - - return tokenSwapInfo; - } - - /** - * Swap the tokens in the pool + * Swap token A for token B * - * @param authority Authority - * @param source Source account - * @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 + * @param userSource User's source token account + * @param poolSource Pool's source token account + * @param poolDestination Pool's destination token account + * @param userDestination User's destination token account + * @param amountIn Amount to transfer from source account + * @param minimumAmountOut Minimum amount of tokens the user will receive */ async swap( - authority: PublicKey, - source: PublicKey, - swapSource: PublicKey, - swapDestination: PublicKey, - destination: PublicKey, - tokenProgramId: PublicKey, + userSource: PublicKey, + poolSource: PublicKey, + poolDestination: PublicKey, + userDestination: PublicKey, amountIn: number | Numberu64, minimumAmountOut: number | Numberu64, ): Promise { @@ -354,13 +383,13 @@ export class TokenSwap { new Transaction().add( TokenSwap.swapInstruction( this.tokenSwap, - authority, - source, - swapSource, - swapDestination, - destination, - this.programId, - tokenProgramId, + this.authority, + userSource, + poolSource, + poolDestination, + userDestination, + this.swapProgramId, + this.tokenProgramId, amountIn, minimumAmountOut, ), @@ -372,10 +401,10 @@ export class TokenSwap { static swapInstruction( tokenSwap: PublicKey, authority: PublicKey, - source: PublicKey, - swapSource: PublicKey, - swapDestination: PublicKey, - destination: PublicKey, + userSource: PublicKey, + poolSource: PublicKey, + poolDestination: PublicKey, + userDestination: PublicKey, swapProgramId: PublicKey, tokenProgramId: PublicKey, amountIn: number | Numberu64, @@ -400,10 +429,10 @@ export class TokenSwap { const keys = [ {pubkey: tokenSwap, isSigner: false, isWritable: false}, {pubkey: authority, isSigner: false, isWritable: false}, - {pubkey: source, isSigner: false, isWritable: true}, - {pubkey: swapSource, isSigner: false, isWritable: true}, - {pubkey: swapDestination, isSigner: false, isWritable: true}, - {pubkey: destination, isSigner: false, isWritable: true}, + {pubkey: userSource, isSigner: false, isWritable: true}, + {pubkey: poolSource, isSigner: false, isWritable: true}, + {pubkey: poolDestination, isSigner: false, isWritable: true}, + {pubkey: userDestination, isSigner: false, isWritable: true}, {pubkey: tokenProgramId, isSigner: false, isWritable: false}, ]; return new TransactionInstruction({ @@ -414,27 +443,18 @@ export class TokenSwap { } /** - * Deposit some tokens into the pool - * - * @param authority Authority - * @param sourceA Source account A - * @param sourceB Source account B - * @param intoA Base account A to deposit into - * @param intoB Base account B to deposit into - * @param poolToken Pool token - * @param poolAccount Pool account to deposit the generated tokens - * @param tokenProgramId Token program id - * @param amount Amount of pool token to deposit, token A and B amount are set by the exchange rate relative to the total pool token supply + * Deposit tokens into the pool + * @param userAccountA User account for token A + * @param userAccountB User account for token B + * @param poolAccount User account for pool token + * @param poolTokenAmount Amount of pool tokens to mint + * @param maximumTokenA The maximum amount of token A to deposit + * @param maximumTokenB The maximum amount of token B to deposit */ async deposit( - authority: PublicKey, - sourceA: PublicKey, - sourceB: PublicKey, - intoA: PublicKey, - intoB: PublicKey, - poolToken: PublicKey, + userAccountA: PublicKey, + userAccountB: PublicKey, poolAccount: PublicKey, - tokenProgramId: PublicKey, poolTokenAmount: number | Numberu64, maximumTokenA: number | Numberu64, maximumTokenB: number | Numberu64, @@ -445,15 +465,15 @@ export class TokenSwap { new Transaction().add( TokenSwap.depositInstruction( this.tokenSwap, - authority, - sourceA, - sourceB, - intoA, - intoB, - poolToken, + this.authority, + userAccountA, + userAccountB, + this.tokenAccountA, + this.tokenAccountB, + this.poolToken, poolAccount, - this.programId, - tokenProgramId, + this.swapProgramId, + this.tokenProgramId, poolTokenAmount, maximumTokenA, maximumTokenB, @@ -515,27 +535,19 @@ export class TokenSwap { } /** - * Withdraw the token from the pool at the current ratio + * Withdraw tokens from the pool * - * @param authority Authority - * @param sourcePoolAccount Source pool account - * @param poolToken Pool token - * @param fromA Base account A to withdraw from - * @param fromB Base account B to withdraw from - * @param userAccountA Token A user account - * @param userAccountB token B user account - * @param tokenProgramId Token program id - * @param amount Amount of pool token to withdraw, token A and B amount are set by the exchange rate relative to the total pool token supply + * @param userAccountA User account for token A + * @param userAccountB User account for token B + * @param poolAccount User account for pool token + * @param poolTokenAmount Amount of pool tokens to burn + * @param minimumTokenA The minimum amount of token A to withdraw + * @param minimumTokenB The minimum amount of token B to withdraw */ async withdraw( - authority: PublicKey, - poolMint: PublicKey, - sourcePoolAccount: PublicKey, - fromA: PublicKey, - fromB: PublicKey, userAccountA: PublicKey, userAccountB: PublicKey, - tokenProgramId: PublicKey, + poolAccount: PublicKey, poolTokenAmount: number | Numberu64, minimumTokenA: number | Numberu64, minimumTokenB: number | Numberu64, @@ -546,15 +558,15 @@ export class TokenSwap { new Transaction().add( TokenSwap.withdrawInstruction( this.tokenSwap, - authority, - poolMint, - sourcePoolAccount, - fromA, - fromB, + this.authority, + this.poolToken, + poolAccount, + this.tokenAccountA, + this.tokenAccountB, userAccountA, userAccountB, - this.programId, - tokenProgramId, + this.swapProgramId, + this.tokenProgramId, poolTokenAmount, minimumTokenA, minimumTokenB, diff --git a/token-swap/js/client/util/account.js b/token-swap/js/client/util/account.js new file mode 100644 index 00000000000000..e29fcae722f666 --- /dev/null +++ b/token-swap/js/client/util/account.js @@ -0,0 +1,19 @@ +import type {Connection} from '@solana/web3.js'; +import {PublicKey} from '@solana/web3.js'; + +export async function loadAccount( + connection: Connection, + address: PublicKey, + programId: PublicKey, +): Buffer { + const accountInfo = await connection.getAccountInfo(address); + if (accountInfo === null) { + throw new Error('Failed to find account'); + } + + if (!accountInfo.owner.equals(programId)) { + throw new Error(`Invalid owner: ${JSON.stringify(accountInfo.owner)}`); + } + + return Buffer.from(accountInfo.data); +} diff --git a/token-swap/js/module.d.ts b/token-swap/js/module.d.ts index f821c1f664ef77..0cb1a989ece5f7 100644 --- a/token-swap/js/module.d.ts +++ b/token-swap/js/module.d.ts @@ -16,23 +16,20 @@ declare module '@solana/spl-token-swap' { static fromBuffer(buffer: Buffer): Numberu64; } - export type TokenSwapInfo = { - nonce: number; - tokenAccountA: PublicKey; - tokenAccountB: PublicKey; - tokenPool: PublicKey; - feesNumerator: Numberu64; - feesDenominator: Numberu64; - feeRatio: number; - }; - export const TokenSwapLayout: Layout; export class TokenSwap { constructor( connection: Connection, + swapProgramId: PublicKey, + tokenProgramId: PublicKey, tokenSwap: PublicKey, - programId: PublicKey, + poolToken: PublicKey, + authority: PublicKey, + tokenAccountA: PublicKey, + tokenAccountB: PublicKey, + feeNumerator: Numberu64, + feeDenominator: Numberu64, payer: Account, ); @@ -54,6 +51,13 @@ declare module '@solana/spl-token-swap' { feeDenominator: number, ): TransactionInstruction; + static loadTokenSwap( + connection: Connection, + address: PublicKey, + programId: PublicKey, + payer: Account, + ): Promise; + static createTokenSwap( connection: Connection, payer: Account, @@ -70,15 +74,11 @@ declare module '@solana/spl-token-swap' { swapProgramId: PublicKey, ): Promise; - getInfo(): Promise; - swap( - authority: PublicKey, - source: PublicKey, - swapSource: PublicKey, - swapDestination: PublicKey, - destination: PublicKey, - tokenProgramId: PublicKey, + userSource: PublicKey, + poolSource: PublicKey, + poolDestination: PublicKey, + userDestination: PublicKey, amountIn: number | Numberu64, minimumAmountOut: number | Numberu64, ): Promise; @@ -86,10 +86,10 @@ declare module '@solana/spl-token-swap' { static swapInstruction( tokenSwap: PublicKey, authority: PublicKey, - source: PublicKey, - swapSource: PublicKey, - swapDestination: PublicKey, - destination: PublicKey, + userSource: PublicKey, + poolSource: PublicKey, + poolDestination: PublicKey, + userDestination: PublicKey, swapProgramId: PublicKey, tokenProgramId: PublicKey, amountIn: number | Numberu64, diff --git a/token-swap/js/module.flow.js b/token-swap/js/module.flow.js index ebe75e0b9d0361..5212163df295ce 100644 --- a/token-swap/js/module.flow.js +++ b/token-swap/js/module.flow.js @@ -12,23 +12,20 @@ declare module '@solana/spl-token-swap' { static fromBuffer(buffer: Buffer): Numberu64; } - declare export type TokenSwapInfo = {| - nonce: number, - tokenAccountA: PublicKey, - tokenAccountB: PublicKey, - tokenPool: PublicKey, - feesNumerator: Numberu64, - feesDenominator: Numberu64, - feeRatio: number, - |}; - declare export var TokenSwapLayout: Layout; declare export class TokenSwap { constructor( connection: Connection, + swapProgramId: PublicKey, + tokenProgramId: PublicKey, tokenSwap: PublicKey, - programId: PublicKey, + poolToken: PublicKey, + authority: PublicKey, + tokenAccountA: PublicKey, + tokenAccountB: PublicKey, + feeNumerator: Numberu64, + feeDenominator: Numberu64, payer: Account, ): TokenSwap; @@ -50,6 +47,13 @@ declare module '@solana/spl-token-swap' { feeDenominator: number, ): TransactionInstruction; + static loadTokenSwap( + connection: Connection, + address: PublicKey, + programId: PublicKey, + payer: Account, + ): Promise; + static createTokenSwap( connection: Connection, payer: Account, @@ -66,15 +70,11 @@ declare module '@solana/spl-token-swap' { programId: PublicKey, ): Promise; - getInfo(): Promise; - swap( - authority: PublicKey, - source: PublicKey, - swapSource: PublicKey, - swapDestination: PublicKey, - destination: PublicKey, - tokenProgramId: PublicKey, + userSource: PublicKey, + poolSource: PublicKey, + poolDestination: PublicKey, + userDestination: PublicKey, amountIn: number | Numberu64, minimumAmountOut: number | Numberu64, ): Promise; @@ -82,10 +82,10 @@ declare module '@solana/spl-token-swap' { static swapInstruction( tokenSwap: PublicKey, authority: PublicKey, - source: PublicKey, - swapSource: PublicKey, - swapDestination: PublicKey, - destination: PublicKey, + userSource: PublicKey, + poolSource: PublicKey, + poolDestination: PublicKey, + userDestination: PublicKey, swapProgramId: PublicKey, tokenProgramId: PublicKey, amountIn: number | Numberu64, diff --git a/token-swap/program/src/processor.rs b/token-swap/program/src/processor.rs index e71f9d5ae1df2b..5f55c2a0052e22 100644 --- a/token-swap/program/src/processor.rs +++ b/token-swap/program/src/processor.rs @@ -214,6 +214,7 @@ impl Processor { let obj = SwapInfo { is_initialized: true, nonce, + token_program_id: *token_program_info.key, token_a: *token_a_info.key, token_b: *token_b_info.key, pool_mint: *pool_mint_info.key, diff --git a/token-swap/program/src/state.rs b/token-swap/program/src/state.rs index ae2cd55377066a..c21a083e4fe860 100644 --- a/token-swap/program/src/state.rs +++ b/token-swap/program/src/state.rs @@ -19,6 +19,10 @@ pub struct SwapInfo { /// authority over the swap's token A account, token B account, and pool /// token mint. pub nonce: u8, + + /// Program ID of the tokens being exchanged. + pub token_program_id: Pubkey, + /// Token A /// The Liquidity token is issued against this value. pub token_a: Pubkey, @@ -27,6 +31,7 @@ pub struct SwapInfo { /// Pool tokens are issued when A or B tokens are deposited. /// Pool tokens can be withdrawn back to the original A or B token. pub pool_mint: Pubkey, + /// Numerator of fee applied to the input token amount prior to output calculation. pub fee_numerator: u64, /// Denominator of fee applied to the input token amount prior to output calculation. @@ -41,14 +46,22 @@ impl IsInitialized for SwapInfo { } impl Pack for SwapInfo { - const LEN: usize = 114; + const LEN: usize = 146; /// Unpacks a byte buffer into a [SwapInfo](struct.SwapInfo.html). fn unpack_from_slice(input: &[u8]) -> Result { - let input = array_ref![input, 0, 114]; + let input = array_ref![input, 0, 146]; #[allow(clippy::ptr_offset_with_cast)] - let (is_initialized, nonce, token_a, token_b, pool_mint, fee_numerator, fee_denominator) = - array_refs![input, 1, 1, 32, 32, 32, 8, 8]; + let ( + is_initialized, + nonce, + token_program_id, + token_a, + token_b, + pool_mint, + fee_numerator, + fee_denominator, + ) = array_refs![input, 1, 1, 32, 32, 32, 32, 8, 8]; Ok(Self { is_initialized: match is_initialized { [0] => false, @@ -56,6 +69,7 @@ impl Pack for SwapInfo { _ => return Err(ProgramError::InvalidAccountData), }, nonce: nonce[0], + token_program_id: Pubkey::new_from_array(*token_program_id), token_a: Pubkey::new_from_array(*token_a), token_b: Pubkey::new_from_array(*token_b), pool_mint: Pubkey::new_from_array(*pool_mint), @@ -65,11 +79,20 @@ impl Pack for SwapInfo { } fn pack_into_slice(&self, output: &mut [u8]) { - let output = array_mut_ref![output, 0, 114]; - let (is_initialized, nonce, token_a, token_b, pool_mint, fee_numerator, fee_denominator) = - mut_array_refs![output, 1, 1, 32, 32, 32, 8, 8]; + let output = array_mut_ref![output, 0, 146]; + let ( + is_initialized, + nonce, + token_program_id, + token_a, + token_b, + pool_mint, + fee_numerator, + fee_denominator, + ) = mut_array_refs![output, 1, 1, 32, 32, 32, 32, 8, 8]; is_initialized[0] = self.is_initialized as u8; nonce[0] = self.nonce; + token_program_id.copy_from_slice(self.token_program_id.as_ref()); token_a.copy_from_slice(self.token_a.as_ref()); token_b.copy_from_slice(self.token_b.as_ref()); pool_mint.copy_from_slice(self.pool_mint.as_ref()); @@ -85,9 +108,11 @@ mod tests { #[test] fn test_swap_info_packing() { let nonce = 255; + let token_program_id_raw = [1u8; 32]; let token_a_raw = [1u8; 32]; let token_b_raw = [2u8; 32]; let pool_mint_raw = [3u8; 32]; + let token_program_id = Pubkey::new_from_array(token_program_id_raw); let token_a = Pubkey::new_from_array(token_a_raw); let token_b = Pubkey::new_from_array(token_b_raw); let pool_mint = Pubkey::new_from_array(pool_mint_raw); @@ -97,6 +122,7 @@ mod tests { let swap_info = SwapInfo { is_initialized, nonce, + token_program_id, token_a, token_b, pool_mint, @@ -112,6 +138,7 @@ mod tests { let mut packed = vec![]; packed.push(1 as u8); packed.push(nonce); + packed.extend_from_slice(&token_program_id_raw); packed.extend_from_slice(&token_a_raw); packed.extend_from_slice(&token_b_raw); packed.extend_from_slice(&pool_mint_raw);