UNPKG

navi-sdk

Version:

The NAVI SDK Client provides tools for interacting with the Sui blockchain networks, designed for handling transactions, accounts, and smart contracts efficiently. This documentation covers the setup, account management, and transaction handling within th

449 lines (390 loc) 18.9 kB
import { describe, it, expect } from 'vitest'; import { NAVISDKClient } from '../src/index'; import { NAVX, nUSDC, Sui, wUSDC } from '../src/address'; import { Transaction } from "@mysten/sui/transactions"; import { borrowCoin, depositCoin, withdrawCoin, repayDebt, stakeTovSuiPTB, updateOraclePTB, swapPTB, SignAndSubmitTXB, checkIfNAVIIntegrated } from '../src/libs/PTB'; import { Pool, PoolConfig, CoinInfo, OptionType } from "../src/types"; import { getConfig, pool, AddressMap, vSui } from "../src/address"; import { error } from 'console'; import dotenv from 'dotenv'; import { AccountManager } from '../src/libs/AccountManager'; import { migrateBorrowPTB, migrateSupplyPTB } from '../src/libs/PTB/migrate'; import { SuiClient } from '@mysten/sui/client'; import { haSui } from "../src/address"; import { generateRefId } from '../src/libs/Aggregator/utils'; dotenv.config(); const rpcUrl = process.env.RPC || ''; const mnemonic = process.env.MNEMONIC || ''; const privateKey = process.env.PRIVATE_KEY || ''; const apiKey = process.env.API_KEY || ''; async function dryRunTXB(txb: Transaction, client: SuiClient) { const dryRunTxBytes: Uint8Array = await txb.build({ client: client }); const dryRunResult = await client.dryRunTransactionBlock({ transactionBlock: dryRunTxBytes }); return dryRunResult; } describe('NAVI SDK Client', async () => { const client = new NAVISDKClient({ networkType: rpcUrl, mnemonic: mnemonic }); const account = client.accounts[0]; it('should generate correct account', async () => { expect(client.accounts[0].getPublicKey()).toBe(client.getAllAccounts()[0].getPublicKey()); }); it('should generate correct account with private key in client', async () => { const client1 = new NAVISDKClient({ privateKeyList: [privateKey, privateKey], networkType: rpcUrl }); expect(client1.accounts.length).toBe(2); expect(client1.accounts[0].getPublicKey()).toBe(client.accounts[0].getPublicKey()); }); it('should generate correct account with private key with AccountManager', async () => { const account = new AccountManager({ privateKey: privateKey }); expect(account.getPublicKey()).toBe(client.getAllAccounts()[0].getPublicKey()); }); it('should get correct pool Info', async () => { const coinAddress = '0xa99b8952d4f7d947ea77fe0ecdcc9e5fc0bcab2841d6e2a5aa00c3044e5544b5::navx::NAVX'; const res = await client.getPoolInfo(NAVX) expect(res.coin_type).toBe(coinAddress); }); it('should get correct reserve Info', async () => { const poolObjectId = '0x2e13b2f1f714c0c5fa72264f147ef7632b48ec2501f810c07df3ccb59d6fdc81'; const res = await client.getReserveDetail(NAVX) expect(res.data?.objectId).toBe(poolObjectId); }); it('should get correct check user health factor', async () => { const userToCheck = '0x523c19fd6af645a12c6cb69bff0740b693aedd3f7613b1b20aa40d78f45204be'; const res = await client.getHealthFactor(userToCheck) expect(res).toBeGreaterThan(1); expect(res).toBeLessThan(20); }); it('should get correct check user dynamic health factor', async () => { const userToCheck = '0x523c19fd6af645a12c6cb69bff0740b693aedd3f7613b1b20aa40d78f45204be'; const res = await client.getDynamicHealthFactor(userToCheck, Sui, 1e9, 0, true) const resNumber = Number(res); expect(resNumber).toBeGreaterThan(1); expect(resNumber).toBeLessThan(20); }); it('should get correct return all accounts\' Navi Portfolio', async () => { const res = await client.getAllNaviPortfolios(); const haSui: any = res.get('HaedalSui'); expect(haSui.borrowBalance).toBe(0); expect(haSui.supplyBalance).toBe(0); }); it('should get correct return all accounts\' wallet balance', async () => { const res = await client.getAllBalances(); const sui: any = res; expect(typeof sui).toBe('object'); }); it('should get correct return available rewards for specific address', async () => { const address = '0xcb98748e6a6bb10d0250eeaef3aade81003b5ac25034c08a95e090768473144b'; const res = await client.getAddressAvailableRewards(address); const reward = res['5']; expect(reward.asset_id).toBe('5'); }); it('should check if NAVI is integrated', async () => { const digest = "4JwmF4UFESM2b18wgNgGsfHA7MStTDAomLuQQnUaa6qr"; const res = await checkIfNAVIIntegrated(digest, account.client); expect(res).toBe(true); }); it('should check if NAVI is not integrated', async () => { const digest = "F94zASa3cudFffhetkW898MDt3ZT1qEmxvAUULp6BSbP"; const res = await checkIfNAVIIntegrated(digest, account.client); expect(res).toBe(false); }); it('should generate correct ref_id', async () => { const apiKey = '5aaaaa-test-4844-bf46-1d6dff248e7a'; const res = (apiKey); expect(res).toBe(5307806464); }); it('should return same ref_id for same apiKey', async () => { const apiKey = '5aaaaa-test-4844-bf46-1d6dff248e7a'; const res1 = generateRefId(apiKey); const res2 = generateRefId(apiKey); expect(res1).toBe(res2); }); it('should return different ref_id for different apiKey', async () => { const apiKey1 = '5aaaaa-test-4844-bf46-1d6dff248e7a'; const apiKey2 = '6bbbbb-test-4844-bf46-1d6dff248e7b'; const res1 = generateRefId(apiKey1); const res2 = generateRefId(apiKey2); expect(res1).not.toBe(res2); }); it('should generate ref_id as an integer and meet contract precision requirements', async () => { const apiKey = '5aaaaa-test-4844-bf46-1d6dff248e7a'; const res = generateRefId(apiKey); expect(Number.isInteger(res)).toBe(true); expect(res).toBeGreaterThanOrEqual(0); expect(res).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); }); }); describe('NAVI SDK Account Manager', async () => { const client = new NAVISDKClient({ networkType: rpcUrl, mnemonic: mnemonic }); const account = client.accounts[0]; it('should return correct public key', async () => { const publicKey = account.getPublicKey(); expect(account.address).toBe(publicKey); }); it('should return correct derivationPath', async () => { const path = account.getDerivationPath(0); expect(path).toBe(`m/44'/784'/0'/0'/0'`); }); it('should return correct coin decimal', async () => { const path = await account.getCoinDecimal(Sui); expect(path).toEqual(9); }); it('should success deposit Sui to NAVI protocol', async () => { let txb = new Transaction(); txb.setSender(account.address); const poolConfig: PoolConfig = pool["Sui" as keyof Pool]; const [toDeposit] = txb.splitCoins(txb.gas, [1e9]); await depositCoin(txb, poolConfig, toDeposit, 1e9); const result = await account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address, }) if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); it('should success withdraw Sui to NAVI protocol', async () => { let txb = new Transaction(); txb.setSender(account.address); const poolConfig: PoolConfig = pool["Sui" as keyof Pool]; const [toDeposit] = txb.splitCoins(txb.gas, [1e9]); await depositCoin(txb, poolConfig, toDeposit, 1e9); await withdrawCoin(txb, poolConfig, 1e9); const result = await account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address, }) if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); it('should success borrow Sui from NAVI protocol', async () => { let txb = new Transaction(); txb.setSender(account.address); const poolConfig: PoolConfig = pool["Sui" as keyof Pool]; const [toDeposit] = txb.splitCoins(txb.gas, [1e9]); await depositCoin(txb, poolConfig, toDeposit, 1e9); await borrowCoin(txb, poolConfig, 0.5e9); const result = await account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address, }) if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); it('should success Repay Sui from NAVI protocol - random address might fail', async () => { let txb = new Transaction(); txb.setSender('0x010ac247d4b9f8fcc9704b53b4aee5c4b00c3263b17bc39d3898ea802518bac9'); const poolConfig: PoolConfig = pool["USDT" as keyof Pool]; const [toRepay] = txb.splitCoins("0x7ae2714cb57cad133e62b83c58c59f4332a912256193203e86905571aa420d03", [0.1e6]); await repayDebt(txb, poolConfig, toRepay, 0.1e6); const result = await account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: '0x010ac247d4b9f8fcc9704b53b4aee5c4b00c3263b17bc39d3898ea802518bac9' }) if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); it('should success stake Sui to Volo Sui', async () => { let txb = new Transaction(); const [toStake] = txb.splitCoins(txb.gas, [1e9]); await stakeTovSuiPTB(txb, toStake); const result = await account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address }) if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); it('should success update oracle', async () => { let txb = new Transaction(); txb.setSender(account.address); await updateOraclePTB(account.client, txb); const result = await account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address, }) if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); it('should get correct NS rewards', async () => { const res = await client.getAddressAvailableRewards('0x33be2c133f87c268e48b527a7c62a509ad862b01e1eb4cf671ea5064218cfdc0'); expect(res).toHaveProperty('13extra'); expect(Number(res['13extra'].available)).toBeGreaterThan(0); }); it('should get correct quote', async () => { const res = await client.getQuote(Sui.address, nUSDC.address, 1e9, ''); console.log(res); expect(Number(res.amount_in)).toBeGreaterThan(0); expect(Number(res.amount_out)).toBeGreaterThan(0); expect(res.routes.length).toBeGreaterThan(0); }); it('should build swap ptb from quote without fee', async () => { const txb = new Transaction(); const from = "0x2::sui::SUI"; const to = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC" const amount = 1e9; const fromCoin = txb.splitCoins(txb.gas, [amount]); swapPTB(account.address, txb, from, to, fromCoin, amount, 0, "" ).then(coinB => { txb.transferObjects([coinB], account.address); account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address, }).then(result => { if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); // txb.build({ // client: account.client // }).then(dryRunTxBytes => { // account.client.dryRunTransactionBlock({ transactionBlock: dryRunTxBytes }).then(res => { // console.log(res); // }); // }); }); }); it('should build swap ptb from quote with fee', async () => { const txb = new Transaction(); const from = "0x2::sui::SUI"; const to = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC" const amount = 1e9; const fromCoin = txb.splitCoins(txb.gas, [amount]); swapPTB(account.address, txb, from, to, fromCoin, amount, 0, "", { feeOption: { fee: 0.1, //10% of fee receiverAddress: account.address } } ).then(coinB => { txb.transferObjects([coinB], account.address); account.client.devInspectTransactionBlock({ transactionBlock: txb, sender: account.address, }).then(result => { if (result.effects.status.status === "failure") { throw new Error(result.effects.status.error) } expect(result.effects.status.status).toEqual("success"); }); }); }); }); describe('test Migration', async () => { const client = new NAVISDKClient({ networkType: rpcUrl, mnemonic: mnemonic }); const account = client.accounts[0]; it('should migrate supply from USDC to Sui', async () => { const txb = new Transaction(); txb.setSender(account.address); const amount = 1e6; await migrateSupplyPTB(txb, nUSDC, Sui, amount, account.address, { apiKey: apiKey }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); it('should migrate supply from Sui to USDC', async () => { const txb = new Transaction(); txb.setSender(account.address); const amount = 1e6; await migrateSupplyPTB(txb, Sui, nUSDC, amount, account.address, { apiKey: apiKey }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); it('should migrate borrow from Sui to USDC', async () => { const txb = new Transaction(); txb.setSender(account.address); const amount = 1e6; await migrateBorrowPTB(txb, Sui, nUSDC, amount, account.address, { apiKey: apiKey }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); it('should migrate repay from USDC to Sui', async () => { const txb = new Transaction(); txb.setSender(account.address); const amount = 1e6; await migrateBorrowPTB(txb, nUSDC, Sui, amount, account.address, { apiKey: apiKey }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); it('should throw error if fromCoin and toCoin are the same', async () => { const txb = new Transaction(); txb.setSender(account.address); const amount = 1e6; try { await migrateSupplyPTB(txb, nUSDC, nUSDC, amount, account.address, { apiKey: apiKey }); } catch (error) { console.log("Caught error:", error.message); expect(error.message).toBe("fromCoin and toCoin cannot be the same"); } }); it('should throw error if amount is 0', async () => { const txb = new Transaction(); txb.setSender(account.address); const amount = 0; try { await migrateSupplyPTB(txb, nUSDC, Sui, amount, account.address, { apiKey: apiKey }); } catch (error) { console.log("Caught error:", error.message); expect(error.message).toBe("amount must be greater than 0"); } }); it('should able to migrate at big amount of flashloan', async () => { const txb = new Transaction(); const sender = '0x03fbfd4e4ee3ca0ca58612a4919257270c035164db21d8c2cfc042ce93ee9065'; txb.setSender(sender); const amount = 300000e6; await migrateSupplyPTB(txb, wUSDC, Sui, amount, sender, { apiKey: apiKey }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); it('should throw error if unsupported coin', async () => { const txb = new Transaction(); const sender = '0x03fbfd4e4ee3ca0ca58612a4919257270c035164db21d8c2cfc042ce93ee9065'; txb.setSender(sender); const amount = 300000e9; try { await migrateSupplyPTB(txb, haSui, Sui, amount, account.address, { apiKey: apiKey }); } catch (error) { console.log("Caught error:", error.message); expect(error.message).toBe("Unsupported coin"); } }); it('should work well without apikey', async () => { const txb = new Transaction(); const sender = '0x03fbfd4e4ee3ca0ca58612a4919257270c035164db21d8c2cfc042ce93ee9065'; txb.setSender(sender); const amount = 100e6; await migrateSupplyPTB(txb, wUSDC, Sui, amount, account.address, { apiKey: apiKey }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); it('should work well with custom baseUrl', async () => { const txb = new Transaction(); const sender = '0x03fbfd4e4ee3ca0ca58612a4919257270c035164db21d8c2cfc042ce93ee9065'; txb.setSender(sender); const amount = 100e6; await migrateSupplyPTB(txb, wUSDC, Sui, amount, account.address, { apiKey: apiKey, baseUrl: 'https://aggregator-api.naviprotocol.io' }); const dryRunResult = await dryRunTXB(txb, account.client); expect(dryRunResult.effects.status.status).toEqual("success"); }); }); describe('show available reward', () => { it('should generate correct account', async () => { const client = new NAVISDKClient({ networkType: rpcUrl, mnemonic: mnemonic }); const availableReward = await client.getAddressAvailableRewards('') console.log(availableReward) }); }, 500000)