From 2eca313f32a033527b79bc6c520508ae9f603d8b Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 15:31:22 +0300 Subject: [PATCH 01/25] Create static mergeAllCoinObjects() in WalletManager --- src/managers/WalletManager.ts | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/managers/WalletManager.ts b/src/managers/WalletManager.ts index 109f7ce..d1e9595 100644 --- a/src/managers/WalletManager.ts +++ b/src/managers/WalletManager.ts @@ -8,6 +8,7 @@ import { } from "@mysten/sui.js/client"; import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519"; import { TransactionBlock } from "@mysten/sui.js/transactions"; +import { TransactionBlock as UpdatedTransactionBlock } from "@mysten/sui.js-0.51.2/transactions"; import { SUI_DECIMALS } from "@mysten/sui.js/utils"; import BigNumber from "bignumber.js"; import { SUI_DENOMINATOR, SWAP_GAS_BUDGET } from "../providers/common"; @@ -16,6 +17,7 @@ import { CoinAssetData, IWalletManager } from "./types"; import { getCoinsAssetsFromCoinObjects, normalizeMnemonic } from "./utils"; import { bech32 } from "bech32"; import { determineFormat } from "./WalletManager.utils"; +import { TransactionResult } from "../transactions/types"; /** * @class WalletManagerSingleton @@ -362,4 +364,44 @@ export class WalletManagerSingleton implements IWalletManager { return allObjects; } + + /** + * Note: this method is using an `UpdatedTransactionBlock`, that is a `TransactionBlock` from + * the @mysten/sui.js v0.51.2 package. + * + * @description Merges all the passed `coinObjects` into one object. + * @return {object} A transaction block, that contains the coins merge; a destination coin object id, into which all + * the other coin objects are merged; a transaction result, that is the result of the coins merge. + */ + public static mergeAllCoinObjects({ + coinObjects, + txb, + }: { + coinObjects: CoinStruct[]; + txb?: UpdatedTransactionBlock; + }): { + tx: UpdatedTransactionBlock; + destinationObjectId: string; + txRes?: TransactionResult; + } { + if (coinObjects.length === 0) { + throw new Error("[mergeAllCoinObjects] Passed `coinObjects` are empty."); + } + + const tx = txb ?? new UpdatedTransactionBlock(); + + const objectIds = coinObjects.map((obj) => obj.coinObjectId); + const [destinationObjectId, ...sourceObjectIds] = objectIds; + + if (sourceObjectIds.length === 0) { + return { tx, destinationObjectId }; + } + + const txRes = tx.mergeCoins( + tx.object(destinationObjectId), + sourceObjectIds.map((objId) => tx.object(objId)), + ); + + return { tx, txRes, destinationObjectId }; + } } From 0fbb6cde736b4215c4052d40037b03288c2b6c81 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 15:32:12 +0300 Subject: [PATCH 02/25] Create separate util getUserCoinObjects() method --- src/providers/utils/getUserCoinObjects.ts | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/providers/utils/getUserCoinObjects.ts diff --git a/src/providers/utils/getUserCoinObjects.ts b/src/providers/utils/getUserCoinObjects.ts new file mode 100644 index 0000000..4673763 --- /dev/null +++ b/src/providers/utils/getUserCoinObjects.ts @@ -0,0 +1,51 @@ +import { SuiClient as UpdatedSuiClient } from "@mysten/sui.js-0.51.2/client"; +import { CoinStruct, PaginatedCoins, SuiClient } from "@mysten/sui.js/client"; + +/** + * @description Retrieves all coin objects associated with a wallet with `publicKey` and specified `coinType`. + * + * @param {Object} params - Parameters object. + * @param {string} params.publicKey - The public key of the wallet. + * @param {string} params.coinType - The coin type of specified coin. + * @param {string} params.provider - The Sui Client to fetch coin objects data. + * @return {Promise} A promise that resolves to an array of coin objects data. + */ +export async function getUserCoinObjects({ + coinType, + publicKey, + provider, +}: { + coinType: string; + publicKey: string; + provider: SuiClient | UpdatedSuiClient; +}): Promise { + const pageCapacity = 50; + const allObjects: CoinStruct[] = []; + let nextCursor: string | null | undefined = null; + let assets: PaginatedCoins = await provider.getCoins({ + owner: publicKey, + coinType, + limit: pageCapacity, + cursor: nextCursor, + }); + + // Fetching and combining part + while (assets.hasNextPage) { + const coinObjects: CoinStruct[] = assets.data; + allObjects.push(...coinObjects); + + nextCursor = assets.nextCursor; + assets = await provider.getCoins({ + owner: publicKey, + coinType, + limit: pageCapacity, + cursor: nextCursor, + }); + } + + // In case user has less tokens than `pageCapacity` (1 page only), we should put them into `allObjects` + const coinObjects: CoinStruct[] = assets.data; + allObjects.push(...coinObjects); + + return allObjects; +} From 1b4daf706d6c447c88c61f8633b533ad98408753 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 15:34:33 +0300 Subject: [PATCH 03/25] Add interest protocol clamm sdk to deps --- package.json | 1 + yarn.lock | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 33c6562..0a69af0 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dependencies": { "@cetusprotocol/cetus-sui-clmm-sdk": "^3.17.8", "@flowx-pkg/ts-sdk": "^0.0.11", + "@interest-protocol/clamm-sdk": "^4.1.0-alpha", "@mysten/sui.js": "yarn:@mysten/sui.js@^0.42.0", "@mysten/sui.js-0.51.2": "yarn:@mysten/sui.js@^0.51.2", "@types/redis": "^4.0.11", diff --git a/yarn.lock b/yarn.lock index da91811..6b48766 100644 --- a/yarn.lock +++ b/yarn.lock @@ -570,6 +570,15 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== +"@interest-protocol/clamm-sdk@^4.1.0-alpha": + version "4.1.0-alpha" + resolved "https://registry.yarnpkg.com/@interest-protocol/clamm-sdk/-/clamm-sdk-4.1.0-alpha.tgz#8d812c43cc90070dc07c1aeaed8d4d343f946287" + integrity sha512-rUxJMxJM5y6D0TuG74NJ70ejxV9KmldTHvdlZQ1FvCttQwEvo6G4UncjueGkNBO8Ons6b5VDrlcJn/j44hyDMw== + dependencies: + "@mysten/sui.js" "^0.51.2" + ramda "^0.29.1" + tiny-invariant "^1.3.3" + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -871,7 +880,7 @@ dependencies: bs58 "^5.0.0" -"@mysten/sui.js-0.51.2@yarn:@mysten/sui.js@^0.51.2": +"@mysten/sui.js-0.51.2@yarn:@mysten/sui.js@^0.51.2", "@mysten/sui.js@^0.51.2": version "0.51.2" resolved "https://registry.yarnpkg.com/@mysten/sui.js/-/sui.js-0.51.2.tgz#40c54bf1df57fdeaf27ad11efddeaa20ff5e6a7b" integrity sha512-RRG/VHhVvkQlWqodFZagRxVDo3SbXh4e3xTBplhPNNyybugbWj/V+wGrNv5/KtxfVxHBw6jTCoB798ijqPBq7g== @@ -5423,6 +5432,11 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +ramda@^0.29.1: + version "0.29.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.29.1.tgz#408a6165b9555b7ba2fc62555804b6c5a2eca196" + integrity sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA== + react-is@^18.0.0: version "18.2.0" resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -6030,6 +6044,11 @@ tiny-invariant@^1.1.0: resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== +tiny-invariant@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + tiny-relative-date@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07" From 774107b5734d17a36aa4f80aecdf367168f92e6a Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 15:36:44 +0300 Subject: [PATCH 04/25] Create interest protocol singleton init version --- src/providers/interest/interest.ts | 376 ++++++++++++++++++++++++++ src/providers/interest/type-guards.ts | 180 ++++++++++++ src/providers/interest/types.ts | 11 + src/providers/interest/utils.ts | 32 +++ 4 files changed, 599 insertions(+) create mode 100644 src/providers/interest/interest.ts create mode 100644 src/providers/interest/type-guards.ts create mode 100644 src/providers/interest/types.ts create mode 100644 src/providers/interest/utils.ts diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts new file mode 100644 index 0000000..db3cfe9 --- /dev/null +++ b/src/providers/interest/interest.ts @@ -0,0 +1,376 @@ +/* eslint-disable require-jsdoc */ +import { CLAMM, InterestPool } from "@interest-protocol/clamm-sdk"; +import { SuiClient } from "@mysten/sui.js-0.51.2/client"; +import { CoinMetadata } from "@mysten/sui.js/client"; +import { TransactionBlock } from "@mysten/sui.js/transactions"; +import BigNumber from "bignumber.js"; +import { EventEmitter } from "../../emitters/EventEmitter"; +import { WalletManagerSingleton } from "../../managers/WalletManager"; +import { CommonCoinData, UpdatedCoinsCache } from "../../managers/types"; +import { InMemoryStorageSingleton } from "../../storages/InMemoryStorage"; +import { Storage } from "../../storages/types"; +import { getCoinsAndPathsCaches } from "../../storages/utils/getCoinsAndPathsCaches"; +import { storeCaches } from "../../storages/utils/storeCaches"; +import { exitHandlerWrapper } from "../common"; +import { CacheOptions, CoinsCache, CommonPoolData, IPoolProvider, PathsCache } from "../types"; +import { getUserCoinObjects } from "../utils/getUserCoinObjects"; +import { isApiResponseValid } from "./type-guards"; +import { InterestOptions, InterestRouteData } from "./types"; +import { getPathMapAndCoinTypesSet } from "./utils"; + +export class InterestProtocolSingleton extends EventEmitter implements IPoolProvider { + private static _instance: InterestProtocolSingleton | undefined; + private static INTEREST_PROTOCOL_PACKAGE_ADDRESS = + "0xf47f67d87aad51b6bd476bf41bf578fd04ba444f6eab8e80950186f166ea1dba"; + private static INTEREST_PROTOCOL_SUI_TEARS = "0xf7334947a5037552a94cee15fc471dbda71bf24d46c97ee24e1fdac38e26644c"; + private provider: SuiClient; + private cacheOptions: CacheOptions; + private intervalId: NodeJS.Timeout | undefined; + private storage: Storage; + + public interestSdk: CLAMM; + public providerName = "Interest"; + public isSmartRoutingAvailable = false; + public pathsCache: PathsCache = new Map(); + public coinsCache: CoinsCache = new Map(); + public poolsCache: InterestPool[] = []; + + private constructor(options: Omit) { + super(); + + this.provider = new SuiClient({ url: options.suiProviderUrl }); + + this.interestSdk = new CLAMM({ + suiClient: this.provider, + packageAddress: InterestProtocolSingleton.INTEREST_PROTOCOL_PACKAGE_ADDRESS, + network: "mainnet", + suiTearsAddress: InterestProtocolSingleton.INTEREST_PROTOCOL_SUI_TEARS, + }); + + const { updateIntervally = true, ...restCacheOptions } = options.cacheOptions; + this.cacheOptions = { updateIntervally, ...restCacheOptions }; + this.storage = options.cacheOptions.storage ?? InMemoryStorageSingleton.getInstance(); + } + + public static async getInstance(options?: InterestOptions): Promise { + if (!InterestProtocolSingleton._instance) { + if (options === undefined) { + throw new Error("[InterestProtocolSingleton] Options are required in arguments to create instance."); + } + const { suiProviderUrl, cacheOptions, lazyLoading = true } = options; + + const instance = new InterestProtocolSingleton({ suiProviderUrl, cacheOptions }); + lazyLoading ? instance.init() : await instance.init(); + + InterestProtocolSingleton._instance = instance; + } + + return InterestProtocolSingleton._instance; + } + + /** + * @private + * @method init + * @description Initializes the InterestProtocolSingleton instance. + * @return {Promise} + */ + private async init() { + console.debug(`[${this.providerName}] Singleton initiating.`); + await this.fillCacheFromStorage(); + await this.updateCaches(); + this.cacheOptions.updateIntervally && this.updateCachesIntervally(); + + this.bufferEvent("cachesUpdate", this.getCoins()); + } + + /** + * Fills the cache from storage asynchronously. + * + * @private + * @return {Promise} A promise that resolves when the cache is filled from storage. + */ + private async fillCacheFromStorage(): Promise { + try { + const { coinsCache, pathsCache } = await getCoinsAndPathsCaches({ + storage: this.storage, + provider: this.providerName, + updateCacheInterval: this.cacheOptions.updateIntervalInMs, + }); + + this.coinsCache = coinsCache; + this.pathsCache = pathsCache; + } catch (error) { + console.error(`[${this.providerName}] fillCacheFromStorage failed:`, error); + } + } + + /** + * Checks if the storage cache is empty. + * + * @private + * @return {boolean} True if the storage cache is empty, false otherwise. + */ + private isStorageCacheEmpty() { + const isCacheEmpty = this.coinsCache.size === 0 || this.pathsCache.size === 0; + + return isCacheEmpty; + } + + /** + * @private + * @method updateCaches + * @description Updates caches. + * @return {Promise} + */ + private async updateCaches({ force }: { force: boolean } = { force: false }): Promise { + const isCacheEmpty = this.isStorageCacheEmpty(); + + if (isCacheEmpty || force) { + try { + await this.updatePoolsCache(); + await this.updatePathsAndCoinsCache(); + this.emit("cachesUpdate", this.getCoins()); + + await storeCaches({ + provider: this.providerName, + storage: this.storage, + coinsCache: this.getCoins(), + pathsCache: this.getPaths(), + }); + + console.debug("[Interest] Caches are updated and stored."); + } catch (error) { + console.error("[Interest] Caches update failed:", error); + } + } + } + + /** + * @private + * @method updateCachesIntervally + * @description Updates caches periodically. + * @return {void} + */ + private updateCachesIntervally(): void { + let isUpdatingCurrently = false; + this.intervalId = setInterval(async () => { + try { + if (isUpdatingCurrently) { + return; + } + isUpdatingCurrently = true; + await this.updateCaches({ force: true }); + } finally { + isUpdatingCurrently = false; + } + }, this.cacheOptions.updateIntervalInMs); + + exitHandlerWrapper({ intervalId: this.intervalId, providerName: this.providerName }); + } + + /** + * @private + * @method updatePoolsCache + * @description Updates pools cache. + * @return {Promise} + */ + private async updatePoolsCache(): Promise { + // TODO: Replace this method usage with the new Interest SDK method + async function getAllPools() { + return []; + } + + const pools: InterestPool[] = await getAllPools(); + const isValidPoolsResponse = isApiResponseValid(pools); + + if (!isValidPoolsResponse) { + console.error("[Interest] Pools response:", pools); + throw new Error("Pools response from API is not valid"); + } + + this.poolsCache = pools; + } + + /** + * @private + * @method updatePathsAndCoinsCache + * @description Updates paths and coins cache. + * @return {Promise} + */ + private async updatePathsAndCoinsCache(): Promise { + const { pathMap, coinTypesSet } = getPathMapAndCoinTypesSet(this.poolsCache); + this.pathsCache = pathMap; + + await Promise.all( + Array.from(coinTypesSet.values()).map(async (coinType: string) => { + try { + const metadata: CoinMetadata | null = await this.provider.getCoinMetadata({ coinType }); + + if (metadata !== null) { + this.coinsCache.set(coinType, { symbol: metadata.symbol, type: coinType, decimals: metadata.decimals }); + } + } catch (error) { + console.error(`[Interest] Error while fetching metadata about coin ${coinType}:`, error); + } + }), + ); + } + + /** + * @public + * @method getPool + * @description Gets the pool with the specified coin types. + * @param {string} coinTypeA - Coin type A. + * @param {string} coinTypeB - Coin type B. + * @return {Pool} The pool object. + */ + public getPool(coinTypeA: string, coinTypeB: string): InterestPool { + const pool: InterestPool | undefined = this.poolsCache.find( + (pool: InterestPool) => pool.coinTypes.includes(coinTypeA) && pool.coinTypes.includes(coinTypeB), + ); + + if (!pool) { + throw new Error(`[Interest] Cannot find pool with coinTypeA "${coinTypeA}" and coinTypeB "${coinTypeB}".`); + } + + return pool; + } + + /** + * @public + * @method getPools + * @description Gets all pools. + * @return {InterestPool[]} Array of pools. + */ + public getPools(): InterestPool[] { + return this.poolsCache; + } + + /** + * @public + * @method getCoins + * @description Gets the updated coins cache. + * @return {UpdatedCoinsCache} Updated coins cache. + */ + public getCoins(): UpdatedCoinsCache { + const allCoins: CommonCoinData[] = Array.from(this.coinsCache.values()); + return { provider: this.providerName, data: allCoins }; + } + + /** + * @public + * @method getPaths + * @description Gets the paths cache. + * @return {Map} Paths cache. + */ + public getPaths(): Map { + return this.pathsCache; + } + + public async getRouteData({ + coinTypeFrom, + coinTypeTo, + inputAmount, + }: { + coinTypeFrom: string; + coinTypeTo: string; + inputAmount: string; + slippagePercentage: number; + publicKey: string; + }): Promise<{ outputAmount: bigint; route: InterestRouteData }> { + const inputCoinData = await this.provider.getCoinMetadata({ coinType: coinTypeFrom }); + + if (inputCoinData === null) { + throw new Error(`[Interest] Cannot get coin metadata for "${coinTypeFrom}".`); + } + + const inputCoinDecimals = inputCoinData.decimals; + const formattedInputAmount = new BigNumber(inputAmount).multipliedBy(10 ** inputCoinDecimals).toString(); + + const pool = this.getPool(coinTypeFrom, coinTypeTo); + + const { amount } = await this.interestSdk.quoteSwap({ + pool, + coinInType: coinTypeFrom, + coinOutType: coinTypeTo, + amount: BigInt(formattedInputAmount), + }); + + return { + outputAmount: amount, + route: { pool, minAmount: amount, inputCoinType: coinTypeFrom, outputCoinType: coinTypeTo }, + }; + } + + public async getSwapTransaction({ + route, + publicKey, + }: { + route: InterestRouteData; + publicKey: string; + slippagePercentage: number; + }): Promise { + const { inputCoinType, outputCoinType, minAmount, pool } = route; + + const inputCoinObjects = await getUserCoinObjects({ coinType: inputCoinType, provider: this.provider, publicKey }); + const { destinationObjectId, tx } = WalletManagerSingleton.mergeAllCoinObjects({ + coinObjects: inputCoinObjects, + }); + + const { coinOut, txb } = await this.interestSdk.swap({ + txb: tx, + coinIn: tx.object(destinationObjectId), + coinInType: inputCoinType, + coinOutType: outputCoinType, + pool, + minAmount, + }); + + txb.transferObjects([coinOut], txb.pure(publicKey)); + + const txBlock = new TransactionBlock(TransactionBlock.from(txb.serialize())); + + return txBlock; + } + + /** + * Gets a transaction block for swapping tokens based on provided swap data. + * + * Note: This method is not implemented yet. + * + * @public + * @async + * @param {SwapRequiredData} route - The required data for the swap. + * @param {string} publicKey - The public key of the user. + * @param {number} [slippagePercentage=10] - The slippage percentage. + * @return {Promise} A Promise that resolves to a TransactionBlock. + */ + public async getSwapTransactionDoctored({ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + route, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + publicKey, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + slippagePercentage = 10, + }: { + route: InterestRouteData; + publicKey: string; + slippagePercentage: number; + }): Promise { + throw new Error(`[${this.providerName}] getSwapTransactionDoctored method not implemented`); + } + + /** + * Removes the current instance of AftermathSingleton. + * + * Disclaimer: While this method in this class is marked as public, it is strongly discouraged + * to use it directly unless you are certain about the behavior. + */ + public static removeInstance() { + InterestProtocolSingleton._instance = undefined; + } + + public buildDcaTxBlockAdapter = () => { + throw new Error("Not implemented"); + }; +} diff --git a/src/providers/interest/type-guards.ts b/src/providers/interest/type-guards.ts new file mode 100644 index 0000000..373e7c9 --- /dev/null +++ b/src/providers/interest/type-guards.ts @@ -0,0 +1,180 @@ +/* eslint-disable require-jsdoc */ +import { + CoinState, + InterestPool, + RebalancingParams, + StableFees, + StablePoolState, + VolatileFees, + VolatilePoolState, +} from "@interest-protocol/clamm-sdk"; + +export function isApiResponseValid(pools: unknown): pools is InterestPool[] { + return pools !== undefined && Array.isArray(pools) && pools.length > 0 && pools.every(isInterestPool); +} + +export function isInterestPool(pool: unknown): pool is InterestPool { + if ( + typeof pool !== "object" || + pool === null || + !("poolObjectId" in pool && typeof pool.poolObjectId === "string") || + !("stateId" in pool && typeof pool.stateId === "string") || + !("lpCoinType" in pool && typeof pool.lpCoinType === "string") || + !("isStable" in pool && typeof pool.isStable === "boolean") || + !("coinTypes" in pool && Array.isArray(pool.coinTypes)) || + !("poolAdminAddress" in pool && typeof pool.poolAdminAddress === "string") || + !("state" in pool && typeof pool.state === "object") + ) { + return false; + } + + if (pool.isStable) { + if (!isStablePoolState(pool.state)) { + return false; + } + } else { + if (!isVolatilePoolState(pool.state)) { + return false; + } + } + + return true; +} + +export function isStablePoolState(state: unknown): state is StablePoolState { + return ( + typeof state === "object" && + state !== null && + "lpCoinSupply" in state && + typeof state.lpCoinSupply === "bigint" && + "lpCoinDecimals" in state && + typeof state.lpCoinDecimals === "number" && + "balances" in state && + Array.isArray(state.balances) && + state.balances.every((balance: unknown) => typeof balance === "bigint") && + "initialA" in state && + typeof state.initialA === "bigint" && + "futureA" in state && + typeof state.futureA === "bigint" && + "initialATime" in state && + typeof state.initialATime === "bigint" && + "futureATime" in state && + typeof state.futureATime === "bigint" && + "nCoins" in state && + typeof state.nCoins === "number" && + "fees" in state && + isStableFees(state.fees) + ); +} + +export function isVolatilePoolState(state: unknown): state is VolatilePoolState { + return ( + typeof state === "object" && + state !== null && + "a" in state && + typeof state.a === "bigint" && + "futureA" in state && + typeof state.futureA === "bigint" && + "gamma" in state && + typeof state.gamma === "bigint" && + "initialTime" in state && + typeof state.initialTime === "bigint" && + "futureGamma" in state && + typeof state.futureGamma === "bigint" && + "futureTime" in state && + typeof state.futureTime === "bigint" && + "adminBalance" in state && + typeof state.adminBalance === "bigint" && + "balances" in state && + Array.isArray(state.balances) && + state.balances.every((balance) => typeof balance === "bigint") && + "d" in state && + typeof state.d === "bigint" && + "fees" in state && + isVolatileFees(state.fees) && + "lastPriceTimestamp" in state && + typeof state.lastPriceTimestamp === "bigint" && + "lpCoinSupply" in state && + typeof state.lpCoinSupply === "bigint" && + "maxA" in state && + typeof state.maxA === "bigint" && + "minA" in state && + typeof state.minA === "bigint" && + "nCoins" in state && + typeof state.nCoins === "number" && + "rebalancingParams" in state && + isRebalancingParams(state.rebalancingParams) && + "virtualPrice" in state && + typeof state.virtualPrice === "bigint" && + "xcpProfit" in state && + typeof state.xcpProfit === "bigint" && + "xcpProfitA" in state && + typeof state.xcpProfitA === "bigint" && + "notAdjusted" in state && + typeof state.notAdjusted === "boolean" && + "coinStateMap" in state && + isCoinStateMap(state.coinStateMap) + ); +} + +function isStableFees(fees: unknown): fees is StableFees { + return ( + typeof fees === "object" && + fees !== null && + "feeInPercent" in fees && + typeof fees.feeInPercent === "bigint" && + "feeOutPercent" in fees && + typeof fees.feeOutPercent === "bigint" && + "adminFeePercent" in fees && + typeof fees.adminFeePercent === "bigint" + ); +} + +function isVolatileFees(fees: unknown): fees is VolatileFees { + return ( + typeof fees === "object" && + fees !== null && + "adminFee" in fees && + typeof fees.adminFee === "bigint" && + "gammaFee" in fees && + typeof fees.gammaFee === "bigint" && + "midFee" in fees && + typeof fees.midFee === "bigint" && + "outFee" in fees && + typeof fees.outFee === "bigint" + ); +} + +function isRebalancingParams(params: unknown): params is RebalancingParams { + return ( + typeof params === "object" && + params !== null && + "adjustmentStep" in params && + typeof params.adjustmentStep === "bigint" && + "extraProfit" in params && + typeof params.extraProfit === "bigint" && + "maHalfTime" in params && + typeof params.maHalfTime === "bigint" + ); +} + +function isCoinStateMap(map: unknown): map is Record { + return typeof map === "object" && map !== null && Object.values(map).every(isCoinState); +} + +function isCoinState(state: unknown): state is CoinState { + return ( + typeof state === "object" && + state !== null && + "index" in state && + typeof state.index === "number" && + "lastPrice" in state && + typeof state.lastPrice === "bigint" && + "price" in state && + typeof state.price === "bigint" && + "priceOracle" in state && + typeof state.priceOracle === "bigint" && + "type" in state && + typeof state.type === "string" + ); +} diff --git a/src/providers/interest/types.ts b/src/providers/interest/types.ts new file mode 100644 index 0000000..dbcd06f --- /dev/null +++ b/src/providers/interest/types.ts @@ -0,0 +1,11 @@ +import { InterestPool } from "@interest-protocol/clamm-sdk"; +import { ProviderOptions } from "../types"; + +export type InterestRouteData = { + pool: InterestPool; + minAmount: bigint; + inputCoinType: string; + outputCoinType: string; +}; + +export type InterestOptions = ProviderOptions & { suiProviderUrl: string }; diff --git a/src/providers/interest/utils.ts b/src/providers/interest/utils.ts new file mode 100644 index 0000000..296c20c --- /dev/null +++ b/src/providers/interest/utils.ts @@ -0,0 +1,32 @@ +import { InterestPool } from "@interest-protocol/clamm-sdk"; +import { CommonPoolData } from "../types"; + +export const getPathMapAndCoinTypesSet = ( + pools: InterestPool[], +): { + pathMap: Map; + coinTypesSet: Set; +} => { + const pathMap: Map = new Map(); + const coinTypesSet: Set = new Set(); + + pools.forEach((pool: InterestPool) => { + const coinTypes = pool.coinTypes; + const base: string = coinTypes[0]; + const quote: string = coinTypes[1]; + + // Fill pathMap + const commonPoolData: CommonPoolData = { + base, + quote, + }; + const poolKey = `${base}-${quote}`; + pathMap.set(poolKey, commonPoolData); + + // Fill coinTypeSet + coinTypesSet.add(base); + coinTypesSet.add(quote); + }); + + return { pathMap, coinTypesSet }; +}; From 93e6e85e50a877ac7f9e4ffc2fe6b55aa58f3063 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 15:37:03 +0300 Subject: [PATCH 05/25] Add InterestProtocolSingleton to Provider type --- src/managers/types.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/managers/types.ts b/src/managers/types.ts index a8703f9..cc89b6f 100644 --- a/src/managers/types.ts +++ b/src/managers/types.ts @@ -2,6 +2,7 @@ import { TransactionBlock } from "@mysten/sui.js/transactions"; import { AftermathSingleton } from "../providers/aftermath/aftermath"; import { CetusSingleton } from "../providers/cetus/cetus"; import { FlowxSingleton } from "../providers/flowx/flowx"; +import { InterestProtocolSingleton } from "../providers/interest/interest"; import { TurbosSingleton } from "../providers/turbos/turbos"; import { TryCatchWrapperResult } from "../providers/types"; @@ -19,7 +20,12 @@ export type CoinAssetData = Omit & { export type UpdatedCoinsCache = { provider: string; data: CommonCoinData[] }; -export type Provider = TurbosSingleton | CetusSingleton | AftermathSingleton | FlowxSingleton; +export type Provider = + | TurbosSingleton + | CetusSingleton + | AftermathSingleton + | FlowxSingleton + | InterestProtocolSingleton; export type Providers = Provider[]; From 17c49801291be4a1c93930471bff6cdf42e88476 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 15:37:27 +0300 Subject: [PATCH 06/25] Create example for interest & add it to common example method --- examples/common.ts | 8 ++++- examples/interest/interest.ts | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 examples/interest/interest.ts diff --git a/examples/common.ts b/examples/common.ts index 7fc505b..990f59a 100644 --- a/examples/common.ts +++ b/examples/common.ts @@ -11,6 +11,7 @@ import { CetusSingleton } from "../src/providers/cetus/cetus"; import { clmmMainnet } from "../src/providers/cetus/config"; import { SWAP_GAS_BUDGET } from "../src/providers/common"; import { FlowxSingleton } from "../src/providers/flowx/flowx"; +import { InterestProtocolSingleton } from "../src/providers/interest/interest"; import { TurbosSingleton } from "../src/providers/turbos/turbos"; import { CacheOptions } from "../src/providers/types"; import { RedisStorageSingleton } from "../src/storages/RedisStorage"; @@ -91,8 +92,13 @@ export const initAndGetProviders = async (storage?: Storage): Promise cacheOptions: { storage, ...cacheOptions }, lazyLoading: false, }); + const interest: InterestProtocolSingleton = await InterestProtocolSingleton.getInstance({ + suiProviderUrl, + cacheOptions: { storage, ...cacheOptions }, + lazyLoading: false, + }); - const providers: Providers = [turbos, cetus, aftermath, flowx]; + const providers: Providers = [turbos, cetus, aftermath, flowx, interest]; return providers; }; diff --git a/examples/interest/interest.ts b/examples/interest/interest.ts new file mode 100644 index 0000000..1a3d6b0 --- /dev/null +++ b/examples/interest/interest.ts @@ -0,0 +1,55 @@ +import { LONG_SUI_COIN_TYPE } from "../../src"; +import { InterestProtocolSingleton } from "../../src/providers/interest/interest"; +import { cacheOptions, initAndGetRedisStorage, provider, suiProviderUrl, user } from "../common"; + +// yarn ts-node examples/interest/interest.ts +export const interest = async ({ + tokenFrom, + tokenTo, + amount, + slippagePercentage, + signerAddress, +}: { + tokenFrom: string; + tokenTo: string; + amount: string; + slippagePercentage: number; + signerAddress: string; +}) => { + const storage = await initAndGetRedisStorage(); + + const interest = await InterestProtocolSingleton.getInstance({ + suiProviderUrl, + cacheOptions: { storage, ...cacheOptions }, + lazyLoading: false, + }); + + const routeData = await interest.getRouteData({ + coinTypeFrom: tokenFrom, + coinTypeTo: tokenTo, + inputAmount: amount, + publicKey: signerAddress, + slippagePercentage, + }); + console.debug("routeData:", routeData); + + const transaction = await interest.getSwapTransaction({ + publicKey: user, + route: routeData.route, + slippagePercentage: 10, + }); + + const res = await provider.devInspectTransactionBlock({ + transactionBlock: transaction, + sender: user, + }); + console.debug("res: ", res); +}; + +interest({ + tokenFrom: LONG_SUI_COIN_TYPE, + tokenTo: "0xdb838a0becb92dcf9fd66127136f517f8f6d7a9f973b2344d1ebbd7d2cf2c0fa::meme_02_05_2024::MEME_02_05_2024", + amount: "0.01", + signerAddress: user, + slippagePercentage: 10, +}); From 2727b29c08f82357c02efb2209d6fdf85d082630 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 3 May 2024 16:52:18 +0300 Subject: [PATCH 07/25] Add JSDoc for Interest Protocol Singleton --- src/providers/interest/interest.ts | 47 +++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts index db3cfe9..9bd5cdc 100644 --- a/src/providers/interest/interest.ts +++ b/src/providers/interest/interest.ts @@ -1,4 +1,3 @@ -/* eslint-disable require-jsdoc */ import { CLAMM, InterestPool } from "@interest-protocol/clamm-sdk"; import { SuiClient } from "@mysten/sui.js-0.51.2/client"; import { CoinMetadata } from "@mysten/sui.js/client"; @@ -18,11 +17,24 @@ import { isApiResponseValid } from "./type-guards"; import { InterestOptions, InterestRouteData } from "./types"; import { getPathMapAndCoinTypesSet } from "./utils"; +/** + * @class InterestProtocolSingleton + * @extends EventEmitter + * @implements {IPoolProvider} + * @description Singleton class for Interest Protocol. + * + * Note: If using `lazyLoading: true` with any storage configuration in a serverless/cloud functions environment, + * be aware that each invocation of your cloud function will start cache population from scratch. + * This may lead to unexpected behavior when using different SDK methods. To avoid this and minimize the time + * for cache population, consider using `lazyLoading: false` along with passing a persistent + * storage adapter (external, e.g., Redis or any kind of DB) to the ProviderSingleton. + */ export class InterestProtocolSingleton extends EventEmitter implements IPoolProvider { private static _instance: InterestProtocolSingleton | undefined; private static INTEREST_PROTOCOL_PACKAGE_ADDRESS = "0xf47f67d87aad51b6bd476bf41bf578fd04ba444f6eab8e80950186f166ea1dba"; private static INTEREST_PROTOCOL_SUI_TEARS = "0xf7334947a5037552a94cee15fc471dbda71bf24d46c97ee24e1fdac38e26644c"; + private provider: SuiClient; private cacheOptions: CacheOptions; private intervalId: NodeJS.Timeout | undefined; @@ -35,6 +47,10 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv public coinsCache: CoinsCache = new Map(); public poolsCache: InterestPool[] = []; + /** + * @constructor + * @param {Omit} options - Options for InterestProtocolSingleton. + */ private constructor(options: Omit) { super(); @@ -52,6 +68,14 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv this.storage = options.cacheOptions.storage ?? InMemoryStorageSingleton.getInstance(); } + /** + * @static + * @method getInstance + * @async + * @param {InterestOptions} [options] - Options for InterestProtocolSingleton instance. + * @return {Promise} + * @throws Error if options are not provided. + */ public static async getInstance(options?: InterestOptions): Promise { if (!InterestProtocolSingleton._instance) { if (options === undefined) { @@ -176,6 +200,9 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv */ private async updatePoolsCache(): Promise { // TODO: Replace this method usage with the new Interest SDK method + /** + * Mock method for getting all the Interest Protocol pools. + */ async function getAllPools() { return []; } @@ -267,6 +294,16 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv return this.pathsCache; } + /** + * @public + * @method getRouteData + * @description Gets route data. + * @param {Object} params - Parameters for route data. + * @param {string} params.coinTypeFrom - Coin type from. + * @param {string} params.coinTypeTo - Coin type to. + * @param {string} params.inputAmount - Input amount. + * @return {Promise<{ outputAmount: bigint, route: InterestRouteData }>} Route data. + */ public async getRouteData({ coinTypeFrom, coinTypeTo, @@ -302,6 +339,14 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv }; } + /** + * @public + * @method getSwapTransaction + * @description Retrieves the swap transaction for the given route and public key. + * @param {InterestRouteData} route - The complete trade route. + * @param {string} publicKey - The public key. + * @return {Promise} A Promise that resolves to the swap transaction. + */ public async getSwapTransaction({ route, publicKey, From 954f04534dda0ddf97e758a1686079667c4fdef4 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 13:17:09 +0300 Subject: [PATCH 08/25] Upd interest SDK version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a69af0..a32dcfb 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "dependencies": { "@cetusprotocol/cetus-sui-clmm-sdk": "^3.17.8", "@flowx-pkg/ts-sdk": "^0.0.11", - "@interest-protocol/clamm-sdk": "^4.1.0-alpha", + "@interest-protocol/clamm-sdk": "^5.1.0-alpha", "@mysten/sui.js": "yarn:@mysten/sui.js@^0.42.0", "@mysten/sui.js-0.51.2": "yarn:@mysten/sui.js@^0.51.2", "@types/redis": "^4.0.11", diff --git a/yarn.lock b/yarn.lock index 6b48766..fd6ac23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -570,10 +570,10 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== -"@interest-protocol/clamm-sdk@^4.1.0-alpha": - version "4.1.0-alpha" - resolved "https://registry.yarnpkg.com/@interest-protocol/clamm-sdk/-/clamm-sdk-4.1.0-alpha.tgz#8d812c43cc90070dc07c1aeaed8d4d343f946287" - integrity sha512-rUxJMxJM5y6D0TuG74NJ70ejxV9KmldTHvdlZQ1FvCttQwEvo6G4UncjueGkNBO8Ons6b5VDrlcJn/j44hyDMw== +"@interest-protocol/clamm-sdk@^5.1.0-alpha": + version "5.1.0-alpha" + resolved "https://registry.yarnpkg.com/@interest-protocol/clamm-sdk/-/clamm-sdk-5.1.0-alpha.tgz#2eeb834f42a4883d1c0a73b403d901dfb5ba2c90" + integrity sha512-OYwSOYFevBnoJ8RD3RTwofYuIUvJg3sLTa4LPJhhX6uMqQbl4zKFEm3dB1oTgsGjwYDbK3NnOz5ODVt5A4usvA== dependencies: "@mysten/sui.js" "^0.51.2" ramda "^0.29.1" From ac947847668a5b0960f78aa503da50d9e825ada4 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 16:27:35 +0300 Subject: [PATCH 09/25] Create interest protocol config --- src/providers/interest/config.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/providers/interest/config.ts diff --git a/src/providers/interest/config.ts b/src/providers/interest/config.ts new file mode 100644 index 0000000..2e37f86 --- /dev/null +++ b/src/providers/interest/config.ts @@ -0,0 +1 @@ +export const ROUTES_QUOTES_AMOUNT_OBJECT_INDEX = 2; From ab58c78d69a540af8b5aa09c8dccef86a43a51c9 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 16:28:19 +0300 Subject: [PATCH 10/25] Create getBestInterestRoute() util --- src/providers/interest/utils.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/providers/interest/utils.ts b/src/providers/interest/utils.ts index 296c20c..fa71a6c 100644 --- a/src/providers/interest/utils.ts +++ b/src/providers/interest/utils.ts @@ -1,5 +1,6 @@ -import { InterestPool } from "@interest-protocol/clamm-sdk"; +import { GetRouteQuotesReturn, InterestPool } from "@interest-protocol/clamm-sdk"; import { CommonPoolData } from "../types"; +import { ROUTES_QUOTES_AMOUNT_OBJECT_INDEX } from "./config"; export const getPathMapAndCoinTypesSet = ( pools: InterestPool[], @@ -30,3 +31,14 @@ export const getPathMapAndCoinTypesSet = ( return { pathMap, coinTypesSet }; }; + +export const getBestInterestRoute = (routes: GetRouteQuotesReturn["routes"]) => { + const bestRoute = routes.reduce((bestRoute, currentRoute) => { + const bestAmount = bestRoute[ROUTES_QUOTES_AMOUNT_OBJECT_INDEX].amount; + const currentAmount = currentRoute[ROUTES_QUOTES_AMOUNT_OBJECT_INDEX].amount; + + return bestAmount > currentAmount ? bestRoute : currentRoute; + }); + + return bestRoute; +}; From 5eeebe63adc371f4d440113bbda6cad8a14d7e69 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 16:28:43 +0300 Subject: [PATCH 11/25] Upd interest protocol types --- src/providers/interest/types.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/providers/interest/types.ts b/src/providers/interest/types.ts index dbcd06f..174f4fd 100644 --- a/src/providers/interest/types.ts +++ b/src/providers/interest/types.ts @@ -1,11 +1,11 @@ -import { InterestPool } from "@interest-protocol/clamm-sdk"; +import { GetRouteQuotesReturn, SwapRouteArgs } from "@interest-protocol/clamm-sdk"; import { ProviderOptions } from "../types"; export type InterestRouteData = { - pool: InterestPool; - minAmount: bigint; + bestRoute: SwapRouteArgs["route"]; + poolsMap: GetRouteQuotesReturn["poolsMap"]; inputCoinType: string; - outputCoinType: string; + minAmount: bigint; }; export type InterestOptions = ProviderOptions & { suiProviderUrl: string }; From ac7844090f134eec3b663b3499fb6bcaf2b02e26 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 16:29:02 +0300 Subject: [PATCH 12/25] Upd interest protocol type guards --- src/providers/interest/type-guards.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/providers/interest/type-guards.ts b/src/providers/interest/type-guards.ts index 373e7c9..1a2ad46 100644 --- a/src/providers/interest/type-guards.ts +++ b/src/providers/interest/type-guards.ts @@ -18,11 +18,9 @@ export function isInterestPool(pool: unknown): pool is InterestPool { typeof pool !== "object" || pool === null || !("poolObjectId" in pool && typeof pool.poolObjectId === "string") || - !("stateId" in pool && typeof pool.stateId === "string") || !("lpCoinType" in pool && typeof pool.lpCoinType === "string") || !("isStable" in pool && typeof pool.isStable === "boolean") || !("coinTypes" in pool && Array.isArray(pool.coinTypes)) || - !("poolAdminAddress" in pool && typeof pool.poolAdminAddress === "string") || !("state" in pool && typeof pool.state === "object") ) { return false; From 6c06105d57ba43882267a59e9101fd50cc959a7b Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 16:29:50 +0300 Subject: [PATCH 13/25] Upd interest proto pools getting & getRouteData() & getSwapTransaction() --- src/providers/interest/interest.ts | 44 ++++++++++++------------------ 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts index 9bd5cdc..e223956 100644 --- a/src/providers/interest/interest.ts +++ b/src/providers/interest/interest.ts @@ -1,4 +1,4 @@ -import { CLAMM, InterestPool } from "@interest-protocol/clamm-sdk"; +import { CLAMM, InterestPool, SwapRouteArgs } from "@interest-protocol/clamm-sdk"; import { SuiClient } from "@mysten/sui.js-0.51.2/client"; import { CoinMetadata } from "@mysten/sui.js/client"; import { TransactionBlock } from "@mysten/sui.js/transactions"; @@ -15,7 +15,7 @@ import { CacheOptions, CoinsCache, CommonPoolData, IPoolProvider, PathsCache } f import { getUserCoinObjects } from "../utils/getUserCoinObjects"; import { isApiResponseValid } from "./type-guards"; import { InterestOptions, InterestRouteData } from "./types"; -import { getPathMapAndCoinTypesSet } from "./utils"; +import { getBestInterestRoute, getPathMapAndCoinTypesSet } from "./utils"; /** * @class InterestProtocolSingleton @@ -32,7 +32,7 @@ import { getPathMapAndCoinTypesSet } from "./utils"; export class InterestProtocolSingleton extends EventEmitter implements IPoolProvider { private static _instance: InterestProtocolSingleton | undefined; private static INTEREST_PROTOCOL_PACKAGE_ADDRESS = - "0xf47f67d87aad51b6bd476bf41bf578fd04ba444f6eab8e80950186f166ea1dba"; + "0x429dbf2fc849c0b4146db09af38c104ae7a3ed746baf835fa57fee27fa5ff382"; private static INTEREST_PROTOCOL_SUI_TEARS = "0xf7334947a5037552a94cee15fc471dbda71bf24d46c97ee24e1fdac38e26644c"; private provider: SuiClient; @@ -42,7 +42,7 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv public interestSdk: CLAMM; public providerName = "Interest"; - public isSmartRoutingAvailable = false; + public isSmartRoutingAvailable = true; public pathsCache: PathsCache = new Map(); public coinsCache: CoinsCache = new Map(); public poolsCache: InterestPool[] = []; @@ -199,15 +199,7 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv * @return {Promise} */ private async updatePoolsCache(): Promise { - // TODO: Replace this method usage with the new Interest SDK method - /** - * Mock method for getting all the Interest Protocol pools. - */ - async function getAllPools() { - return []; - } - - const pools: InterestPool[] = await getAllPools(); + const { pools }: { pools: readonly InterestPool[] } = await this.interestSdk.getPools(); const isValidPoolsResponse = isApiResponseValid(pools); if (!isValidPoolsResponse) { @@ -324,18 +316,19 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv const inputCoinDecimals = inputCoinData.decimals; const formattedInputAmount = new BigNumber(inputAmount).multipliedBy(10 ** inputCoinDecimals).toString(); - const pool = this.getPool(coinTypeFrom, coinTypeTo); - - const { amount } = await this.interestSdk.quoteSwap({ - pool, - coinInType: coinTypeFrom, - coinOutType: coinTypeTo, + const { routes, poolsMap } = await this.interestSdk.getRoutesQuotes({ + coinIn: coinTypeFrom, + coinOut: coinTypeTo, amount: BigInt(formattedInputAmount), }); + const [coinPath, poolObjectIdPath, amountObject] = getBestInterestRoute(routes); + const bestRoute: SwapRouteArgs["route"] = [coinPath, poolObjectIdPath]; + const { amount } = amountObject; + return { outputAmount: amount, - route: { pool, minAmount: amount, inputCoinType: coinTypeFrom, outputCoinType: coinTypeTo }, + route: { bestRoute, poolsMap, inputCoinType: coinTypeFrom, minAmount: amount }, }; } @@ -355,19 +348,18 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv publicKey: string; slippagePercentage: number; }): Promise { - const { inputCoinType, outputCoinType, minAmount, pool } = route; + const { bestRoute, poolsMap, inputCoinType, minAmount } = route; const inputCoinObjects = await getUserCoinObjects({ coinType: inputCoinType, provider: this.provider, publicKey }); const { destinationObjectId, tx } = WalletManagerSingleton.mergeAllCoinObjects({ coinObjects: inputCoinObjects, }); - const { coinOut, txb } = await this.interestSdk.swap({ + const { coinOut, txb } = this.interestSdk.swapRoute({ txb: tx, - coinIn: tx.object(destinationObjectId), - coinInType: inputCoinType, - coinOutType: outputCoinType, - pool, + coinIn: destinationObjectId, + route: bestRoute, + poolsMap, minAmount, }); From 8b2e2623c721d4b364848750ca2f109ac359f8f1 Mon Sep 17 00:00:00 2001 From: bathord Date: Mon, 6 May 2024 16:30:10 +0300 Subject: [PATCH 14/25] Upd interest example --- examples/interest/interest.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/interest/interest.ts b/examples/interest/interest.ts index 1a3d6b0..db3ddac 100644 --- a/examples/interest/interest.ts +++ b/examples/interest/interest.ts @@ -1,4 +1,3 @@ -import { LONG_SUI_COIN_TYPE } from "../../src"; import { InterestProtocolSingleton } from "../../src/providers/interest/interest"; import { cacheOptions, initAndGetRedisStorage, provider, suiProviderUrl, user } from "../common"; @@ -47,9 +46,9 @@ export const interest = async ({ }; interest({ - tokenFrom: LONG_SUI_COIN_TYPE, - tokenTo: "0xdb838a0becb92dcf9fd66127136f517f8f6d7a9f973b2344d1ebbd7d2cf2c0fa::meme_02_05_2024::MEME_02_05_2024", - amount: "0.01", + tokenFrom: "0xae870af23dda8285a5f11e8136190568796bb76a6e7f3b4061f7ded0c1ebe889::usdt::USDT", + tokenTo: "0x62a807f396a729dfb9dd931bc6a49d840ede3ce058fe11e38d1f097d8466ee60::bonden::BONDEN", + amount: "10", signerAddress: user, slippagePercentage: 10, }); From dfa71ae52f24fed7473ef431b6990bb7a4a33c35 Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 7 May 2024 20:23:06 +0300 Subject: [PATCH 15/25] Add formattedInputAmount to InterestRouteData type --- src/providers/interest/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/providers/interest/types.ts b/src/providers/interest/types.ts index 174f4fd..269b28e 100644 --- a/src/providers/interest/types.ts +++ b/src/providers/interest/types.ts @@ -6,6 +6,7 @@ export type InterestRouteData = { poolsMap: GetRouteQuotesReturn["poolsMap"]; inputCoinType: string; minAmount: bigint; + formattedInputAmount: string; }; export type InterestOptions = ProviderOptions & { suiProviderUrl: string }; From 9b02b7df2b8cac39e79caf9227f7a1f0f8ff50be Mon Sep 17 00:00:00 2001 From: bathord Date: Tue, 7 May 2024 20:24:28 +0300 Subject: [PATCH 16/25] Upd interest getSwapTransaction() & handle short sui coin type --- src/providers/interest/interest.ts | 33 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts index e223956..5a7a76b 100644 --- a/src/providers/interest/interest.ts +++ b/src/providers/interest/interest.ts @@ -1,5 +1,6 @@ -import { CLAMM, InterestPool, SwapRouteArgs } from "@interest-protocol/clamm-sdk"; +import { CLAMM, InterestPool, MoveObjectArgument, SwapRouteArgs } from "@interest-protocol/clamm-sdk"; import { SuiClient } from "@mysten/sui.js-0.51.2/client"; +import { TransactionBlock as UpdatedTransactionBlock } from "@mysten/sui.js-0.51.2/transactions"; import { CoinMetadata } from "@mysten/sui.js/client"; import { TransactionBlock } from "@mysten/sui.js/transactions"; import BigNumber from "bignumber.js"; @@ -10,9 +11,10 @@ import { InMemoryStorageSingleton } from "../../storages/InMemoryStorage"; import { Storage } from "../../storages/types"; import { getCoinsAndPathsCaches } from "../../storages/utils/getCoinsAndPathsCaches"; import { storeCaches } from "../../storages/utils/storeCaches"; -import { exitHandlerWrapper } from "../common"; +import { LONG_SUI_COIN_TYPE, exitHandlerWrapper } from "../common"; import { CacheOptions, CoinsCache, CommonPoolData, IPoolProvider, PathsCache } from "../types"; import { getUserCoinObjects } from "../utils/getUserCoinObjects"; +import { isSuiCoinType } from "../utils/isSuiCoinType"; import { isApiResponseValid } from "./type-guards"; import { InterestOptions, InterestRouteData } from "./types"; import { getBestInterestRoute, getPathMapAndCoinTypesSet } from "./utils"; @@ -307,6 +309,13 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv slippagePercentage: number; publicKey: string; }): Promise<{ outputAmount: bigint; route: InterestRouteData }> { + // Interest Protocol Routing crashes, when using SHORT_SUI_COIN_TYPE + if (isSuiCoinType(coinTypeFrom)) { + coinTypeFrom = LONG_SUI_COIN_TYPE; + } else if (isSuiCoinType(coinTypeTo)) { + coinTypeTo = LONG_SUI_COIN_TYPE; + } + const inputCoinData = await this.provider.getCoinMetadata({ coinType: coinTypeFrom }); if (inputCoinData === null) { @@ -328,7 +337,7 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv return { outputAmount: amount, - route: { bestRoute, poolsMap, inputCoinType: coinTypeFrom, minAmount: amount }, + route: { bestRoute, poolsMap, inputCoinType: coinTypeFrom, minAmount: amount, formattedInputAmount }, }; } @@ -348,12 +357,22 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv publicKey: string; slippagePercentage: number; }): Promise { - const { bestRoute, poolsMap, inputCoinType, minAmount } = route; + const tx = new UpdatedTransactionBlock(); + const { bestRoute, poolsMap, inputCoinType, minAmount, formattedInputAmount } = route; const inputCoinObjects = await getUserCoinObjects({ coinType: inputCoinType, provider: this.provider, publicKey }); - const { destinationObjectId, tx } = WalletManagerSingleton.mergeAllCoinObjects({ - coinObjects: inputCoinObjects, - }); + let destinationObjectId: MoveObjectArgument; + + if (isSuiCoinType(inputCoinType)) { + const [coin] = tx.splitCoins(tx.gas, [tx.pure(formattedInputAmount)]); + destinationObjectId = coin; + } else { + const { destinationObjectId: mergeDestination } = WalletManagerSingleton.mergeAllCoinObjects({ + coinObjects: inputCoinObjects, + }); + + destinationObjectId = mergeDestination; + } const { coinOut, txb } = this.interestSdk.swapRoute({ txb: tx, From b6db0e83882e254d9709a77e43ddc1f27a17da7d Mon Sep 17 00:00:00 2001 From: bathord Date: Thu, 9 May 2024 20:29:36 +0300 Subject: [PATCH 17/25] Bump interest sdk version --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a32dcfb..2afbf45 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "dependencies": { "@cetusprotocol/cetus-sui-clmm-sdk": "^3.17.8", "@flowx-pkg/ts-sdk": "^0.0.11", - "@interest-protocol/clamm-sdk": "^5.1.0-alpha", + "@interest-protocol/clamm-sdk": "^6.1.1-alpha", "@mysten/sui.js": "yarn:@mysten/sui.js@^0.42.0", "@mysten/sui.js-0.51.2": "yarn:@mysten/sui.js@^0.51.2", "@types/redis": "^4.0.11", diff --git a/yarn.lock b/yarn.lock index fd6ac23..1315c5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -570,10 +570,10 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== -"@interest-protocol/clamm-sdk@^5.1.0-alpha": - version "5.1.0-alpha" - resolved "https://registry.yarnpkg.com/@interest-protocol/clamm-sdk/-/clamm-sdk-5.1.0-alpha.tgz#2eeb834f42a4883d1c0a73b403d901dfb5ba2c90" - integrity sha512-OYwSOYFevBnoJ8RD3RTwofYuIUvJg3sLTa4LPJhhX6uMqQbl4zKFEm3dB1oTgsGjwYDbK3NnOz5ODVt5A4usvA== +"@interest-protocol/clamm-sdk@^6.1.1-alpha": + version "6.1.1-alpha" + resolved "https://registry.yarnpkg.com/@interest-protocol/clamm-sdk/-/clamm-sdk-6.1.1-alpha.tgz#8d412e5a786d78e24fa04b4c54126ac46ac37e06" + integrity sha512-0nkWJSJlgvPFawCa5Z+k+RVZWcHihpybMCwhHUhA0YEM/Miwq+QkBFVH5UvEwT88tc5CEA86/4XWItCkRIi/sw== dependencies: "@mysten/sui.js" "^0.51.2" ramda "^0.29.1" From fedbb41e3128b0182e4078b8c72c67c78aef822b Mon Sep 17 00:00:00 2001 From: bathord Date: Thu, 9 May 2024 20:30:03 +0300 Subject: [PATCH 18/25] Add return type for getBestInterestRoute() --- src/providers/interest/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/interest/utils.ts b/src/providers/interest/utils.ts index fa71a6c..0eaaac3 100644 --- a/src/providers/interest/utils.ts +++ b/src/providers/interest/utils.ts @@ -32,7 +32,7 @@ export const getPathMapAndCoinTypesSet = ( return { pathMap, coinTypesSet }; }; -export const getBestInterestRoute = (routes: GetRouteQuotesReturn["routes"]) => { +export const getBestInterestRoute = (routes: GetRouteQuotesReturn["routes"]): GetRouteQuotesReturn["routes"][0] => { const bestRoute = routes.reduce((bestRoute, currentRoute) => { const bestAmount = bestRoute[ROUTES_QUOTES_AMOUNT_OBJECT_INDEX].amount; const currentAmount = currentRoute[ROUTES_QUOTES_AMOUNT_OBJECT_INDEX].amount; From 5c3d11a67079e12489fb98cb829fc312af3d25b8 Mon Sep 17 00:00:00 2001 From: bathord Date: Thu, 9 May 2024 20:30:26 +0300 Subject: [PATCH 19/25] Add try/catch for updatePoolsCache() --- src/providers/interest/interest.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts index 5a7a76b..44335af 100644 --- a/src/providers/interest/interest.ts +++ b/src/providers/interest/interest.ts @@ -201,15 +201,19 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv * @return {Promise} */ private async updatePoolsCache(): Promise { - const { pools }: { pools: readonly InterestPool[] } = await this.interestSdk.getPools(); - const isValidPoolsResponse = isApiResponseValid(pools); + try { + const { pools }: { pools: readonly InterestPool[] } = await this.interestSdk.getPools(); + const isValidPoolsResponse = isApiResponseValid(pools); - if (!isValidPoolsResponse) { - console.error("[Interest] Pools response:", pools); - throw new Error("Pools response from API is not valid"); - } + if (!isValidPoolsResponse) { + console.error("[Interest] Pools response:", pools); + throw new Error("Pools response from API is not valid"); + } - this.poolsCache = pools; + this.poolsCache = pools; + } catch (error) { + console.error("[Interest.updatePoolsCache]:", error); + } } /** From e3d7383ecf85050f4f921a7807f5fb8bc45d8502 Mon Sep 17 00:00:00 2001 From: bathord Date: Thu, 9 May 2024 20:31:28 +0300 Subject: [PATCH 20/25] Export InterestProtocol from index --- src/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/index.ts b/src/index.ts index 924b7bc..a3f9ac1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,13 @@ export * from "./providers/turbos/turbos"; export * from "./providers/turbos/types"; export * from "./providers/turbos/utils"; +// Interest Protocol +export * from "./providers/interest/interest"; +export * from "./providers/interest/types"; +export * from "./providers/interest/utils"; +export * from "./providers/interest/type-guards"; +export * from "./providers/interest/config"; + // Storages export * from "./storages/RedisStorage"; export * from "./storages/InMemoryStorage"; From 9358251ced21b71d1f6e1b3635961aed4af91564 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 10 May 2024 15:02:50 +0300 Subject: [PATCH 21/25] Create getAmountWithSlippage() --- src/providers/interest/utils.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/providers/interest/utils.ts b/src/providers/interest/utils.ts index 0eaaac3..fe81654 100644 --- a/src/providers/interest/utils.ts +++ b/src/providers/interest/utils.ts @@ -1,4 +1,5 @@ import { GetRouteQuotesReturn, InterestPool } from "@interest-protocol/clamm-sdk"; +import BigNumber from "bignumber.js"; import { CommonPoolData } from "../types"; import { ROUTES_QUOTES_AMOUNT_OBJECT_INDEX } from "./config"; @@ -42,3 +43,9 @@ export const getBestInterestRoute = (routes: GetRouteQuotesReturn["routes"]): Ge return bestRoute; }; + +export const getAmountWithSlippage = (amount: string, slippagePercentage: number): string => { + const slippageAmount = new BigNumber(amount).multipliedBy(slippagePercentage).div(100); + + return new BigNumber(amount).minus(slippageAmount).toFixed(0); +}; From 45ade32dedf01b83a2a2eef2a200588f8841477b Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 10 May 2024 15:03:20 +0300 Subject: [PATCH 22/25] Fix interest methods to work with slippage & split input coin --- src/providers/interest/interest.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts index 44335af..e9ff0bb 100644 --- a/src/providers/interest/interest.ts +++ b/src/providers/interest/interest.ts @@ -17,7 +17,7 @@ import { getUserCoinObjects } from "../utils/getUserCoinObjects"; import { isSuiCoinType } from "../utils/isSuiCoinType"; import { isApiResponseValid } from "./type-guards"; import { InterestOptions, InterestRouteData } from "./types"; -import { getBestInterestRoute, getPathMapAndCoinTypesSet } from "./utils"; +import { getAmountWithSlippage, getBestInterestRoute, getPathMapAndCoinTypesSet } from "./utils"; /** * @class InterestProtocolSingleton @@ -33,9 +33,9 @@ import { getBestInterestRoute, getPathMapAndCoinTypesSet } from "./utils"; */ export class InterestProtocolSingleton extends EventEmitter implements IPoolProvider { private static _instance: InterestProtocolSingleton | undefined; - private static INTEREST_PROTOCOL_PACKAGE_ADDRESS = + public static INTEREST_PROTOCOL_PACKAGE_ADDRESS = "0x429dbf2fc849c0b4146db09af38c104ae7a3ed746baf835fa57fee27fa5ff382"; - private static INTEREST_PROTOCOL_SUI_TEARS = "0xf7334947a5037552a94cee15fc471dbda71bf24d46c97ee24e1fdac38e26644c"; + public static INTEREST_PROTOCOL_SUI_TEARS = "0xf7334947a5037552a94cee15fc471dbda71bf24d46c97ee24e1fdac38e26644c"; private provider: SuiClient; private cacheOptions: CacheOptions; @@ -306,6 +306,7 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv coinTypeFrom, coinTypeTo, inputAmount, + slippagePercentage, }: { coinTypeFrom: string; coinTypeTo: string; @@ -339,9 +340,17 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv const bestRoute: SwapRouteArgs["route"] = [coinPath, poolObjectIdPath]; const { amount } = amountObject; + const amountWithSlippage = getAmountWithSlippage(amount.toString(), slippagePercentage); + return { - outputAmount: amount, - route: { bestRoute, poolsMap, inputCoinType: coinTypeFrom, minAmount: amount, formattedInputAmount }, + outputAmount: BigInt(amountWithSlippage), + route: { + bestRoute, + poolsMap, + inputCoinType: coinTypeFrom, + minAmount: BigInt(amountWithSlippage), + formattedInputAmount, + }, }; } @@ -373,9 +382,11 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv } else { const { destinationObjectId: mergeDestination } = WalletManagerSingleton.mergeAllCoinObjects({ coinObjects: inputCoinObjects, + txb: tx, }); - destinationObjectId = mergeDestination; + const [coin] = tx.splitCoins(tx.object(mergeDestination), [tx.pure(formattedInputAmount)]); + destinationObjectId = coin; } const { coinOut, txb } = this.interestSdk.swapRoute({ From 65829b9348a5a24682c5bb63b434613074ff6950 Mon Sep 17 00:00:00 2001 From: bathord Date: Fri, 10 May 2024 15:17:46 +0300 Subject: [PATCH 23/25] Prettify & add comments --- src/providers/interest/interest.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/providers/interest/interest.ts b/src/providers/interest/interest.ts index e9ff0bb..17bdab3 100644 --- a/src/providers/interest/interest.ts +++ b/src/providers/interest/interest.ts @@ -338,9 +338,7 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv const [coinPath, poolObjectIdPath, amountObject] = getBestInterestRoute(routes); const bestRoute: SwapRouteArgs["route"] = [coinPath, poolObjectIdPath]; - const { amount } = amountObject; - - const amountWithSlippage = getAmountWithSlippage(amount.toString(), slippagePercentage); + const amountWithSlippage = getAmountWithSlippage(amountObject.amount.toString(), slippagePercentage); return { outputAmount: BigInt(amountWithSlippage), @@ -376,10 +374,13 @@ export class InterestProtocolSingleton extends EventEmitter implements IPoolProv const inputCoinObjects = await getUserCoinObjects({ coinType: inputCoinType, provider: this.provider, publicKey }); let destinationObjectId: MoveObjectArgument; + // If SUI is an input coin, just split a needed for a swap input amount from gas object. if (isSuiCoinType(inputCoinType)) { const [coin] = tx.splitCoins(tx.gas, [tx.pure(formattedInputAmount)]); destinationObjectId = coin; } else { + // If the input coin is not SUI, merge all its objects into one and split the needed for the swap input amount + // from the merge-result object. const { destinationObjectId: mergeDestination } = WalletManagerSingleton.mergeAllCoinObjects({ coinObjects: inputCoinObjects, txb: tx, From 9fbcd8c1dc219b7efb59907105a17ef346940b0b Mon Sep 17 00:00:00 2001 From: bathord Date: Sat, 11 May 2024 18:46:35 +0300 Subject: [PATCH 24/25] Remove wallet manager getAllCoinObjects() & replace it with getUserCoinObjects() --- examples/dca/create-dca.ts | 12 +++------ examples/dca/deposit-base.ts | 31 +++-------------------- src/managers/WalletManager.ts | 47 ----------------------------------- 3 files changed, 8 insertions(+), 82 deletions(-) diff --git a/examples/dca/create-dca.ts b/examples/dca/create-dca.ts index dadf5e7..f179633 100644 --- a/examples/dca/create-dca.ts +++ b/examples/dca/create-dca.ts @@ -1,10 +1,10 @@ import { SuiClient } from "@mysten/sui.js/client"; import { TransactionBlock } from "@mysten/sui.js/transactions"; -import { CoinManagerSingleton, WalletManagerSingleton } from "../../src"; import { DCAManagerSingleton } from "../../src/managers/dca/DCAManager"; import { DCATimescale } from "../../src/managers/dca/types"; +import { getUserCoinObjects } from "../../src/providers/utils/getUserCoinObjects"; import { RINCEL_COIN_TYPE, USDC_COIN_TYPE } from "../coin-types"; -import { initAndGetProviders, initAndGetRedisStorage, keypair, user } from "../common"; +import { keypair, user } from "../common"; // yarn ts-node examples/dca/create-dca.ts export const createDCA = async () => { @@ -23,14 +23,10 @@ export const createDCA = async () => { // TODO: Need to update inner function where this value is used const baseCoinAmountToDepositIntoDCA = "150000"; - const storage = await initAndGetRedisStorage(); - const providers = await initAndGetProviders(storage); - const coinManager: CoinManagerSingleton = CoinManagerSingleton.getInstance(providers, suiProviderUrl); - const walletManager: WalletManagerSingleton = WalletManagerSingleton.getInstance(provider, coinManager); - - const allCoinObjectsList = await walletManager.getAllCoinObjects({ + const allCoinObjectsList = await getUserCoinObjects({ publicKey: keypair.toSuiAddress(), coinType: baseCoinType, + provider, }); const totalOrders = 10; diff --git a/examples/dca/deposit-base.ts b/examples/dca/deposit-base.ts index 7b5c7e4..18808a7 100644 --- a/examples/dca/deposit-base.ts +++ b/examples/dca/deposit-base.ts @@ -1,16 +1,8 @@ import { SuiClient } from "@mysten/sui.js/client"; import { TransactionBlock } from "@mysten/sui.js/transactions"; -import { - AftermathSingleton, - CetusSingleton, - CoinManagerSingleton, - Providers, - TurbosSingleton, - WalletManagerSingleton, - clmmMainnet, -} from "../../src"; import { DCAManagerSingleton } from "../../src/managers/dca/DCAManager"; -import { cacheOptions, keypair, user } from "../common"; +import { getUserCoinObjects } from "../../src/providers/utils/getUserCoinObjects"; +import { keypair, user } from "../common"; // yarn ts-node examples/dca/deposit-base.ts export const depositBase = async () => { @@ -37,25 +29,10 @@ export const depositBase = async () => { const baseCoinAmountToDepositIntoDCA = "250000"; const addOrdersCount = 1; - const turbos: TurbosSingleton = await TurbosSingleton.getInstance({ - suiProviderUrl, - cacheOptions, - lazyLoading: false, - }); - const cetus: CetusSingleton = await CetusSingleton.getInstance({ - sdkOptions: clmmMainnet, - cacheOptions, - suiProviderUrl, - lazyLoading: false, - }); - const aftermath: AftermathSingleton = await AftermathSingleton.getInstance({ cacheOptions, lazyLoading: false }); - const providers: Providers = [turbos, cetus, aftermath]; - const coinManager: CoinManagerSingleton = CoinManagerSingleton.getInstance(providers, suiProviderUrl); - const walletManager: WalletManagerSingleton = WalletManagerSingleton.getInstance(provider, coinManager); - - const allCoinObjectsList = await walletManager.getAllCoinObjects({ + const allCoinObjectsList = await getUserCoinObjects({ publicKey: keypair.toSuiAddress(), coinType: baseCoinType, + provider, }); const { tx, txRes } = await DCAManagerSingleton.createDCADepositBaseTransaction({ diff --git a/src/managers/WalletManager.ts b/src/managers/WalletManager.ts index d1e9595..c7d3dd8 100644 --- a/src/managers/WalletManager.ts +++ b/src/managers/WalletManager.ts @@ -318,53 +318,6 @@ export class WalletManagerSingleton implements IWalletManager { return coinAssets; } - /** - * @public - * @method getAllCoinObjects - * @param {Object} params - Parameters object. - * @param {string} params.publicKey - The public key of the wallet. - * @param {string} params.coinType - The coin type of specified coin. - * @description Retrieves all coin objects associated with a wallet and specified coinType. - * @return {Promise} A promise that resolves to an array of coin objects data. - */ - public async getAllCoinObjects({ - publicKey, - coinType, - }: { - publicKey: string; - coinType: string; - }): Promise { - const pageCapacity = 50; - const allObjects: CoinStruct[] = []; - let nextCursor: string | null | undefined = null; - let assets: PaginatedCoins = await this.provider.getCoins({ - owner: publicKey, - coinType, - limit: pageCapacity, - cursor: nextCursor, - }); - - // fetching and combining part - while (assets.hasNextPage) { - const coinObjects: CoinStruct[] = assets.data; - allObjects.push(...coinObjects); - - nextCursor = assets.nextCursor; - assets = await this.provider.getCoins({ - owner: publicKey, - coinType, - limit: pageCapacity, - cursor: nextCursor, - }); - } - - // In case user has less tokens than `pageCapacity` (1 page only), we should put them into `allObjects` - const coinObjects: CoinStruct[] = assets.data; - allObjects.push(...coinObjects); - - return allObjects; - } - /** * Note: this method is using an `UpdatedTransactionBlock`, that is a `TransactionBlock` from * the @mysten/sui.js v0.51.2 package. From 5cf03c162f3341b942e4d8e0a359d40eee7203e2 Mon Sep 17 00:00:00 2001 From: avernikoz Date: Sat, 11 May 2024 19:20:54 +0300 Subject: [PATCH 25/25] upgrade jest.config.js --- jest.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jest.config.js b/jest.config.js index 3abcbd9..0e517ef 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,4 +2,7 @@ module.exports = { preset: "ts-jest", testEnvironment: "node", + transform: { + "^.+\\.[tj]s$": "ts-jest", + }, };