Skip to content

Commit

Permalink
ts: swap referrer fee (#98)
Browse files Browse the repository at this point in the history
* ts: swap referrer fee

* version bump
  • Loading branch information
codewithgun authored Aug 3, 2023
1 parent 31cd828 commit ffd589f
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 10 deletions.
2 changes: 1 addition & 1 deletion ts-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mercurial-finance/dynamic-amm-sdk",
"version": "0.4.8",
"version": "0.4.9",
"description": "Mercurial Vaults SDK is a typescript library that allows you to interact with Mercurial v2's AMM.",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
20 changes: 18 additions & 2 deletions ts-client/src/amm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -844,9 +844,16 @@ export default class AmmImpl implements AmmImplementation {
* @param {PublicKey} inTokenMint - The mint of the token you're swapping from.
* @param {BN} inAmountLamport - The amount of the input token you want to swap.
* @param {BN} outAmountLamport - The minimum amount of the output token you want to receive.
* @param {PublicKey} [referrerToken] - The referrer fee token account. The mint of the token account must matches inTokenMint. 20% of admin trade fee.
* @returns A transaction object
*/
public async swap(owner: PublicKey, inTokenMint: PublicKey, inAmountLamport: BN, outAmountLamport: BN) {
public async swap(
owner: PublicKey,
inTokenMint: PublicKey,
inAmountLamport: BN,
outAmountLamport: BN,
referrerToken?: PublicKey,
) {
const [sourceToken, destinationToken] =
this.tokenA.address === inTokenMint.toBase58()
? [this.poolState.tokenAMint, this.poolState.tokenBMint]
Expand Down Expand Up @@ -874,6 +881,15 @@ export default class AmmImpl implements AmmImplementation {
unwrapSOLIx && postInstructions.push(unwrapSOLIx);
}

const remainingAccounts = this.swapCurve.getRemainingAccounts();
if (referrerToken) {
remainingAccounts.push({
isSigner: false,
isWritable: true,
pubkey: referrerToken,
});
}

const swapTx = await this.program.methods
.swap(inAmountLamport, outAmountLamport)
.accounts({
Expand All @@ -893,7 +909,7 @@ export default class AmmImpl implements AmmImplementation {
tokenProgram: TOKEN_PROGRAM_ID,
vaultProgram: this.vaultProgram.programId,
})
.remainingAccounts(this.swapCurve.getRemainingAccounts())
.remainingAccounts(remainingAccounts)
.preInstructions(preInstructions)
.postInstructions(postInstructions)
.transaction();
Expand Down
95 changes: 88 additions & 7 deletions ts-client/src/amm/tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Cluster, PublicKey } from '@solana/web3.js';
import AmmImpl from '../index';
import { DEFAULT_SLIPPAGE, MAINNET_POOL, DEVNET_POOL, DEVNET_COIN } from '../constants';
import { AnchorProvider, BN } from '@project-serum/anchor';
import { TokenListProvider, TokenInfo } from '@solana/spl-token-registry';
import { TokenInfo, TokenListProvider } from '@solana/spl-token-registry';
import { Cluster, Keypair, PublicKey } from '@solana/web3.js';
import { DEFAULT_SLIPPAGE, DEVNET_COIN, DEVNET_POOL, MAINNET_POOL } from '../constants';
import AmmImpl from '../index';
import { calculateSwapQuote, getOnchainTime } from '../utils';
import { DEVNET, MAINNET, airDropSol, mockWallet } from './utils';
import { DEVNET, MAINNET, airDropSol, getOrCreateATA, mockWallet } from './utils';
import { NATIVE_MINT } from '@solana/spl-token';

describe('Interact with Devnet pool', () => {
const provider = new AnchorProvider(DEVNET.connection, mockWallet, {
Expand All @@ -17,6 +18,8 @@ describe('Interact with Devnet pool', () => {
let currentDepegPoolBalance: BN;
let currentStablePoolBalance: BN;

let referrer = Keypair.generate();

beforeAll(async () => {
await airDropSol(DEVNET.connection, mockWallet.publicKey);

Expand Down Expand Up @@ -88,6 +91,45 @@ describe('Interact with Devnet pool', () => {
}
});

test('Swap SOL → USDT with referrer fee', async () => {
const referrerSolAta = await getOrCreateATA(provider.connection, NATIVE_MINT, referrer.publicKey, mockWallet.payer);
const inAmountLamport = new BN(0.1 * 10 ** cpPool.tokenB.decimals);

const { swapOutAmount, minSwapOutAmount } = cpPool.getSwapQuote(
new PublicKey(cpPool.tokenB.address),
inAmountLamport,
DEFAULT_SLIPPAGE,
);
expect(swapOutAmount.toNumber()).toBeGreaterThan(0);

const swapTx = await cpPool.swap(
mockWallet.publicKey,
new PublicKey(cpPool.tokenB.address),
inAmountLamport,
minSwapOutAmount,
referrerSolAta,
);

try {
const beforeReferrerTokenBalance = await provider.connection
.getTokenAccountBalance(referrerSolAta)
.then((r) => r.value.uiAmount);

const swapResult = await provider.sendAndConfirm(swapTx);
console.log('Swap Result of SOL → USDT', swapResult);
expect(typeof swapResult).toBe('string');

const afterReferrerTokenBalance = await provider.connection
.getTokenAccountBalance(referrerSolAta)
.then((r) => r.value.uiAmount);

expect(afterReferrerTokenBalance!).toBeGreaterThan(beforeReferrerTokenBalance!);
} catch (error: any) {
console.trace(error);
throw new Error(error.message);
}
});

test('Swap USDT → SOL', async () => {
const inAmountLamport = new BN(0.1 * 10 ** cpPool.tokenA.decimals);

Expand Down Expand Up @@ -115,7 +157,7 @@ describe('Interact with Devnet pool', () => {
}
});

test('SWAP USDT -> USDC', async () => {
test('Swap USDT -> USDC', async () => {
const inAmountLamport = new BN(0.1 * 10 ** stablePool.tokenA.decimals);
const { swapOutAmount, minSwapOutAmount } = stablePool.getSwapQuote(
new PublicKey(stablePool.tokenA.address),
Expand All @@ -141,7 +183,7 @@ describe('Interact with Devnet pool', () => {
}
});

test('SWAP USDC -> USDT', async () => {
test('Swap USDC -> USDT', async () => {
const inAmountLamport = new BN(0.1 * 10 ** stablePool.tokenB.decimals);
const { swapOutAmount, minSwapOutAmount } = stablePool.getSwapQuote(
new PublicKey(stablePool.tokenB.address),
Expand Down Expand Up @@ -194,6 +236,45 @@ describe('Interact with Devnet pool', () => {
}
});

test('Swap SOL → mSOL with referrer fee', async () => {
const referrerSolAta = await getOrCreateATA(provider.connection, NATIVE_MINT, referrer.publicKey, mockWallet.payer);
const inAmountLamport = new BN(0.01 * 10 ** depegPool.tokenA.decimals);

const { swapOutAmount, minSwapOutAmount } = depegPool.getSwapQuote(
new PublicKey(depegPool.tokenA.address),
inAmountLamport,
DEFAULT_SLIPPAGE,
);
expect(swapOutAmount.toNumber()).toBeGreaterThan(0);

const swapTx = await depegPool.swap(
mockWallet.publicKey,
new PublicKey(depegPool.tokenA.address),
inAmountLamport,
minSwapOutAmount,
referrerSolAta,
);

try {
const beforeReferrerTokenBalance = await provider.connection
.getTokenAccountBalance(referrerSolAta)
.then((r) => r.value.uiAmount);

const swapResult = await provider.sendAndConfirm(swapTx);
console.log('Swap Result of SOL → mSOL', swapResult);
expect(typeof swapResult).toBe('string');

const afterReferrerTokenBalance = await provider.connection
.getTokenAccountBalance(referrerSolAta)
.then((r) => r.value.uiAmount);

expect(afterReferrerTokenBalance!).toBeGreaterThan(beforeReferrerTokenBalance!);
} catch (error: any) {
console.trace(error);
throw new Error(error.message);
}
});

test('Swap mSOL → SOL', async () => {
const inAmountLamport = new BN(0.01 * 10 ** depegPool.tokenB.decimals);

Expand Down
8 changes: 8 additions & 0 deletions ts-client/src/amm/tests/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Wallet } from '@project-serum/anchor';
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes';
import { TOKEN_PROGRAM_ID, Token } from '@solana/spl-token';
import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';

export const airDropSol = async (connection: Connection, publicKey: PublicKey, amount = 1) => {
Expand All @@ -17,6 +18,13 @@ export const airDropSol = async (connection: Connection, publicKey: PublicKey, a
}
};

export const getOrCreateATA = async (connection: Connection, mint: PublicKey, owner: PublicKey, payer: Keypair) => {
const token = new Token(connection, mint, TOKEN_PROGRAM_ID, payer);
const ata = await token.getOrCreateAssociatedAccountInfo(owner);

return ata.address;
};

export const mockWallet = new Wallet(
process.env.WALLET_PRIVATE_KEY ? Keypair.fromSecretKey(bs58.decode(process.env.WALLET_PRIVATE_KEY)) : new Keypair(),
);
Expand Down

0 comments on commit ffd589f

Please sign in to comment.