UNPKG

@indigo-labs/indigo-sdk

Version:

Indigo SDK for interacting with Indigo endpoints via lucid-evolution

441 lines (410 loc) 12 kB
import { Credential, fromText, LucidEvolution, ScriptHash, toHex, toText, UTxO, } from '@lucid-evolution/lucid'; import { addrDetails, adjustPriceToDecimals, cdpCollateralRatioPercentage, createScriptAddress, fromSystemParamsAsset, matchSingle, SystemParams, } from '../../src'; import { option as O, array as A, function as F } from 'fp-ts'; import { CDPContent, parseCdpDatum, parseStableswapPoolDatum, StableswapPoolContent, } from '../../src/contracts/cdp/types-new'; import { match, P } from 'ts-pattern'; import { AssetClass, assetClassToUnit, getInlineDatumOrThrow, getRandomElement, } from '@3rd-eye-labs/cardano-offchain-common'; import { InterestOracleDatum, parseInterestOracleDatum, } from '../../src/contracts/interest-oracle/types-new'; import { findCollateralAsset, findIAsset } from '../queries/iasset-queries'; import { findStabilityPool } from '../queries/stability-pool-queries'; import { findPriceOracle } from '../price-oracle/price-oracle-queries'; import { findInterestOracle } from '../queries/interest-oracle-queries'; import { findRandomCollector } from '../queries/collector-queries'; import { findGov } from '../gov/governance-queries'; import { findRandomTreasuryUtxoWithOnlyAda } from '../treasury/treasury-queries'; import { parsePriceOracleDatum } from '../../src/contracts/price-oracle/types-new'; import { CollateralAssetOutput, IAssetOutput, } from '../../src/contracts/iasset/types'; import { findRandomNonAdminInterestCollector } from '../interest-collection/interest-collector-queries'; import { LucidContext } from '../test-helpers'; import { Rational } from '../../src/types/rational'; export async function findAllActiveCdps( lucid: LucidEvolution, sysParams: SystemParams, assetAscii: string, stakeCred?: Credential, ): Promise<{ utxo: UTxO; datum: CDPContent }[]> { const cdpUtxos = await lucid.utxosAtWithUnit( createScriptAddress( lucid.config().network!, sysParams.validatorHashes.cdpHash, stakeCred, ), assetClassToUnit(fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken)), ); return F.pipe( cdpUtxos.map((utxo) => F.pipe( O.fromNullable(utxo.datum), O.flatMap(parseCdpDatum), O.flatMap((datum) => { if (toHex(datum.iasset) === fromText(assetAscii) && datum.cdpOwner) { return O.some({ utxo, datum: datum }); } else { return O.none; } }), ), ), A.compact, ); } export async function findCdp( lucid: LucidEvolution, cdpScriptHash: ScriptHash, cdpNft: AssetClass, ownerPkh: string, stakeCred?: Credential, ): Promise<{ utxo: UTxO; datum: CDPContent }> { const cdpUtxos = await lucid.utxosAtWithUnit( createScriptAddress(lucid.config().network!, cdpScriptHash, stakeCred), assetClassToUnit(cdpNft), ); return matchSingle( F.pipe( cdpUtxos.map((utxo) => F.pipe( O.fromNullable(utxo.datum), O.flatMap(parseCdpDatum), O.flatMap((datum) => { if (datum.cdpOwner && toHex(datum.cdpOwner) === ownerPkh) { return O.some({ utxo, datum: datum }); } else { return O.none; } }), ), ), A.compact, ), (res) => new Error('Expected a single CDP UTXO.: ' + JSON.stringify(res)), ); } // TODO: use the new variant defined below. export async function findOwnCdp( lucid: LucidEvolution, cdpScriptHash: ScriptHash, cdpNft: AssetClass, ): Promise<{ utxo: UTxO; datum: CDPContent }> { const [pkh, skh] = await addrDetails(lucid); return findCdp(lucid, cdpScriptHash, cdpNft, pkh.hash, skh); } export async function findOwnCdpNew( lucid: LucidEvolution, sysParams: SystemParams, ): Promise<{ utxo: UTxO; datum: CDPContent }> { const [pkh, skh] = await addrDetails(lucid); return findCdp( lucid, sysParams.validatorHashes.cdpHash, fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken), pkh.hash, skh, ); } export async function findFrozenCDPs( lucid: LucidEvolution, cdpScriptHash: ScriptHash, cdpNft: AssetClass, assetAscii: string, ): Promise<{ utxo: UTxO; datum: CDPContent }[]> { const cdpUtxos = await lucid.utxosAtWithUnit( createScriptAddress(lucid.config().network!, cdpScriptHash), assetClassToUnit(cdpNft), ); return F.pipe( cdpUtxos.map((utxo) => F.pipe( O.fromNullable(utxo.datum), O.flatMap(parseCdpDatum), O.flatMap((datum) => { if ( datum.cdpOwner == null && toHex(datum.iasset) === fromText(assetAscii) ) { return O.some({ utxo, datum: datum }); } else { return O.none; } }), ), ), A.compact, ); } export async function findStableswapPool( lucid: LucidEvolution, cdpScriptHash: ScriptHash, cdpNft: AssetClass, iassetName: string, collateralAsset: AssetClass, ): Promise<{ utxo: UTxO; datum: StableswapPoolContent }> { const cdpUtxos = await lucid.utxosAtWithUnit( createScriptAddress(lucid.config().network!, cdpScriptHash), assetClassToUnit(cdpNft), ); return matchSingle( F.pipe( cdpUtxos.map((utxo) => F.pipe( O.fromNullable(utxo.datum), O.flatMap(parseStableswapPoolDatum), O.flatMap((datum) => { if ( toHex(datum.iasset) === iassetName && toHex(datum.collateralAsset.currencySymbol) === toHex(collateralAsset.currencySymbol) && toHex(datum.collateralAsset.tokenName) === toHex(collateralAsset.tokenName) ) { return O.some({ utxo, datum: datum }); } else { return O.none; } }), ), ), A.compact, ), (res) => new Error( 'Expected a single stableswap pool UTXO.: ' + JSON.stringify(res), ), ); } export async function findAllCdpCreators( lucid: LucidEvolution, cdpCreatorScriptHash: string, cdpCreatorNft: AssetClass, ): Promise<UTxO[]> { return lucid.utxosAtWithUnit( createScriptAddress(lucid.config().network!, cdpCreatorScriptHash), assetClassToUnit(cdpCreatorNft), ); } // TODO: replace by the new variant defined below export async function findRandomCdpCreator( lucid: LucidEvolution, cdpCreatorScriptHash: string, cdpCreatorNft: AssetClass, ): Promise<UTxO> { const cdpCreatorUtxos = await findAllCdpCreators( lucid, cdpCreatorScriptHash, cdpCreatorNft, ); return F.pipe( O.fromNullable(getRandomElement(cdpCreatorUtxos)), O.match(() => { throw new Error('Expected some cdp creator UTXOs.'); }, F.identity), ); } export async function findRandomCdpCreatorNew( context: LucidContext, sysParams: SystemParams, ): Promise<UTxO> { const cdpCreatorUtxos = await findAllCdpCreators( context.lucid, sysParams.validatorHashes.cdpCreatorHash, fromSystemParamsAsset(sysParams.cdpCreatorParams.cdpCreatorNft), ); return F.pipe( O.fromNullable(getRandomElement(cdpCreatorUtxos)), O.match(() => { throw new Error('Expected some cdp creator UTXOs.'); }, F.identity), ); } export function findPriceOracleFromCollateralAsset( lucid: LucidEvolution, collateralAsset: CollateralAssetOutput, ): Promise<UTxO | undefined> { return match(collateralAsset.datum.priceInfo) .with({ OracleNft: P.select() }, (oracleNft) => findPriceOracle(lucid, oracleNft), ) .with({ Delisted: P.any }, () => { throw new Error('Cannot find price oracle as iAsset is delisted'); }) .otherwise(() => Promise.resolve(undefined)); } export async function findAllNecessaryOrefs( lucid: LucidEvolution, sysParams: SystemParams, iasset: string, collateralAsset: AssetClass, ): Promise<{ stabilityPoolUtxo: UTxO; iasset: IAssetOutput; collateralAsset: CollateralAssetOutput; cdpCreatorUtxo: UTxO; interestOracleUtxo: UTxO; collectorUtxo: UTxO; interestCollectorUtxo: UTxO; govUtxo: UTxO; treasuryUtxo: UTxO; }> { const iassetOut = await findIAsset( lucid, sysParams.validatorHashes.iassetHash, fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken), iasset, ); const collateralAssetOut = await findCollateralAsset( lucid, sysParams, fromSystemParamsAsset(sysParams.cdpCreatorParams.collateralAssetAuthTk), iasset, collateralAsset, ); const stabilityPool = await findStabilityPool(lucid, sysParams, iasset); return { stabilityPoolUtxo: stabilityPool.utxo, iasset: iassetOut, collateralAsset: collateralAssetOut, cdpCreatorUtxo: await findRandomCdpCreator( lucid, sysParams.validatorHashes.cdpCreatorHash, fromSystemParamsAsset(sysParams.cdpCreatorParams.cdpCreatorNft), ), interestOracleUtxo: await findInterestOracle( lucid, collateralAssetOut.datum.interestOracleNft, ), collectorUtxo: await findRandomCollector( lucid, sysParams.validatorHashes.collectorHash, ), interestCollectorUtxo: await findRandomNonAdminInterestCollector( lucid, sysParams.validatorHashes.interestCollectionHash, fromSystemParamsAsset(sysParams.interestCollectionParams.multisigUtxoNft), ), govUtxo: ( await findGov( lucid, sysParams.validatorHashes.govHash, fromSystemParamsAsset(sysParams.govParams.govNFT), ) ).utxo, treasuryUtxo: await findRandomTreasuryUtxoWithOnlyAda(lucid, sysParams), }; } export async function findPrice( lucid: LucidEvolution, sysParams: SystemParams, asset: string, collateralAsset: AssetClass, ): Promise<Rational> { const orefs = await findAllNecessaryOrefs( lucid, sysParams, asset, collateralAsset, ); const priceIasset = await match(orefs.collateralAsset.datum.priceInfo) .with({ OracleNft: P.select() }, async (oracleNft) => { const priceOracleUtxo = matchSingle( await lucid.utxosByOutRef([await findPriceOracle(lucid, oracleNft)]), (_) => new Error('Expected a single price oracle UTXO'), ); return parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)) .price; }) .with({ Delisted: P.select() }, (price) => { return price.price; }) .with({ DeferredValidation: P.select() }, (_) => { throw new Error('Pyth price not implemented'); }) .exhaustive(); return priceIasset; } export async function findInterestDatum( lucid: LucidEvolution, sysParams: SystemParams, asset: string, collateralAsset: AssetClass, ): Promise<InterestOracleDatum> { const orefs = await findAllNecessaryOrefs( lucid, sysParams, asset, collateralAsset, ); const interestOracleUtxo = matchSingle( await lucid.utxosByOutRef([orefs.interestOracleUtxo]), (_) => new Error('Expected a single interest oracle UTXO'), ); return parseInterestOracleDatum(getInlineDatumOrThrow(interestOracleUtxo)); } export async function findCdpCR( lucid: LucidEvolution, sysParams: SystemParams, cdp: { utxo: UTxO; datum: CDPContent }, slot: number, ): Promise<number> { const iassetAscii = toText(toHex(cdp.datum.iasset)); const collateralAsset = await findCollateralAsset( lucid, sysParams, fromSystemParamsAsset(sysParams.cdpParams.collateralAssetAuthToken), iassetAscii, cdp.datum.collateralAsset, ); const price = await findPrice( lucid, sysParams, iassetAscii, cdp.datum.collateralAsset, ); const adjustedPrice = adjustPriceToDecimals( collateralAsset.datum.extraDecimals, price, ); return cdpCollateralRatioPercentage( slot, adjustedPrice, cdp.utxo, cdp.datum, await findInterestDatum( lucid, sysParams, iassetAscii, cdp.datum.collateralAsset, ), lucid.config().network!, ); }