UNPKG

@indigo-labs/indigo-sdk

Version:

Indigo SDK for interacting with Indigo endpoints via lucid-evolution

893 lines (824 loc) 28.7 kB
import { applyParamsToScript, Assets, Constr, Credential, Data, fromText, LucidEvolution, OutRef, SpendingValidator, toText, TxBuilder, UTxO, validatorToAddress, validatorToScriptHash, } from '@lucid-evolution/lucid'; import { CdpParams, ScriptReferences, SystemParams, } from '../types/system-params'; import { IAssetHelpers } from '../helpers/asset-helpers'; import { PriceOracleContract } from './price-oracle'; import { CDPCreatorContract } from './cdp-creator'; import { CollectorContract } from './collector'; import { InterestOracleContract } from './interest-oracle'; import { GovContract } from './gov'; import { TreasuryContract } from './treasury'; import { addrDetails, getRandomElement, scriptRef } from '../helpers/lucid-utils'; import { AssetClass } from '../types/generic'; import { calculateFeeFromPercentage } from '../helpers/helpers'; import { CDP, CDPDatum, CDPFees } from '../types/indigo/cdp'; import { _cdpValidator } from '../scripts/cdp-validator'; export class CDPContract { static async openPosition( asset: string, collateralAmount: bigint, mintedAmount: bigint, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, cdpCreatorRef?: OutRef, collectorRef?: OutRef, ): Promise<TxBuilder> { const [pkh, skh] = await addrDetails(lucid); const now = Date.now(); const assetOut = await (assetRef ? IAssetHelpers.findIAssetByRef(assetRef, params, lucid) : IAssetHelpers.findIAssetByName(asset, params, lucid)); // Fail if delisted asset if ('getOnChainPrice' in assetOut.datum.price) return Promise.reject('Trying to open CDP against delisted asset'); const oracleAsset = assetOut.datum.price as AssetClass; const oracleOut = priceOracleRef ? (await lucid.utxosByOutRef([priceOracleRef]))[0] : await lucid.utxoByUnit( oracleAsset[0].unCurrencySymbol + fromText(oracleAsset[1].unTokenName), ); if (!oracleOut.datum) return Promise.reject('Price Oracle datum not found'); const oracleDatum = PriceOracleContract.decodePriceOracleDatum( oracleOut.datum, ); const interestOracleAsset = assetOut.datum.interestOracle; const interestOracleOut = interestOracleRef ? (await lucid.utxosByOutRef([interestOracleRef]))[0] : await lucid.utxoByUnit( interestOracleAsset[0].unCurrencySymbol + fromText(interestOracleAsset[1].unTokenName), ); if (!interestOracleOut.datum) return Promise.reject('Interest Oracle datum not found'); const interestOracleDatum = InterestOracleContract.decodeInterestOracleDatum(interestOracleOut.datum); const cdpCreatorOut = getRandomElement( cdpCreatorRef ? await lucid.utxosByOutRef([cdpCreatorRef]) : await lucid.utxosAtWithUnit( CDPCreatorContract.address(params.cdpCreatorParams, lucid), params.cdpCreatorParams.cdpCreatorNft[0].unCurrencySymbol + fromText(params.cdpCreatorParams.cdpCreatorNft[1].unTokenName), ), ); const cdpCreatorRedeemer = CDPCreatorContract.redeemer( pkh, mintedAmount, collateralAmount, BigInt(now), ); const cdpCreatorScriptRefUtxo = await CDPCreatorContract.scriptRef( params.scriptReferences, lucid, ); const cdpAddress = CDPContract.address(params.cdpParams, lucid, skh); const cdpToken = params.cdpParams.cdpAuthToken[0].unCurrencySymbol + fromText(params.cdpParams.cdpAuthToken[1].unTokenName); const cdpValue: Assets = { lovelace: collateralAmount, }; cdpValue[cdpToken] = 1n; const newSnapshot = InterestOracleContract.calculateUnitaryInterestSinceOracleLastUpdated( BigInt(now), interestOracleDatum, ) + interestOracleDatum.unitaryInterest; const cdpDatum = CDPContract.datum(pkh, asset, mintedAmount, { type: 'ActiveCDPInterestTracking', last_settled: BigInt(now), unitary_interest_snapshot: newSnapshot }); const assetToken = params.cdpParams.cdpAssetSymbol.unCurrencySymbol + fromText(asset); const cdpTokenMintValue: Assets = {}; cdpTokenMintValue[cdpToken] = 1n; const iassetTokenMintValue: Assets = {}; iassetTokenMintValue[assetToken] = BigInt(mintedAmount); const cdpAuthTokenScriptRefUtxo = await CDPContract.cdpAuthTokenRef( params.scriptReferences, lucid, ); const iAssetTokenScriptRefUtxo = await CDPContract.assetTokenRef( params.scriptReferences, lucid, ); const debtMintingFee = calculateFeeFromPercentage( BigInt(assetOut.datum.debtMintingFeePercentage.getOnChainInt), (mintedAmount * oracleDatum.price) / 1_000_000n, ); // Oracle timestamp - 20s (length of a slot) const cappedValidateTo = oracleDatum.expiration - 20_001n; const timeValidFrom = now - 1_000; const timeValidTo_ = now + params.cdpCreatorParams.biasTime - 1_000; const timeValidTo = cappedValidateTo <= timeValidFrom ? timeValidTo_ : Math.min(timeValidTo_, Number(cappedValidateTo)); const tx = lucid .newTx() .collectFrom([cdpCreatorOut], Data.to(cdpCreatorRedeemer)) .readFrom([cdpCreatorScriptRefUtxo]) .pay.ToContract( cdpAddress, { kind: 'inline', value: Data.to(cdpDatum) }, cdpValue, ) .pay.ToContract( cdpCreatorOut.address, { kind: 'inline', value: cdpCreatorOut.datum }, cdpCreatorOut.assets, ) .readFrom([oracleOut, interestOracleOut, assetOut.utxo]) .mintAssets(cdpTokenMintValue, Data.to(new Constr(0, []))) .readFrom([cdpAuthTokenScriptRefUtxo]) .mintAssets(iassetTokenMintValue, Data.to(new Constr(0, []))) .readFrom([iAssetTokenScriptRefUtxo]) .addSignerKey(pkh.hash) .validFrom(Number(now - 60_000)) .validTo(Number(timeValidTo)); if (debtMintingFee > 0) { await CollectorContract.feeTx( debtMintingFee, lucid, params, tx, collectorRef, ); } return tx; } static async deposit( cdpRef: OutRef, amount: bigint, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, collectorRef?: OutRef, govRef?: OutRef, treasuryRef?: OutRef, ): Promise<TxBuilder> { return CDPContract.adjust( cdpRef, amount, 0n, params, lucid, assetRef, priceOracleRef, interestOracleRef, collectorRef, govRef, treasuryRef, ); } static async withdraw( cdpRef: OutRef, amount: bigint, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, collectorRef?: OutRef, govRef?: OutRef, treasuryRef?: OutRef, ): Promise<TxBuilder> { return CDPContract.adjust( cdpRef, -amount, 0n, params, lucid, assetRef, priceOracleRef, interestOracleRef, collectorRef, govRef, treasuryRef, ); } static async mint( cdpRef: OutRef, amount: bigint, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, collectorRef?: OutRef, govRef?: OutRef, treasuryRef?: OutRef, ): Promise<TxBuilder> { return CDPContract.adjust( cdpRef, 0n, amount, params, lucid, assetRef, priceOracleRef, interestOracleRef, collectorRef, govRef, treasuryRef, ); } static async burn( cdpRef: OutRef, amount: bigint, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, collectorRef?: OutRef, govRef?: OutRef, treasuryRef?: OutRef, ): Promise<TxBuilder> { return CDPContract.adjust( cdpRef, 0n, -amount, params, lucid, assetRef, priceOracleRef, interestOracleRef, collectorRef, govRef, treasuryRef, ); } static async adjust( cdpRef: OutRef, collateralAmount: bigint, mintAmount: bigint, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, collectorRef?: OutRef, govRef?: OutRef, treasuryRef?: OutRef, ): Promise<TxBuilder> { // Find Pkh, Skh const [pkh, skh] = await addrDetails(lucid); const now = Date.now(); // Fail if no pkh if (!pkh) return Promise.reject( 'Unable to determine the pub key hash of the wallet', ); // Find Outputs: iAsset Output, CDP Output, Gov Output const cdp = (await lucid.utxosByOutRef([cdpRef]))[0]; if (!cdp.datum) throw 'Unable to find CDP Datum'; const cdpDatum = CDPContract.decodeCdpDatum(cdp.datum); if (cdpDatum.type !== 'CDP') throw 'Invalid CDP Datum'; const iAsset = await (assetRef ? IAssetHelpers.findIAssetByRef(assetRef, params, lucid) : IAssetHelpers.findIAssetByName(cdpDatum.asset, params, lucid)); const gov = govRef ? (await lucid.utxosByOutRef([govRef]))[0] : await lucid.utxoByUnit( params.govParams.govNFT[0].unCurrencySymbol + fromText(params.govParams.govNFT[1].unTokenName), ); // const [iAsset, cdp, gov] = await lucid.utxosByOutRef([ // dIAssetTokenRef, // dCDPTokenRef, // dGovTokenRef, // ]); if (!gov.datum) throw 'Unable to find Gov Datum'; const govData = GovContract.decodeGovDatum(gov.datum); if (!govData) throw 'No Governance datum found'; const cdpScriptRefUtxo = await CDPContract.scriptRef( params.scriptReferences, lucid, ); const cdpAssets = Object.assign({}, cdp.assets); cdpAssets['lovelace'] = cdp.assets['lovelace'] + collateralAmount; const interestOracleAsset = iAsset.datum.interestOracle; const interestOracleOut = interestOracleRef ? (await lucid.utxosByOutRef([interestOracleRef]))[0] : await lucid.utxoByUnit( interestOracleAsset[0].unCurrencySymbol + fromText(interestOracleAsset[1].unTokenName), ); if (!interestOracleOut.datum) return Promise.reject('Interest Oracle datum not found'); const interestOracleDatum = InterestOracleContract.decodeInterestOracleDatum(interestOracleOut.datum); let tx = lucid .newTx() .collectFrom([cdp], Data.to(new Constr(0, [BigInt(now), mintAmount, collateralAmount]))) .readFrom([iAsset.utxo, gov, cdpScriptRefUtxo]) .addSignerKey(pkh.hash); if (!cdp.datum) throw 'Unable to find CDP Datum'; let cdpD = CDPContract.decodeCdpDatum(cdp.datum); if (!cdpD || cdpD.type !== 'CDP') throw 'Invalid CDP Datum'; if (cdpD.fees.type !== 'ActiveCDPInterestTracking') throw 'Invalid CDP Fees'; const newSnapshot = InterestOracleContract.calculateUnitaryInterestSinceOracleLastUpdated( BigInt(now), interestOracleDatum, ) + interestOracleDatum.unitaryInterest; const cdpD_: CDP = { ...cdpD, mintedAmount: cdpD.mintedAmount + mintAmount, fees: { type: 'ActiveCDPInterestTracking', last_settled: BigInt(now), unitary_interest_snapshot: newSnapshot } }; tx.pay.ToContract( cdp.address, { kind: 'inline', value: CDPContract.encodeCdpDatum(cdpD_), }, cdpAssets, ); // Find Oracle Ref Input const oracleAsset = iAsset.datum.price as AssetClass; const oracleRefInput = priceOracleRef ? (await lucid.utxosByOutRef([priceOracleRef]))[0] : await lucid.utxoByUnit( oracleAsset[0].unCurrencySymbol + fromText(oracleAsset[1].unTokenName), ); // Fail if delisted asset if (!oracleRefInput.datum) return Promise.reject('Invalid oracle input'); const od = PriceOracleContract.decodePriceOracleDatum(oracleRefInput.datum); if (!od) return Promise.reject('Invalid oracle input'); // TODO: Sanity check: oacle expiration // Oracle timestamp - 20s (length of a slot) // Oracle timestamp - 20s (length of a slot) const cappedValidateTo = od.expiration - 20_001n; const timeValidFrom = now - 1_000; const timeValidTo_ = now + params.cdpCreatorParams.biasTime - 1_000; const timeValidTo = cappedValidateTo <= timeValidFrom ? timeValidTo_ : Math.min(timeValidTo_, Number(cappedValidateTo)); tx .readFrom([oracleRefInput]) .validFrom(Number(timeValidFrom)) .validTo(Number(timeValidTo)); let fee = 0n; if (collateralAmount < 0) { fee += calculateFeeFromPercentage( govData.protocolParams.collateralFeePercentage, collateralAmount, ); } if (mintAmount > 0) { fee += calculateFeeFromPercentage( iAsset.datum.debtMintingFeePercentage.getOnChainInt, (mintAmount * od.price) / 1_000_000n, ); } // Interest payment const interestPaymentAsset = InterestOracleContract.calculateAccruedInterest( BigInt(now), cdpD.fees.unitary_interest_snapshot, cdpD.mintedAmount, cdpD.fees.last_settled, interestOracleDatum, ); const interestPayment = (interestPaymentAsset * od.price) / 1_000_000n; const interestCollectorPayment = calculateFeeFromPercentage( iAsset.datum.interestCollectorPortionPercentage.getOnChainInt, interestPayment, ); const interestTreasuryPayment = interestPayment - interestCollectorPayment; console.log(interestPayment, interestCollectorPayment, interestTreasuryPayment); if (interestTreasuryPayment > 0) { await TreasuryContract.feeTx(interestTreasuryPayment, lucid, params, tx, treasuryRef); } fee += interestCollectorPayment; tx.readFrom([interestOracleOut]); if (mintAmount !== 0n) { const iAssetTokenScriptRefUtxo = await CDPContract.assetTokenRef( params.scriptReferences, lucid, ); const iassetToken = params.cdpParams.cdpAssetSymbol.unCurrencySymbol + fromText(cdpD.asset); const mintValue = {} as Assets; mintValue[iassetToken] = mintAmount; tx.readFrom([iAssetTokenScriptRefUtxo]).mintAssets( mintValue, Data.to(new Constr(0, [])), ); } if (fee > 0n) { await CollectorContract.feeTx(fee, lucid, params, tx, collectorRef) } return tx; } static async close( cdpRef: OutRef, params: SystemParams, lucid: LucidEvolution, assetRef?: OutRef, priceOracleRef?: OutRef, interestOracleRef?: OutRef, collectorRef?: OutRef, govRef?: OutRef, treasuryRef?: OutRef, ): Promise<TxBuilder> { // Find Pkh, Skh const [pkh, skh] = await addrDetails(lucid); const now = Date.now(); // Fail if no pkh if (!pkh) return Promise.reject( 'Unable to determine the pub key hash of the wallet', ); // Find Outputs: iAsset Output, CDP Output, Gov Output const cdp = (await lucid.utxosByOutRef([cdpRef]))[0]; if (!cdp.datum) throw 'Unable to find CDP Datum'; const cdpDatum = CDPContract.decodeCdpDatum(cdp.datum); if (cdpDatum.type !== 'CDP') throw 'Invalid CDP Datum'; const iAsset = await (assetRef ? IAssetHelpers.findIAssetByRef(assetRef, params, lucid) : IAssetHelpers.findIAssetByName(cdpDatum.asset, params, lucid)); const gov = govRef ? (await lucid.utxosByOutRef([govRef]))[0] : await lucid.utxoByUnit( params.govParams.govNFT[0].unCurrencySymbol + fromText(params.govParams.govNFT[1].unTokenName), ); if (!gov.datum) throw 'Unable to find Gov Datum'; const govData = GovContract.decodeGovDatum(gov.datum); if (!govData) throw 'No Governance datum found'; const cdpScriptRefUtxo = await CDPContract.scriptRef( params.scriptReferences, lucid, ); const interestOracleAsset = iAsset.datum.interestOracle; const interestOracleOut = interestOracleRef ? (await lucid.utxosByOutRef([interestOracleRef]))[0] : await lucid.utxoByUnit( interestOracleAsset[0].unCurrencySymbol + fromText(interestOracleAsset[1].unTokenName), ); if (!interestOracleOut.datum) return Promise.reject('Interest Oracle datum not found'); const interestOracleDatum = InterestOracleContract.decodeInterestOracleDatum(interestOracleOut.datum); let tx = lucid .newTx() .collectFrom([cdp], Data.to(new Constr(1, [BigInt(now)]))) .readFrom([iAsset.utxo, gov, cdpScriptRefUtxo]) .addSignerKey(pkh.hash); if (!cdp.datum) throw 'Unable to find CDP Datum'; let cdpD = CDPContract.decodeCdpDatum(cdp.datum); if (!cdpD || cdpD.type !== 'CDP') throw 'Invalid CDP Datum'; if (cdpD.fees.type !== 'ActiveCDPInterestTracking') throw 'Invalid CDP Fees'; // Find Oracle Ref Input const oracleAsset = iAsset.datum.price as AssetClass; const oracleRefInput = priceOracleRef ? (await lucid.utxosByOutRef([priceOracleRef]))[0] : await lucid.utxoByUnit( oracleAsset[0].unCurrencySymbol + fromText(oracleAsset[1].unTokenName), ); // Fail if delisted asset if (!oracleRefInput.datum) return Promise.reject('Invalid oracle input'); const od = PriceOracleContract.decodePriceOracleDatum(oracleRefInput.datum); if (!od) return Promise.reject('Invalid oracle input'); // TODO: Sanity check: oacle expiration // Oracle timestamp - 20s (length of a slot) // Oracle timestamp - 20s (length of a slot) const cappedValidateTo = od.expiration - 20_001n; const timeValidFrom = now - 1_000; const timeValidTo_ = now + params.cdpCreatorParams.biasTime - 1_000; const timeValidTo = cappedValidateTo <= timeValidFrom ? timeValidTo_ : Math.min(timeValidTo_, Number(cappedValidateTo)); tx .readFrom([oracleRefInput]) .validFrom(Number(timeValidFrom)) .validTo(Number(timeValidTo)); let fee = 0n; // Interest payment const interestPaymentAsset = InterestOracleContract.calculateAccruedInterest( BigInt(now), cdpD.fees.unitary_interest_snapshot, cdpD.mintedAmount, cdpD.fees.last_settled, interestOracleDatum, ); const interestPayment = (interestPaymentAsset * od.price) / 1_000_000n; const interestCollectorPayment = calculateFeeFromPercentage( iAsset.datum.interestCollectorPortionPercentage.getOnChainInt, interestPayment, ); const interestTreasuryPayment = interestPayment - interestCollectorPayment; console.log(interestPayment, interestCollectorPayment, interestTreasuryPayment); if (interestTreasuryPayment > 0) { await TreasuryContract.feeTx(interestTreasuryPayment, lucid, params, tx, treasuryRef); } fee += interestCollectorPayment; tx.readFrom([interestOracleOut]); const iAssetTokenScriptRefUtxo = await CDPContract.assetTokenRef( params.scriptReferences, lucid, ); const iassetToken = params.cdpParams.cdpAssetSymbol.unCurrencySymbol + fromText(cdpD.asset); const assetBurnValue = {} as Assets; assetBurnValue[iassetToken] = -BigInt(cdpD.mintedAmount); const cdpTokenBurnValue = {} as Assets; cdpTokenBurnValue[params.cdpParams.cdpAuthToken[0].unCurrencySymbol + fromText(params.cdpParams.cdpAuthToken[1].unTokenName)] = -1n; const cdpAuthTokenScriptRefUtxo = await CDPContract.cdpAuthTokenRef( params.scriptReferences, lucid, ); tx.readFrom([iAssetTokenScriptRefUtxo]).mintAssets( assetBurnValue, Data.to(new Constr(0, [])), ).readFrom([cdpAuthTokenScriptRefUtxo]).mintAssets( cdpTokenBurnValue, Data.to(new Constr(0, [])), ); if (fee > 0n) { await CollectorContract.feeTx(fee, lucid, params, tx, collectorRef) } return tx; } static decodeCdpDatum(datum: string): CDPDatum { const cdpDatum = Data.from(datum) as any; if (cdpDatum.index == 1 && cdpDatum.fields[0].index == 0) { const iasset = cdpDatum.fields[0].fields; return { type: 'IAsset', name: toText(iasset[0]), price: iasset[1].index === 0 ? { getOnChainInt: iasset[1].fields[0] } : [ { unCurrencySymbol: iasset[1].fields[0].fields[0].fields[0] }, { unTokenName: toText(iasset[1].fields[0].fields[0].fields[1]), }, ], interestOracle: [ { unCurrencySymbol: iasset[2].fields[0] }, { unTokenName: toText(iasset[2].fields[1]) }, ], redemptionRatioPercentage: { getOnChainInt: iasset[3].fields[0] }, maintenanceRatioPercentage: { getOnChainInt: iasset[4].fields[0] }, liquidationRatioPercentage: { getOnChainInt: iasset[5].fields[0] }, debtMintingFeePercentage: { getOnChainInt: iasset[6].fields[0] }, liquidationProcessingFeePercentage: { getOnChainInt: iasset[7].fields[0], }, stabilityPoolWithdrawalFeePercentage: { getOnChainInt: iasset[8].fields[0], }, redemptionReimbursementPercentage: { getOnChainInt: iasset[9].fields[0], }, redemptionProcessingFeePercentage: { getOnChainInt: iasset[10].fields[0], }, interestCollectorPortionPercentage: { getOnChainInt: iasset[11].fields[0], }, firstAsset: iasset[12].index === 1, nextAsset: iasset[13].index === 0 ? toText(iasset[13].fields[0]) : undefined, }; } else if (cdpDatum.index == 0 && cdpDatum.fields[0].index == 0) { const cdp = cdpDatum.fields[0].fields; return { type: 'CDP', owner: cdp[0].fields[0], asset: toText(cdp[1]), mintedAmount: cdp[2], fees: cdp[3].index === 0 ? { type: 'ActiveCDPInterestTracking', last_settled: cdp[3].fields[0], unitary_interest_snapshot: cdp[3].fields[1], } : { type: 'FrozenCDPAccumulatedFees', lovelaces_treasury: cdp[3].fields[0], lovelaces_indy_stakers: cdp[3].fields[1], }, }; } throw 'Invalid CDP Datum provided'; } static encodeCdpDatum(datum: CDPDatum): string { if (datum.type === 'CDP') { return Data.to( new Constr(0, [ new Constr(0, [ datum.owner ? new Constr(0, [datum.owner]) : new Constr(1, []), fromText(datum.asset), BigInt(datum.mintedAmount), datum.fees.type === 'ActiveCDPInterestTracking' ? new Constr(0, [ datum.fees.last_settled, datum.fees.unitary_interest_snapshot, ]) : new Constr(1, [ datum.fees.lovelaces_treasury, datum.fees.lovelaces_indy_stakers, ]), ]), ]), ); } else if (datum.type === 'IAsset') { return Data.to( new Constr(1, [ new Constr(0, [ fromText(datum.name), 'getOnChainInt' in datum.price ? new Constr(0, [ new Constr(0, [BigInt(datum.price.getOnChainInt)]), ]) : new Constr(1, [ new Constr(0, [ new Constr(0, [ datum.price[0].unCurrencySymbol, fromText(datum.price[1].unTokenName), ]), ]), ]), new Constr(0, [ datum.interestOracle[0].unCurrencySymbol, fromText(datum.interestOracle[1].unTokenName), ]), new Constr(0, [ BigInt(datum.redemptionRatioPercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.maintenanceRatioPercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.liquidationRatioPercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.debtMintingFeePercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.liquidationProcessingFeePercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.stabilityPoolWithdrawalFeePercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.redemptionReimbursementPercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.redemptionProcessingFeePercentage.getOnChainInt), ]), new Constr(0, [ BigInt(datum.interestCollectorPortionPercentage.getOnChainInt), ]), datum.firstAsset ? new Constr(1, []) : new Constr(0, []), datum.nextAsset ? new Constr(0, [fromText(datum.nextAsset)]) : new Constr(1, []), ]), ]), ); } throw 'Invalid CDP Datum provided'; } static datum( hash: Credential, asset: string, mintedAmount: bigint, fees: CDPFees, ): Constr<Data> { return new Constr(0, [ new Constr(0, [ new Constr(0, [hash.hash]), fromText(asset), BigInt(mintedAmount), fees.type === 'ActiveCDPInterestTracking' ? new Constr(0, [ BigInt(fees.last_settled), BigInt(fees.unitary_interest_snapshot), ]) : new Constr(0, [ BigInt(fees.lovelaces_treasury), BigInt(fees.lovelaces_indy_stakers), ]), ]), ]); } static validator(params: CdpParams): SpendingValidator { return { type: _cdpValidator.type, script: applyParamsToScript(_cdpValidator.cborHex, [ new Constr(0, [ new Constr(0, [ params.cdpAuthToken[0].unCurrencySymbol, fromText(params.cdpAuthToken[1].unTokenName), ]), params.cdpAssetSymbol.unCurrencySymbol, new Constr(0, [ params.iAssetAuthToken[0].unCurrencySymbol, fromText(params.iAssetAuthToken[1].unTokenName), ]), new Constr(0, [ params.stabilityPoolAuthToken[0].unCurrencySymbol, fromText(params.stabilityPoolAuthToken[1].unTokenName), ]), new Constr(0, [ params.versionRecordToken[0].unCurrencySymbol, fromText(params.versionRecordToken[1].unTokenName), ]), new Constr(0, [ params.upgradeToken[0].unCurrencySymbol, fromText(params.upgradeToken[1].unTokenName), ]), params.collectorValHash, params.spValHash, new Constr(0, [ params.govNFT[0].unCurrencySymbol, fromText(params.govNFT[1].unTokenName), ]), BigInt(params.minCollateralInLovelace), BigInt(params.partialRedemptionExtraFeeLovelace), BigInt(params.biasTime), params.treasuryValHash, ]), ]), }; } static validatorHash(params: CdpParams): string { return validatorToScriptHash(CDPContract.validator(params)); } static address( cdpParams: CdpParams, lucid: LucidEvolution, skh?: Credential, ) { const network = lucid.config().network; if (!network) { throw new Error('Network configuration is undefined'); } return validatorToAddress(network, CDPContract.validator(cdpParams), skh); } static scriptRef( params: ScriptReferences, lucid: LucidEvolution, ): Promise<UTxO> { return scriptRef(params.cdpValidatorRef, lucid); } static cdpAuthTokenRef( params: ScriptReferences, lucid: LucidEvolution, ): Promise<UTxO> { return scriptRef(params.authTokenPolicies.cdpAuthTokenRef, lucid); } static assetTokenRef( params: ScriptReferences, lucid: LucidEvolution, ): Promise<UTxO> { return scriptRef(params.iAssetTokenPolicyRef, lucid); } static assetAuthTokenRef( params: ScriptReferences, lucid: LucidEvolution, ): Promise<UTxO> { return scriptRef(params.authTokenPolicies.iAssetTokenRef, lucid); } }