UNPKG

@indigo-labs/indigo-sdk

Version:

Indigo SDK for interacting with Indigo endpoints via lucid-evolution

510 lines (467 loc) 13.3 kB
import { beforeEach, describe, test, expect } from 'vitest'; import { IndigoTestContext, runAndAwaitTx } from '../test-helpers'; import { EXAMPLE_TOKEN_1, EXAMPLE_TOKEN_2, EXAMPLE_TOKEN_3, MAINNET_PROTOCOL_PARAMETERS, } from '../indigo-test-helpers'; import { addAssets, Emulator, fromHex, fromText, generateEmulatorAccount, generateEmulatorAccountFromPrivateKey, Lucid, slotToUnixTime, toHex, } from '@lucid-evolution/lucid'; import { adaAssetClass, AssetClass, mkAssetsOf, mkLovelacesOf, } from '@3rd-eye-labs/cardano-offchain-common'; import { DEFAULT_INTEREST, DEFAULT_PRICE, ibtcInitialAssetCfgWithPyth, iethInitialAssetCfgWithPyth, iusdInitialAssetCfgWithPyth, mkBaseCollateralAsset, } from '../mock/assets-mock'; import { runFreezeCdp, runOpenCdp, runWithdrawCdp } from '../cdp/actions'; import { benchmarkAndAwaitTx } from '../utils/benchmark-utils'; import { addrDetails, MIN_ROB_COLLATERAL_AMT, openRob } from '../../src'; import { ParsedFeedPayload } from '@pythnetwork/pyth-lazer-sdk'; import { createPythMessage } from './helpers'; import { findSingleRob } from '../rob/rob-queries'; import { expectValue } from '../utils/asserts'; import { runRedeemRob } from '../rob/actions'; import { rationalFromInt } from '../../src/types/rational'; import { init } from '../endpoints/initialize'; const collateralAssetA: AssetClass = { currencySymbol: fromHex( // random generated 'cc072059ae741791b7b9c23d9baea6a0b0d764dec617ce7e027a8dea', ), tokenName: fromHex(fromText('A')), }; describe('Pyth > Indigo', () => { beforeEach<IndigoTestContext>(async (context: IndigoTestContext) => { context.users = { admin: generateEmulatorAccount( addAssets( mkLovelacesOf(100_000_000_000_000n), mkAssetsOf(EXAMPLE_TOKEN_1, 1_000_000_000_000_000n), mkAssetsOf(collateralAssetA, 100_000_000n), ), ), user: generateEmulatorAccount( addAssets( mkLovelacesOf(100_000_000_000_000n), mkAssetsOf(EXAMPLE_TOKEN_1, 1_000_000_000_000_000n), mkAssetsOf(EXAMPLE_TOKEN_2, 1_000_000_000_000_000n), mkAssetsOf(EXAMPLE_TOKEN_3, 1_000_000_000_000_000n), ), ), user2: generateEmulatorAccount({ lovelace: BigInt(100_000_000_000_000), }), withdrawalAccount: generateEmulatorAccountFromPrivateKey({}), }; context.emulator = new Emulator( [ context.users.admin, context.users.user, context.users.user2, context.users.withdrawalAccount, ], MAINNET_PROTOCOL_PARAMETERS, ); context.lucid = await Lucid(context.emulator, 'Custom'); context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase); const [systemParams, assetConfigs] = await init( context.lucid, [], context.emulator.slot, (pythStateNft: AssetClass) => [ iusdInitialAssetCfgWithPyth(pythStateNft, (pythStatePolicyId) => [ mkBaseCollateralAsset( adaAssetClass, DEFAULT_INTEREST, DEFAULT_PRICE, 0n, pythStatePolicyId, { tag: 'value', val: { priceFeedId: 1 }, }, ), ]), iethInitialAssetCfgWithPyth(pythStateNft, (pythStatePolicyId) => [ mkBaseCollateralAsset( collateralAssetA, DEFAULT_INTEREST, DEFAULT_PRICE, 0n, pythStatePolicyId, { tag: 'value', val: { priceFeedId: 2 }, }, ), ]), ibtcInitialAssetCfgWithPyth(pythStateNft, (pythStatePolicyId) => [ mkBaseCollateralAsset( adaAssetClass, DEFAULT_INTEREST, DEFAULT_PRICE, 0n, pythStatePolicyId, { tag: 'divide', val: [ { tag: 'value', val: { priceFeedId: 1 } }, { tag: 'value', val: { priceFeedId: 2 } }, ], }, ), ]), ], ); context.systemParams = systemParams; context.assetConfigs = assetConfigs; }); test('Open CDP - Direct Price Value (ada collateral)', async (context: IndigoTestContext) => { const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); const iUsdFeed: ParsedFeedPayload = { priceFeedId: 1, price: '1000000', exponent: -6, }; const message = await createPythMessage([iUsdFeed], currentTime * 1_000n); await benchmarkAndAwaitTx( 'Pyth - CDP - Direct Price Value (ADA collateral)', await runOpenCdp( context, context.systemParams, 'iUSD', adaAssetClass, 10_000_000n, 500_000n, toHex(message), ), context.lucid, context.emulator, ); }); test('Open CDP - Direct Price Value (non-ADA collateral)', async (context: IndigoTestContext) => { const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); const iEthFeed: ParsedFeedPayload = { priceFeedId: 2, price: '1000000', exponent: -6, feedUpdateTimestamp: Number(currentTime) * 1000, }; const message = await createPythMessage([iEthFeed], currentTime * 1_000n); await benchmarkAndAwaitTx( 'Pyth - CDP - Direct Price Value (non-ADA collateral)', await runOpenCdp( context, context.systemParams, 'iETH', collateralAssetA, 10_000_000n, 500_000n, toHex(message), ), context.lucid, context.emulator, ); }); test('Open CDP - Divide Price Value', async (context: IndigoTestContext) => { const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); const feed1: ParsedFeedPayload = { priceFeedId: 1, price: '1000000', exponent: -6, }; const feed2: ParsedFeedPayload = { priceFeedId: 2, price: '10', exponent: -1, }; const message = await createPythMessage( [feed1, feed2], currentTime * 1_000n, ); await benchmarkAndAwaitTx( 'Pyth - CDP - Divide Price Value', await runOpenCdp( context, context.systemParams, 'iBTC', adaAssetClass, 10_000_000n, 500_000n, toHex(message), ), context.lucid, context.emulator, ); }); test('Open CDP - Fails if missing feed price', async (context: IndigoTestContext) => { const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); // This feed doesn't include a price for feedId 2, which is used by the iBTC asset const feed1: ParsedFeedPayload = { priceFeedId: 1, price: '1000000', exponent: -6, }; const message = await createPythMessage([feed1], currentTime * 1_000n); await expect( runOpenCdp( context, context.systemParams, 'iBTC', adaAssetClass, 10_000_000n, 500_000n, toHex(message), ), ).rejects.toThrowError('Pyth payload not found for feed ID: 2'); }); test('Withdraw CDP - Direct Price Value', async (context: IndigoTestContext) => { const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); const iUsdFeed: ParsedFeedPayload = { priceFeedId: 1, price: '1000000', exponent: -6, feedUpdateTimestamp: Number(currentTime) * 1000, }; const message = await createPythMessage([iUsdFeed], currentTime * 1_000n); await runAndAwaitTx( context.lucid, runOpenCdp( context, context.systemParams, 'iUSD', adaAssetClass, 11_000_000n, 500_000n, toHex(message), ), ); await benchmarkAndAwaitTx( 'Pyth - CDP - Withdraw CDP - Direct Price Value', await runWithdrawCdp( context, context.systemParams, 'iUSD', adaAssetClass, 1_000_000n, toHex(message), ), context.lucid, context.emulator, ); }); test('Freeze CDP - Direct Price Value', async (context: IndigoTestContext) => { const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); { const iUsdFeed: ParsedFeedPayload = { priceFeedId: 1, price: '1000000', exponent: -6, }; const message = await createPythMessage([iUsdFeed], currentTime * 1_000n); await runAndAwaitTx( context.lucid, runOpenCdp( context, context.systemParams, 'iUSD', adaAssetClass, 11_000_000n, 500_000n, toHex(message), ), ); } { const iUsdFeed: ParsedFeedPayload = { priceFeedId: 1, price: '100000000', exponent: -6, }; const message = await createPythMessage([iUsdFeed], currentTime * 1_000n); const [pkh, skh] = await addrDetails(context.lucid); await benchmarkAndAwaitTx( 'Pyth - CDP - Freeze CDP - Direct Price Value', await runFreezeCdp( context, context.systemParams, 'iUSD', adaAssetClass, pkh.hash, skh, toHex(message), ), context.lucid, context.emulator, ); } }); test<IndigoTestContext>('ROB redemption on buy order', async (context: IndigoTestContext) => { const [ownPkh, _] = await addrDetails(context.lucid); const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); const iUsdFeed: ParsedFeedPayload = { priceFeedId: 1, price: '1000000', exponent: -6, feedUpdateTimestamp: Number(currentTime) * 1000, }; const message = await createPythMessage([iUsdFeed], currentTime * 1_000n); await runAndAwaitTx( context.lucid, runOpenCdp( context, context.systemParams, 'iUSD', adaAssetClass, 100_000_000n, 30_000_000n, toHex(message), ), ); const initialDeposit = 20_000_000n; await runAndAwaitTx( context.lucid, openRob( 'iUSD', initialDeposit, { BuyIAssetOrder: { collateralAsset: adaAssetClass, maxPrice: rationalFromInt(1n), }, }, context.lucid, context.systemParams, ), ); const robUtxo = await findSingleRob( context, context.systemParams, 'iUSD', ownPkh, ); const redemptionIAssetAmt = 7_500_000n; await benchmarkAndAwaitTx( 'Pyth - ROB - Redemption on buy order', await runRedeemRob( context, context.systemParams, [[robUtxo, redemptionIAssetAmt]], 'iUSD', adaAssetClass, context.emulator.slot, toHex(message), ), context.lucid, context.emulator, ); }); test<IndigoTestContext>('ROB redemption on sell order', async (context: IndigoTestContext) => { const [ownPkh, _] = await addrDetails(context.lucid); const currentTime = BigInt( slotToUnixTime(context.lucid.config().network!, context.emulator.slot), ); const iUsdFeed: ParsedFeedPayload = { priceFeedId: 1, price: '1250000', exponent: -6, feedUpdateTimestamp: Number(currentTime) * 1000, }; const message = await createPythMessage([iUsdFeed], currentTime * 1_000n); await runAndAwaitTx( context.lucid, runOpenCdp( context, context.systemParams, 'iUSD', adaAssetClass, 100_000_000n, 30_000_000n, toHex(message), ), ); const initialDeposit = 20_000_000n; await runAndAwaitTx( context.lucid, openRob( 'iUSD', initialDeposit, { SellIAssetOrder: { allowedCollateralAssets: [[adaAssetClass, rationalFromInt(1n)]], }, }, context.lucid, context.systemParams, ), ); const robUtxo = await findSingleRob( context, context.systemParams, 'iUSD', ownPkh, ); const iassetAc: AssetClass = { currencySymbol: fromHex( context.systemParams.robParams.iassetPolicyId.unCurrencySymbol, ), tokenName: fromHex(fromText('iUSD')), }; expectValue( robUtxo.utxo.assets, 'Wrong ROB value before redemption', ).toEqual( addAssets( mkLovelacesOf(MIN_ROB_COLLATERAL_AMT), mkAssetsOf(iassetAc, initialDeposit), ), ); const payoutCollateralAmt = 7_500_000n; await benchmarkAndAwaitTx( 'Pyth - ROB - Redemption on sell order', await runRedeemRob( context, context.systemParams, [[robUtxo, payoutCollateralAmt]], 'iUSD', adaAssetClass, context.emulator.slot, toHex(message), ), context.lucid, context.emulator, ); }); });