Skip to content

Commit

Permalink
chore: jupiter tests, fix: swap tests
Browse files Browse the repository at this point in the history
  • Loading branch information
NickKelly1 committed Sep 10, 2024
1 parent 08e84b3 commit b66fd25
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 20 deletions.
27 changes: 15 additions & 12 deletions packages/swap/src/providers/jupiter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import {
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import {
DEFAULT_SLIPPAGE,
FEE_CONFIGS,
NATIVE_TOKEN_ADDRESS,
} from "@src/configs";
import { toBN } from "web3-utils";
import { TOKEN_AMOUNT_INFINITY_AND_BEYOND } from "../../utils/approvals";
import { extractComputeBudget } from "../../utils/solana";
import {
ProviderClass,
ProviderName,
Expand All @@ -34,10 +32,12 @@ import {
StatusOptionsResponse,
SolanaTransaction,
TokenNetworkType,
} from "@src/types";
import { TOKEN_AMOUNT_INFINITY_AND_BEYOND } from "@src/utils/approvals";
import { extractComputeBudget } from "@src/utils/solana";
import { toBN } from "web3-utils";
} from "../../types";
import {
DEFAULT_SLIPPAGE,
FEE_CONFIGS,
NATIVE_TOKEN_ADDRESS,
} from "../../configs";

/** Enables debug logging in this file */
const DEBUG = false;
Expand Down Expand Up @@ -160,9 +160,8 @@ const SPL_TOKEN_ATA_ACCOUNT_SIZE_BYTES = 165;
let debug: (...args: any[]) => void;
if (DEBUG) {
console.debug.bind(console);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
else {
} else {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
debug = (..._args: any[]) => {};
}

Expand Down Expand Up @@ -1331,6 +1330,10 @@ function getCreateAssociatedTokenAccountIdempotentInstruction(params: {
* @example new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v') // USDC
* @example new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB') // USDT
* @example new PublicKey('So11111111111111111111111111111111111111112') // Wrapped SOL
*
* USDC @see https://solscan.io/token/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
* USDT @see https://solscan.io/token/Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
* Wrapped SOL @see https://solscan.io/token/So11111111111111111111111111111111111111112
*/
mintPubkey: PublicKey;
/**
Expand Down
3 changes: 1 addition & 2 deletions packages/swap/src/providers/rango/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
TransactionType as RangoTransactionType,
} from "rango-sdk-basic";
import { Connection, VersionedTransaction } from "@solana/web3.js";
import { extractComputeBudget } from "@src/utils/solana";
import { extractComputeBudget } from "../../utils/solana";
import {
EVMTransaction,
getQuoteOptions,
Expand Down Expand Up @@ -124,7 +124,6 @@ class Rango extends ProviderClass {
}

async init(tokenList?: TokenType[]): Promise<void> {
(window as any).rango = this;
const resMeta = await rangoClient.meta({
excludeNonPopulars: true,
transactionTypes: [RangoTransactionType.EVM, RangoTransactionType.SOLANA],
Expand Down
9 changes: 3 additions & 6 deletions packages/swap/tests/changelly.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from "chai";
import Web3Eth from "web3-eth";
import { NATIVE_TOKEN_ADDRESS } from "../src/configs";
import Changelly from "../src/providers/changelly";
import {
Expand All @@ -14,13 +13,11 @@ import {
amount,
fromAddress,
toAddress,
nodeURL,
} from "./fixtures/mainnet/configs";

describe("Changelly Provider", () => {
// @ts-ignore
const web3eth = new Web3Eth(nodeURL);
const changelly = new Changelly(web3eth, SupportedNetworkName.Ethereum);
const changelly = new Changelly(SupportedNetworkName.Ethereum);
const init = changelly.init();
it("it should return a quote ", async () => {
await init;
Expand Down Expand Up @@ -70,15 +67,15 @@ describe("Changelly Provider", () => {
});

it("it should initialize other networks: Bitcoin", async () => {
const changelly2 = new Changelly(web3eth, SupportedNetworkName.Bitcoin);
const changelly2 = new Changelly(SupportedNetworkName.Bitcoin);
await changelly2.init();
const fromTokens = changelly2.getFromTokens();
expect(Object.values(fromTokens).length).to.be.eq(1);
expect(fromTokens[NATIVE_TOKEN_ADDRESS].name).to.be.eq("Bitcoin");
});

it("it should initialize other networks: Polkadot", async () => {
const changelly2 = new Changelly(web3eth, SupportedNetworkName.Polkadot);
const changelly2 = new Changelly(SupportedNetworkName.Polkadot);
await changelly2.init();
const fromTokens = changelly2.getFromTokens();
expect(Object.values(fromTokens).length).to.be.eq(1);
Expand Down
85 changes: 85 additions & 0 deletions packages/swap/tests/fixtures/solana/configs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { NetworkNames } from "@enkryptcom/types";
import { PublicKey } from "@solana/web3.js";
import { toBN } from "web3-utils";
import { NetworkType, TokenType, TokenTypeTo } from "../../../src/types";

export const nodeURL = "https://nodes.mewapi.io/rpc/sol";

export const amount = toBN("100000");

export const fromAddress = "CMGoYEKM8kSXwN9HzYiwRiZRXoMtEAQ98ZiPE9y67T38";
export const toAddress = "3zDT4WonZsGr6x6ysQeuhTHtabpdawZNsjhC6g1yZDEK";

export const fromTokenNative: TokenType = {
address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
decimals: 9,
logoURI: "",
name: "Solana",
symbol: "SOL",
rank: 1,
cgId: "solana",
type: NetworkType.Solana,
};

/** @see https://solscan.io/token/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v */
export const fromToken: TokenType = {
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
decimals: 6,
logoURI:
"https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v/logo.png",
name: "USDC",
symbol: "USDC",
rank: 5,
cgId: "usd-coin",
type: NetworkType.Solana,
};

/** @see https://solscan.io/token/So11111111111111111111111111111111111111112 */
export const toTokenWSOL: TokenTypeTo = {
address: "So11111111111111111111111111111111111111112",
decimals: 9,
logoURI:
"https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/So11111111111111111111111111111111111111112/logo.png",
name: "Wrapped SOL",
symbol: "WSOL",
rank: 15,
cgId: "wrapped-solana",
type: NetworkType.EVM,
networkInfo: {
name: NetworkNames.Solana,
isAddress: (address: string) => {
try {
// eslint-disable-next-line no-new
new PublicKey(address);
return Promise.resolve(true);
} catch (err) {
return Promise.resolve(false);
}
},
},
};

/** @see https://solscan.io/token/Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB */
export const toToken: TokenTypeTo = {
address: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
decimals: 6,
logoURI:
"https://raw.githubusercontent.com/solana-labs/token-list/main/assets/mainnet/Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB/logo.svg",
name: "Tether",
symbol: "USDT",
rank: 10,
cgId: "tether",
type: NetworkType.Solana,
networkInfo: {
name: NetworkNames.Solana,
isAddress: (address: string) => {
try {
// eslint-disable-next-line no-new
new PublicKey(address);
return Promise.resolve(true);
} catch (err) {
return Promise.resolve(false);
}
},
},
};
148 changes: 148 additions & 0 deletions packages/swap/tests/jupiter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {
AddressLookupTableAccount,
ComputeBudgetInstruction,
ComputeBudgetProgram,
Connection,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import { expect } from "chai";
import { Jupiter } from "../src/providers/jupiter";
import {
ProviderName,
ProviderQuoteResponse,
ProviderSwapResponse,
SolanaTransaction,
SupportedNetworkName,
WalletIdentifier,
} from "../src/types";
import {
fromToken,
amount,
fromAddress,
toAddress,
nodeURL,
toToken,
} from "./fixtures/solana/configs";

describe("Jupiter Provider", () => {
const conn = new Connection(nodeURL);
const jupiter = new Jupiter(conn, SupportedNetworkName.Solana);

// // TODO:
// it("Should generate a swap transaction that creates both the source referral fee Associated Token Account and the destination Associated Token Account account if it doesn't exist", async () => {
// //
// })
//
// // TODO:
// it("Should generate a swap transaction that creates the destination Associated Token Account if it doesn't exist", async () => {
// //
// })
//
// // TODO:
// it("Should generate a swap transaction that creates the source referral fee Associated Token Account if it doesn't exist", async () => {
// //
// })
//
// // TODO:
// it("Should generate a swap transaction that creates both the source referral fee Associated Token Account if it doesn't exist", async () => {
// //
// })

it("Should return a quote", async () => {
const quote: null | ProviderQuoteResponse = await jupiter.getQuote(
{
amount,
fromAddress,
fromToken,
toToken,
toAddress,
},
{ infiniteApproval: true, walletIdentifier: WalletIdentifier.enkrypt }
);
expect(quote!.provider).to.be.eq(ProviderName.jupiter);
expect(quote!.quote.meta.infiniteApproval).to.be.eq(true);
expect(quote!.quote.meta.walletIdentifier).to.be.eq(
WalletIdentifier.enkrypt
);
expect(quote!.fromTokenAmount.toString()).to.be.eq(amount.toString());
expect(quote!.toTokenAmount.gtn(0)).to.be.eq(true);

const swap: ProviderSwapResponse = await jupiter.getSwap(quote!.quote);
expect(swap.transactions.length).to.be.eq(1);

const tx = VersionedTransaction.deserialize(
Buffer.from(
(swap.transactions[0] as SolanaTransaction).serialized,
"base64"
)
);

// Decode the transaction and check some facts about it

// Get lookup addresses (addresses optimized out of the transaction)
const addressLookupTableAccounts: AddressLookupTableAccount[] = [];
for (let i = 0, len = tx.message.addressTableLookups.length; i < len; i++) {
const addressTableLookup = tx.message.addressTableLookups[i];
const result = await conn.getAddressLookupTable(
addressTableLookup.accountKey
);
const addressLookupTableAccount = result.value;
// eslint-disable-next-line no-unused-expressions
expect(
addressLookupTableAccount,
"Address lookup table account not found"
).to.be.ok;
addressLookupTableAccounts.push(addressLookupTableAccount!);
}
// Decode message
const decompiledMessage = TransactionMessage.decompile(tx.message, {
addressLookupTableAccounts,
});

// Decode instructions
let computeBudget: undefined | number;
let priorityRate: undefined | number | bigint;
for (let i = 0, len = decompiledMessage.instructions.length; i < len; i++) {
const instruction = decompiledMessage.instructions[i];
switch (instruction.programId.toBase58()) {
case ComputeBudgetProgram.programId.toBase58(): {
const instructionType =
ComputeBudgetInstruction.decodeInstructionType(instruction);
switch (instructionType) {
case "SetComputeUnitLimit": {
// eslint-disable-next-line no-unused-expressions
expect(
computeBudget == null,
"Multiple SetComputeUnitLimit instructions found in the same transaction"
).to.be.ok;
const command =
ComputeBudgetInstruction.decodeSetComputeUnitLimit(instruction);
computeBudget = command.units;
break;
}
case "SetComputeUnitPrice": {
// eslint-disable-next-line no-unused-expressions
expect(
priorityRate == null,
"Multiple SetComputeUnitPrice instructions found in the same transaction"
).to.be.ok;
const command =
ComputeBudgetInstruction.decodeSetComputeUnitPrice(instruction);
priorityRate = command.microLamports;
break;
}
default: /* noop */
}
break;
}
default: /* noop */
}
}

expect(
decompiledMessage.payerKey.toBase58(),
"Payer key is not the from address"
).to.equal(fromAddress);
}).timeout(10_000);
});

0 comments on commit b66fd25

Please sign in to comment.