diff --git a/example/swap.ts b/example/swap.ts index 6c91362..1de2029 100644 --- a/example/swap.ts +++ b/example/swap.ts @@ -1,9 +1,17 @@ -import { AggregatorClient } from "@cetusprotocol/aggregator-sdk" +import { AggregatorClient, Env } from "@cetusprotocol/aggregator-sdk"; import BN from "bn.js" + async function main() { - // default to mainnet - const client = new AggregatorClient() + + const aggregatorURL = "https://api-sui.cetus.zone/router_v2/find_routes"; + const client = new AggregatorClient( + { + endpoint: aggregatorURL, + // default to mainnet + env: Env.Mainnet + } + ) const from = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI" const target = "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS" diff --git a/package.json b/package.json index 1f915c3..4ed8823 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cetusprotocol/aggregator-sdk", - "version": "0.11.1", + "version": "0.14.2", "sideEffects": false, "main": "dist/index.js", "types": "dist/index.d.ts", @@ -30,13 +30,15 @@ "@mysten/sui": "^1.6.0", "@pythnetwork/pyth-sui-js": "^2.1.0", "@types/jest": "^29.5.12", + "@types/json-bigint": "^1.0.4", "@types/node": "^20.12.12", "babel-jest": "^29.7.0", "bip39": "^3.1.0", "dotenv": "^16.4.5", "jest": "^29.7.0", + "json-bigint": "^1.0.0", "node-fetch": "^3.3.2", "ts-jest": "^29.1.3", - "typescript": "^5.4.5" + "typescript": "^5.0.0" } -} \ No newline at end of file +} diff --git a/packages/cetus-aggregator-v2/simple-mainnet/Move.toml b/packages/cetus-aggregator-v2/simple-mainnet/Move.toml index d1f44e9..0251807 100644 --- a/packages/cetus-aggregator-v2/simple-mainnet/Move.toml +++ b/packages/cetus-aggregator-v2/simple-mainnet/Move.toml @@ -2,7 +2,7 @@ name = "CetusAggregatorSimple" version = "0.1.1" edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move -published-at = "0x44ca6438ab034be95cedfca7d4070e6d33fa12e088e9dce13abb1bf055093264" +published-at = "0x35a304d62f17d48df80791d1e418c04bb7c81974439a54fa91f4a06e91aa65e9" [dependencies] IntegerMate = { git = "https://github.com/CetusProtocol/integer-mate.git", subdir = "sui", rev = "sui-v1.2.0", override = true } diff --git a/packages/externals/flowx-clmm/Move.toml b/packages/externals/flowx-clmm/Move.toml index 83b6332..fa9d2e1 100644 --- a/packages/externals/flowx-clmm/Move.toml +++ b/packages/externals/flowx-clmm/Move.toml @@ -1,7 +1,7 @@ [package] name = "FlowxClmm" version = "0.1.1" -published-at = "0xcd83322a271ef764063a44fc079a7e349fb7b0744465d76e2f39c8bcf5708bd2" +published-at = "0xe882cd54551e73e64ff5b257146a0c5264546974cf00d78ecc871017cb22df67" edition = "2024.beta" [dependencies] diff --git a/packages/externals/flowx-clmm/sources/versioned.move b/packages/externals/flowx-clmm/sources/versioned.move index b31a034..0c7cf6d 100644 --- a/packages/externals/flowx-clmm/sources/versioned.move +++ b/packages/externals/flowx-clmm/sources/versioned.move @@ -6,4 +6,16 @@ module flowx_clmm::versioned { id: UID, version: u64, } + + public fun check_pause(arg0: &Versioned) { + abort 0 + } + + public fun check_version(arg0: &Versioned) { + abort 0 + } + + public fun is_paused(arg0: &Versioned) : bool { + abort 0 + } } diff --git a/packages/externals/turbos-dex/sources/pool.move b/packages/externals/turbos-dex/sources/pool.move index 968ceef..2fce931 100644 --- a/packages/externals/turbos-dex/sources/pool.move +++ b/packages/externals/turbos-dex/sources/pool.move @@ -41,4 +41,8 @@ module turbos_dex::pool { reward_infos: vector, reward_last_updated_time_ms: u64, } + + public fun check_version(arg0: &Versioned) { + abort 0 + } } diff --git a/packages/test/Move.toml b/packages/test/Move.toml index b36ce03..7fb4b05 100644 --- a/packages/test/Move.toml +++ b/packages/test/Move.toml @@ -2,7 +2,7 @@ name = "test" version = "0.1.1" edition = "2024.beta" -published-at = "0x0" +published-at = "0x92b80f1fbeba186dbaf5332394ace696c129cebb354576b08f3d8a9411620a56" [dependencies] # IntegerMate = { git = "https://github.com/CetusProtocol/integer-mate.git", subdir = "sui", rev = "sui-v1.2.0", override = true } diff --git a/src/api.ts b/src/api.ts index 8627c5f..a7d2088 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,5 +1,6 @@ import BN from "bn.js" import Decimal from "decimal.js" +import JSONbig from "json-bigint" import { completionCoin } from "~/utils/coin" import { ZERO } from "./const" import { @@ -8,7 +9,7 @@ import { } from "./errors" import { parseRouterResponse } from "./client" -const SDK_VERSION = 1001101 +const SDK_VERSION = 1001402 export interface FindRouterParams { from: string @@ -69,6 +70,11 @@ export type ExtendedDetails = { obricCoinBPriceSeed?: string obricCoinAPriceId?: string obricCoinBPriceId?: string + sevenkCoinAPriceSeed?: string + sevenkCoinBPriceSeed?: string + sevenkCoinAOracleId?: string + sevenkCoinBOracleId?: string + sevenkLPCapType?: string } export type Path = { @@ -102,6 +108,7 @@ export type RouterData = { byAmountIn: boolean routes: Router[] insufficientLiquidity: boolean + deviationRatio?: number packages?: Map totalDeepFee?: number error?: RouterError @@ -144,13 +151,14 @@ export async function getRouterResult( routes: [], byAmountIn: params.byAmountIn, insufficientLiquidity: false, + deviationRatio: 0, error: { code: errorCode, msg: getAggregatorServerErrorMessage(errorCode), }, } } - const data = await response.json() + const data = JSONbig.parse(await response.text()) const insufficientLiquidity = data.msg === "liquidity is not enough" if (data.msg && data.msg.indexOf("HoneyPot scam") > -1) { @@ -160,6 +168,7 @@ export async function getRouterResult( routes: [], byAmountIn: params.byAmountIn, insufficientLiquidity, + deviationRatio: 0, error: { code: AggregatorServerErrorCode.HoneyPot, msg: getAggregatorServerErrorMessage( @@ -194,6 +203,7 @@ export async function getRouterResult( routes: [], insufficientLiquidity, byAmountIn: params.byAmountIn, + deviationRatio: 0, error: { code: AggregatorServerErrorCode.InsufficientLiquidity, msg: getAggregatorServerErrorMessage( diff --git a/src/client.ts b/src/client.ts index 4aa3451..7176303 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2,6 +2,7 @@ import Decimal from "decimal.js" import { getFullnodeUrl, SuiClient } from "@mysten/sui/client" import { Transaction, + TransactionArgument, TransactionObjectArgument, } from "@mysten/sui/transactions" import { Signer } from "@mysten/sui/cryptography" @@ -55,6 +56,8 @@ import { Obric } from "./transaction/obric" import { HaWAL } from "./transaction/hawal" import { Momentum } from "./transaction/momentum" import { SteammOmmV2 } from "./transaction/steamm_omm_v2" +import { Magma } from "./transaction/magma" +import { Sevenk } from "./transaction/sevenk" export const CETUS = "CETUS" export const DEEPBOOKV2 = "DEEPBOOK" @@ -82,6 +85,8 @@ export const HAWAL = "HAWAL" export const STEAMM_OMM = "STEAMM_OMM" export const MOMENTUM = "MOMENTUM" export const STEAMM_OMM_V2 = "STEAMM_OMM_V2" +export const MAGMA = "MAGMA" +export const SEVENK = "SEVENK" export const DEFAULT_ENDPOINT = "https://api-sui.cetus.zone/router_v2" export const ALL_DEXES = [ @@ -110,6 +115,8 @@ export const ALL_DEXES = [ MOMENTUM, STEAMM_OMM, STEAMM_OMM_V2, + MAGMA, + SEVENK, ] export type BuildRouterSwapParams = { @@ -198,7 +205,7 @@ export function getAllProviders(): string[] { * @returns Filtered provider list */ export function getProvidersExcluding(excludeProviders: string[]): string[] { - return ALL_DEXES.filter(provider => !excludeProviders.includes(provider)) + return ALL_DEXES.filter((provider) => !excludeProviders.includes(provider)) } /** @@ -207,7 +214,7 @@ export function getProvidersExcluding(excludeProviders: string[]): string[] { * @returns Filtered provider list */ export function getProvidersIncluding(includeProviders: string[]): string[] { - return ALL_DEXES.filter(provider => includeProviders.includes(provider)) + return ALL_DEXES.filter((provider) => includeProviders.includes(provider)) } export type AggregatorClientParams = { @@ -510,9 +517,19 @@ export class AggregatorClient { for (let i = 0; i < routers.length; i++) { const router = routers[i] + let amount_arg = txb.pure.u64( + router.amountOut.toString() + ) as TransactionArgument for (let j = router.path.length - 1; j >= 0; j--) { const path = router.path[j] - const flashSwapResult = dex.flash_swap(this, txb, path, false) + const flashSwapResult = dex.flash_swap( + this, + txb, + path, + amount_arg, + false + ) + amount_arg = flashSwapResult.payAmount returnCoins.unshift(flashSwapResult.targetCoin) receipts.unshift(flashSwapResult.flashReceipt) } @@ -826,6 +843,8 @@ export class AggregatorClient { byAmountIn, slippage ) + + console.log("amountLimit", amountLimit.toString()) const amount = byAmountIn ? expectedAmountIn : amountLimit const buildFromCoinRes = buildInputCoin( txb, @@ -901,11 +920,7 @@ export class AggregatorClient { // Include cetus、deepbookv2、flowxv2 & v3、kriyav2 & v3、turbos、aftermath、haedal、afsui、volo、bluemove publishedAtV2(): string { if (this.env === Env.Mainnet) { - // return "0x3fb42ddf908af45f9fc3c59eab227888ff24ba2e137b3b55bf80920fd47e11af" // version 6 - // return "0xf9c6f78322ed667909e05f6b42b2f5a67af6297503c905335e02a15148e9440d" // version 7 - // return "0x2485feb9d42c7c3bcb8ecde555ad40f1b073d9fb4faf354fa2d30a0b183a23ce" // version 8 - // return "0x3864c7c59a4889fec05d1aae4bc9dba5a0e0940594b424fbed44cb3f6ac4c032" // version 9 - return "0x51966dc1d9d3e6d85aed55aa87eb9e78e928b4e74b4844a15ef7e3dfb5af3bae" // version 10 + return "0x8ae871505a80d8bf6bf9c05906cda6edfeea460c85bebe2e26a4313f5e67874a" // version 12 } else { // return "0x0ed287d6c3fe4962d0994ffddc1d19a15fba6a81533f3f0dcc2bbcedebce0637" // version 2 return "0x52eae33adeb44de55cfb3f281d4cc9e02d976181c0952f5323648b5717b33934" @@ -915,20 +930,7 @@ export class AggregatorClient { // Include deepbookv3, scallop, bluefin publishedAtV2Extend(): string { if (this.env === Env.Mainnet) { - // return "0x43811be4677f5a5de7bf2dac740c10abddfaa524aee6b18e910eeadda8a2f6ae" // version 1, deepbookv3 - // return "0x6d70ffa7aa3f924c3f0b573d27d29895a0ee666aaff821073f75cb14af7fd01a" // version 3, deepbookv3 & scallop - // return "0x16d9418726c26d8cb4ce8c9dd75917fa9b1c7bf47d38d7a1a22603135f0f2a56" // version 4, add suilend - // return "0x3b6d71bdeb8ce5b06febfd3cfc29ecd60d50da729477c8b8038ecdae34541b91" // version 5, add bluefin - // return "0x81ade554cb24a7564ca43a4bfb7381b08d9e5c5f375162c95215b696ab903502" // version 6, force upgrade scallop - // return "0x347dd58bbd11cd82c8b386b344729717c04a998da73386e82a239cc196d5706b" // version 7 - // return "0xf2fcea41dc217385019828375764fa06d9bd25e8e4726ba1962680849fb8d613" // version 8 - // return "0xa2d8a4279d69d8fec04b2fea8852d0d467d3cc0d39c5890180d439ae7a9953ed" // version 9 - // return "0x34ef25b60b51f9d07cd9b7dc5b08dfdf26c7b0ff00c57bb17454c161fa6b6b83" // version 10 - // return "0xf57be4b9f9036034b1c5484d299d8fb68d5f43862d6afe8886d67db293dfc4bc" // version 11 - // return "0x200e762fa2c49f3dc150813038fbf22fd4f894ac6f23ebe1085c62f2ef97f1ca" // version 12 - // return "0x3b6239bc9bccfdc6b702fd971f5f1999a724d6335f0146d7d5a0ff8dcadcefb8" // version 13 - // return "0x39402d188b7231036e52266ebafad14413b4bf3daea4ac17115989444e6cd516" // version 14 - return "0x7cdd26c4aa40c990d5ca780e0919b2de796be9bb41fba461d133bfacb0f677bc" // version 15 + return "0x8a2f7a5b20665eeccc79de3aa37c3b6c473eca233ada1e1cd4678ec07d4d4073" // version 17 } else { return "0xabb6a81c8a216828e317719e06125de5bb2cb0fe8f9916ff8c023ca5be224c78" } @@ -936,11 +938,7 @@ export class AggregatorClient { publishedAtV2Extend2(): string { if (this.env === Env.Mainnet) { - // return "0x368d13376443a8051b22b42a9125f6a3bc836422bb2d9c4a53984b8d6624c326" - // return "0x0018f7bbbece22f4272ed2281b290f745e5aa69d870f599810a30b4eeffc1a5e" // version 2 - // return "0x1727de5223a86156c0fd4ee290aaabaea7f0ffeff485c367c2eb0dcbdfc0061a" //version 3 - // return "0xed2b71968fb6b74c2161bad4c98561c28c69ce82df2cb3707c3b8bc947f2162d" - return "0x186d6d71cedd341ad744e40873cc1513ac539ffac860d0c3ebd27ec5da63d9ba" + return "0x5cb7499fc49c2642310e24a4ecffdbee00133f97e80e2b45bca90c64d55de880" // version 8 } else { return "0x0" } @@ -1071,6 +1069,10 @@ export class AggregatorClient { return new Momentum(this.env) case STEAMM_OMM_V2: return new SteammOmmV2(this.env, pythPriceIDs) + case MAGMA: + return new Magma(this.env) + case SEVENK: + return new Sevenk(this.env, pythPriceIDs) default: throw new Error(`Unsupported dex ${provider}`) } @@ -1208,6 +1210,14 @@ export function findPythPriceIDs(routes: Router[]): string[] { priceIDs.add(path.extendedDetails.steammOraclePythPriceSeedB) } } + if (path.provider === SEVENK) { + if (path.extendedDetails && path.extendedDetails.sevenkCoinAPriceSeed) { + priceIDs.add(path.extendedDetails.sevenkCoinAPriceSeed) + } + if (path.extendedDetails && path.extendedDetails.sevenkCoinBPriceSeed) { + priceIDs.add(path.extendedDetails.sevenkCoinBPriceSeed) + } + } } } return Array.from(priceIDs) @@ -1237,6 +1247,7 @@ export function parseRouterResponse( let routerData: RouterData = { amountIn: new BN(data.amount_in.toString()), amountOut: new BN(data.amount_out.toString()), + deviationRatio: data.deviation_ratio ? Number(data.deviation_ratio) : undefined, byAmountIn, insufficientLiquidity: false, routes: data.routes.map((route: any) => { @@ -1260,7 +1271,8 @@ export function parseRouterResponse( path.provider === OBRIC || path.provider === STEAMM || path.provider === STEAMM_OMM || - path.provider === STEAMM_OMM_V2 + path.provider === STEAMM_OMM_V2 || + path.provider === SEVENK ) { extendedDetails = { aftermathLpSupplyType: @@ -1309,6 +1321,13 @@ export function parseRouterResponse( path.extended_details?.obric_coin_b_price_seed, obricCoinAPriceId: path.extended_details?.obric_coin_a_price_id, obricCoinBPriceId: path.extended_details?.obric_coin_b_price_id, + sevenkCoinAPriceSeed: + path.extended_details?.sevenk_coin_a_price_seed, + sevenkCoinBPriceSeed: + path.extended_details?.sevenk_coin_b_price_seed, + sevenkCoinAOracleId: path.extended_details?.sevenk_oracle_config_a, + sevenkCoinBOracleId: path.extended_details?.sevenk_oracle_config_b, + sevenkLPCapType: path.extended_details?.sevenk_lp_cap_type, } } diff --git a/src/transaction/cetus.ts b/src/transaction/cetus.ts index 1278244..5b41b04 100644 --- a/src/transaction/cetus.ts +++ b/src/transaction/cetus.ts @@ -32,6 +32,7 @@ export class Cetus implements Dex { client: AggregatorClient, txb: Transaction, path: Path, + amount_arg: TransactionArgument, by_amount_in: boolean, packages?: Map ): CetusFlashSwapResult { @@ -39,12 +40,11 @@ export class Cetus implements Dex { const [func, coinAType, coinBType] = direction ? ["flash_swap_a2b", from, target] : ["flash_swap_b2a", target, from] - let amount = by_amount_in ? path.amountIn : path.amountOut const args = [ txb.object(this.globalConfig), txb.object(path.id), txb.object(this.partner), - txb.pure.u64(amount), + amount_arg, txb.pure.bool(by_amount_in), txb.object(CLOCK_ADDRESS), ] diff --git a/src/transaction/magma.ts b/src/transaction/magma.ts new file mode 100644 index 0000000..12e0f1a --- /dev/null +++ b/src/transaction/magma.ts @@ -0,0 +1,48 @@ +import { + Transaction, + TransactionObjectArgument, +} from "@mysten/sui/transactions" +import { AggregatorClient, CLOCK_ADDRESS, Dex, Env, getAggregatorV2Extend2PublishedAt, Path } from ".." + +export class Magma implements Dex { + private globalConfig: string + + constructor(env: Env) { + if (env !== Env.Mainnet) { + throw new Error("Magma CLMM only supported on mainnet") + } + + this.globalConfig = + "0x4c4e1402401f72c7d8533d0ed8d5f8949da363c7a3319ccef261ffe153d32f8a" + } + + async swap( + client: AggregatorClient, + txb: Transaction, + path: Path, + inputCoin: TransactionObjectArgument, + packages?: Map + ): Promise { + const { direction, from, target } = path + + const [func, coinAType, coinBType] = direction + ? ["swap_a2b", from, target] + : ["swap_b2a", target, from] + + const args = [ + txb.object(this.globalConfig), + txb.object(path.id), + inputCoin, + txb.object(CLOCK_ADDRESS), + ] + + const publishedAt = getAggregatorV2Extend2PublishedAt(client.publishedAtV2Extend2(), packages) + const res = txb.moveCall({ + target: `${publishedAt}::magma::${func}`, + typeArguments: [coinAType, coinBType], + arguments: args, + }) as TransactionObjectArgument + + return res + } +} diff --git a/src/transaction/sevenk.ts b/src/transaction/sevenk.ts new file mode 100644 index 0000000..0052dd3 --- /dev/null +++ b/src/transaction/sevenk.ts @@ -0,0 +1,117 @@ +import { + Transaction, + TransactionArgument, + TransactionObjectArgument, +} from "@mysten/sui/transactions" +import { + AggregatorClient, + CLOCK_ADDRESS, + Dex, + Env, + getAggregatorV2Extend2PublishedAt, + Path, +} from ".." + +export class Sevenk implements Dex { + private pythPriceIDs: Map + private oraclePublishedAt: string + + constructor(env: Env, pythPriceIDs: Map) { + if (env === Env.Testnet) { + throw new Error("Sevenk is not supported on testnet") + } + this.pythPriceIDs = pythPriceIDs + this.oraclePublishedAt = "0x8c36ea167c5e6da8c3d60b4fc897416105dcb986471bd81cfbfd38720a4487c0" + } + + async swap( + client: AggregatorClient, + txb: Transaction, + path: Path, + inputCoin: TransactionObjectArgument, + packages?: Map + ): Promise { + const { direction, from, target } = path + const [func, coinAType, coinBType] = direction + ? ["swap_a2b", from, target] + : ["swap_b2a", target, from] + + let coinAPriceSeed: string + let coinBPriceSeed: string + let coinAOracleId: string + let coinBOracleId: string + let lpCapType: string + + if (path.extendedDetails == null) { + throw new Error("Extended details not supported haedal pmm") + } else { + if ( + !path.extendedDetails.sevenkCoinAPriceSeed || + !path.extendedDetails.sevenkCoinBPriceSeed || + !path.extendedDetails.sevenkCoinAOracleId || + !path.extendedDetails.sevenkCoinBOracleId || + !path.extendedDetails.sevenkLPCapType + ) { + throw new Error("Base price seed or quote price seed not supported") + } + coinAPriceSeed = path.extendedDetails.sevenkCoinAPriceSeed + coinBPriceSeed = path.extendedDetails.sevenkCoinBPriceSeed + coinAOracleId = path.extendedDetails.sevenkCoinAOracleId + coinBOracleId = path.extendedDetails.sevenkCoinBOracleId + lpCapType = path.extendedDetails.sevenkLPCapType + } + + const coinAPriceInfoObjectId = this.pythPriceIDs.get(coinAPriceSeed) + const coinBPriceInfoObjectId = this.pythPriceIDs.get(coinBPriceSeed) + + if (!coinAPriceInfoObjectId || !coinBPriceInfoObjectId) { + throw new Error( + "Base price info object id or quote price info object id not found" + ) + } + + const holder = txb.moveCall({ + target: `${this.oraclePublishedAt}::oracle::new_holder`, + typeArguments: [], + arguments: [], + }) as TransactionArgument + + txb.moveCall({ + target: `${this.oraclePublishedAt}::pyth::get_price`, + typeArguments: [], + arguments: [ + txb.object(coinAOracleId), + holder, + txb.object(coinAPriceInfoObjectId), + txb.object(CLOCK_ADDRESS), + ], + }) as TransactionArgument + + txb.moveCall({ + target: `${this.oraclePublishedAt}::pyth::get_price`, + typeArguments: [], + arguments: [ + txb.object(coinBOracleId), + holder, + txb.object(coinBPriceInfoObjectId), + txb.object(CLOCK_ADDRESS), + ], + }) as TransactionArgument + + const args = [ + txb.object(path.id), + holder, + inputCoin, + ] + const publishedAt = getAggregatorV2Extend2PublishedAt( + client.publishedAtV2Extend2(), + packages + ) + const res = txb.moveCall({ + target: `${publishedAt}::sevenk::${func}`, + typeArguments: [coinAType, coinBType, lpCapType], + arguments: args, + }) as TransactionArgument + return res + } +} diff --git a/src/transaction/swap.ts b/src/transaction/swap.ts index 2c246d9..85a433e 100644 --- a/src/transaction/swap.ts +++ b/src/transaction/swap.ts @@ -3,7 +3,7 @@ import { SwapInPoolsParams } from "~/client" import { compareCoins, completionCoin } from "~/utils/coin" import { Env, SwapInPoolsResult, U64_MAX_BN, ZERO } from ".." import { ConfigErrorCode, TransactionErrorCode } from "~/errors" -import { checkInvalidSuiAddress, printTransaction } from "~/utils/transaction" +import { checkInvalidSuiAddress } from "~/utils/transaction" import { SuiClient } from "@mysten/sui/client" import { BN } from "bn.js" import { sqrtPriceX64ToPrice } from "~/math" @@ -21,7 +21,7 @@ export async function swapInPools( const tx = new Transaction() const direction = compareCoins(fromCoin, targetCoin) const integratePublishedAt = env === Env.Mainnet ? - "0x2d8c2e0fc6dd25b0214b3fa747e0fd27fd54608142cd2e4f64c1cd350cc4add4" : + "0xb2db7142fa83210a7d78d9c12ac49c043b3cbbd482224fea6e3da00aa5a5ae2d" : "0x4f920e1ef6318cfba77e20a0538a419a5a504c14230169438b99aba485db40a6" const coinA = direction ? fromCoin : targetCoin const coinB = direction ? targetCoin : fromCoin @@ -127,6 +127,7 @@ export async function swapInPools( const routeData = { amountIn: amountIn, amountOut: new BN(event.amount_out ?? 0), + deviationRatio: 0, routes: [ { path: [ diff --git a/tests/router/magma.test.ts b/tests/router/magma.test.ts new file mode 100644 index 0000000..f5333c8 --- /dev/null +++ b/tests/router/magma.test.ts @@ -0,0 +1,129 @@ +import { describe, test } from "@jest/globals" +import dotenv from "dotenv" +import { AggregatorClient } from "~/client" +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519" +import { printTransaction } from "~/utils/transaction" +import BN from "bn.js" +import { fromB64 } from "@mysten/sui/utils" +import { SuiClient } from "@mysten/sui/client" +import { Env } from "~/index" +import { Transaction } from "@mysten/sui/transactions" + +dotenv.config() + +export function buildTestAccount(): Ed25519Keypair { + const mnemonics = process.env.SUI_WALLET_MNEMONICS || "" + const testAccountObject = Ed25519Keypair.deriveKeypair(mnemonics) + return testAccountObject +} + +describe("Test magma module", () => { + let client: AggregatorClient + let keypair: Ed25519Keypair + + const T_WAL = "0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL" + const T_SUI = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI" + const T_USDC = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC" + + beforeAll(() => { + const fullNodeURL = process.env.SUI_RPC! + const aggregatorURL = process.env.CETUS_AGGREGATOR! + const secret = process.env.SUI_WALLET_SECRET! + + if (secret) { + keypair = Ed25519Keypair.fromSecretKey(fromB64(secret).slice(1, 33)) + } else { + keypair = buildTestAccount() + } + + const wallet = keypair.getPublicKey().toSuiAddress() + console.log("wallet: ", wallet) + + const endpoint = aggregatorURL + + const suiClient = new SuiClient({ + url: fullNodeURL, + }) + + client = new AggregatorClient({ + endpoint, + signer: wallet, + client: suiClient, + env: Env.Mainnet, + pythUrls: ["https://cetus-pythnet-a648.mainnet.pythnet.rpcpool.com/219cf7a8-6d75-432d-a648-d487a6dd5dc3/hermes"], + }) + }) + + test("Find Routers", async () => { + const amounts = ["1000", "1000000", "100000000", "5000000000", "10000000000000"] + + for (const amount of amounts) { + const res = await client.findRouters({ + from: T_WAL, + target: T_SUI, + amount: new BN(amount), + byAmountIn: true, + depth: 3, + splitCount: 1, + providers: ["MAGMA"], + }) + + if (res != null) { + console.log(JSON.stringify(res, null, 2)) + } + console.log("amount in", res?.amountIn.toString()) + console.log("amount out", res?.amountOut.toString()) + } + }) + + test("Build Router TX", async () => { + const amount = "1000000" + + const res = await client.findRouters({ + from: T_USDC, + target: T_SUI, + amount: new BN(amount), + byAmountIn: true, + depth: 3, + providers: ["MAGMA"], + }) + + console.log("amount in", res?.amountIn.toString()) + console.log("amount out", res?.amountOut.toString()) + + const txb = new Transaction() + + if (res != null) { + console.log(JSON.stringify(res, null, 2)) + await client.fastRouterSwap({ + routers: res, + txb, + slippage: 0.01, + refreshAllCoins: true, + payDeepFeeAmount: 0, + }) + + printTransaction(txb) + + txb.setSender(client.signer) + const buildTxb = await txb.build({ client: client.client }) + // const buildTxb = await txb.getData() + + console.log("buildTxb", buildTxb) + + + let result = await client.devInspectTransactionBlock(txb) + console.log("🚀 ~ file: router.test.ts:180 ~ test ~ result:", result) + for (const event of result.events) { + console.log("event", JSON.stringify(event, null, 2)) + } + + if (result.effects.status.status === "success") { + const result = await client.signAndExecuteTransaction(txb, keypair) + console.log("result", result) + } else { + console.log("result", result) + } + } + }, 600000) +}) diff --git a/tests/router/sevenk.test.ts b/tests/router/sevenk.test.ts new file mode 100644 index 0000000..1ebf98e --- /dev/null +++ b/tests/router/sevenk.test.ts @@ -0,0 +1,129 @@ +import { describe, test } from "@jest/globals" +import dotenv from "dotenv" +import { AggregatorClient } from "~/client" +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519" +import { printTransaction } from "~/utils/transaction" +import BN from "bn.js" +import { fromB64 } from "@mysten/sui/utils" +import { SuiClient } from "@mysten/sui/client" +import { Env } from "~/index" +import { Transaction } from "@mysten/sui/transactions" + +dotenv.config() + +export function buildTestAccount(): Ed25519Keypair { + const mnemonics = process.env.SUI_WALLET_MNEMONICS || "" + const testAccountObject = Ed25519Keypair.deriveKeypair(mnemonics) + return testAccountObject +} + +describe("Test magma module", () => { + let client: AggregatorClient + let keypair: Ed25519Keypair + + const T_WAL = "0x356a26eb9e012a68958082340d4c4116e7f55615cf27affcff209cf0ae544f59::wal::WAL" + const T_SUI = "0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI" + const T_USDC = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC" + + beforeAll(() => { + const fullNodeURL = process.env.SUI_RPC! + const aggregatorURL = process.env.CETUS_AGGREGATOR! + const secret = process.env.SUI_WALLET_SECRET! + + if (secret) { + keypair = Ed25519Keypair.fromSecretKey(fromB64(secret).slice(1, 33)) + } else { + keypair = buildTestAccount() + } + + const wallet = keypair.getPublicKey().toSuiAddress() + console.log("wallet: ", wallet) + + const endpoint = aggregatorURL + + const suiClient = new SuiClient({ + url: fullNodeURL, + }) + + client = new AggregatorClient({ + endpoint, + signer: wallet, + client: suiClient, + env: Env.Mainnet, + pythUrls: ["https://cetus-pythnet-a648.mainnet.pythnet.rpcpool.com/219cf7a8-6d75-432d-a648-d487a6dd5dc3/hermes"], + }) + }) + + test("Find Routers", async () => { + const amounts = ["100000000"] + + for (const amount of amounts) { + const res = await client.findRouters({ + from: T_SUI, + target: T_USDC, + amount: new BN(amount), + byAmountIn: true, + depth: 3, + splitCount: 1, + providers: ["SEVENK"], + }) + + if (res != null) { + console.log(JSON.stringify(res, null, 2)) + } + console.log("amount in", res?.amountIn.toString()) + console.log("amount out", res?.amountOut.toString()) + } + }) + + test("Build Router TX", async () => { + const amount = "1000000000" + + const res = await client.findRouters({ + from: T_SUI, + target: T_USDC, + amount: new BN(amount), + byAmountIn: true, + depth: 3, + providers: ["SEVENK"], + }) + + console.log("amount in", res?.amountIn.toString()) + console.log("amount out", res?.amountOut.toString()) + + const txb = new Transaction() + + if (res != null) { + console.log(JSON.stringify(res, null, 2)) + await client.fastRouterSwap({ + routers: res, + txb, + slippage: 0.01, + refreshAllCoins: true, + payDeepFeeAmount: 0, + }) + + printTransaction(txb) + + txb.setSender(client.signer) + const buildTxb = await txb.build({ client: client.client }) + // const buildTxb = await txb.getData() + + console.log("buildTxb", buildTxb) + + + let result = await client.devInspectTransactionBlock(txb) + console.log("🚀 ~ file: router.test.ts:180 ~ test ~ result:", result) + for (const event of result.events) { + console.log("event", JSON.stringify(event, null, 2)) + } + + if (result.effects.status.status === "success") { + const result = await client.signAndExecuteTransaction(txb, keypair) + console.log("result", result) + } else { + console.log("result", result) + } + } + }, 600000) +})