@indigo-labs/indigo-sdk
Version:
Indigo SDK for interacting with Indigo endpoints via lucid-evolution
352 lines (331 loc) • 9.99 kB
text/typescript
import { pipe } from 'fp-ts/lib/function';
import {
array as A,
option as O,
string as S,
ord as Ord,
function as F,
} from 'fp-ts';
import {
addAssets,
Assets,
LucidEvolution,
OutRef,
toHex,
toText,
UTxO,
} from '@lucid-evolution/lucid';
import { matchSingle } from '../../utils/utils';
import { getInlineDatumOrThrow } from '../../utils/lucid-utils';
import {
AddCollateralAsssetContent,
ProposeAssetContent,
TreasuryWithdrawal,
TreasuryWithdrawalItem,
} from './types-new';
import { AssetClass, mkAssetsOf } from '@3rd-eye-labs/cardano-offchain-common';
import {
CollateralAssetContent,
CollateralAssetOutput,
IAssetContent,
IAssetOutput,
parseCollateralAssetDatumOrThrow,
parseIAssetDatumOrThrow,
} from '../iasset/types';
import { getAssetClassComparisonStr, ParsedOutput } from '../../types/generic';
export function proposalDeposit(
baseDeposit: bigint,
activeProposals: bigint,
): bigint {
return baseDeposit * 2n ** activeProposals;
}
export function createValueFromWithdrawal(w: TreasuryWithdrawal): Assets {
return A.reduce<TreasuryWithdrawalItem, Assets>({}, (acc, item) =>
addAssets(
acc,
mkAssetsOf(
{
currencySymbol: item.currencySymbol,
tokenName: item.tokenName,
},
item.amount,
),
),
)([...w.value]);
}
/**
* A generic solution for iassets and collateral assets chain.
*/
async function findRelativeAssetForInsertion<
T extends Uint8Array<ArrayBufferLike> | AssetClass,
K,
>(
newAsset: T,
allIAssetOrefs: OutRef[],
lucid: LucidEvolution,
assetOrd: Ord.Ord<T>,
getComparisonAsset: (datum: K) => T,
parseDatum: (utxo: UTxO) => K,
): Promise<O.Option<ParsedOutput<K>>> {
const parsedUtxos = await Promise.all(
allIAssetOrefs.map(async (oref) => {
const utxo = matchSingle(
await lucid.utxosByOutRef([oref]),
(_) => new Error('Expected a single UTXO'),
);
const datum = parseDatum(utxo);
return { datum: datum, utxo: utxo };
}),
);
// The iasset just before the new token name based on assets ordering
return pipe(
parsedUtxos,
// Sort by the asset names
A.sort(
Ord.contramap<T, ParsedOutput<K>>((a) => getComparisonAsset(a.datum))(
assetOrd,
),
),
// split head and tail
A.foldLeft(
() => O.none,
(head, rest) =>
O.some<[ParsedOutput<K>, ParsedOutput<K>[]]>([head, rest]),
),
// find the preceding iasset for the new token name
O.flatMap(([firstIAsset, rest]) =>
O.some(
A.reduce<ParsedOutput<K>, ParsedOutput<K>>(
firstIAsset,
(acc, iasset) => {
const isGreater =
assetOrd.compare(getComparisonAsset(iasset.datum), newAsset) ===
-1;
// When the new asset is greater, stop searching for the reference.
return isGreater ? iasset : acc;
},
)(rest),
),
),
);
}
/**
* Find the IAsset that should be a preceding one for the new IAsset token name.
* In case there are no iassets, none should be returned.
*/
export async function findRelativeIAssetForInsertion(
newIAssetTokenName: Uint8Array<ArrayBufferLike>,
allIAssetOrefs: OutRef[],
lucid: LucidEvolution,
): Promise<O.Option<IAssetOutput>> {
return findRelativeAssetForInsertion(
newIAssetTokenName,
allIAssetOrefs,
lucid,
Ord.contramap<string, Uint8Array<ArrayBufferLike>>((x) => toText(toHex(x)))(
S.Ord,
),
(d) => d.assetName,
(utxo) => parseIAssetDatumOrThrow(getInlineDatumOrThrow(utxo)),
);
}
/**
* Find collateral asset that should be a preceding one for the new Collateral asset.
* In case there are no collateral assets, none should be returned.
*/
export async function findRelativeCollateralAssetForInsertion(
newCollateralAsset: AssetClass,
allCollateralAssetOrefs: OutRef[],
lucid: LucidEvolution,
): Promise<O.Option<CollateralAssetOutput>> {
return findRelativeAssetForInsertion(
newCollateralAsset,
allCollateralAssetOrefs,
lucid,
Ord.contramap<string, AssetClass>((x) => getAssetClassComparisonStr(x))(
S.Ord,
),
(d) => d.collateralAsset,
(utxo) => parseCollateralAssetDatumOrThrow(getInlineDatumOrThrow(utxo)),
);
}
/**
* A generic solution for iassets and collateral assets chain.
*/
async function findFirstAsset<
T extends Uint8Array<ArrayBufferLike> | AssetClass,
K,
>(
allIAssetOrefs: OutRef[],
lucid: LucidEvolution,
assetOrd: Ord.Ord<T>,
getComparisonAsset: (datum: K) => T,
parseDatum: (utxo: UTxO) => K,
): Promise<O.Option<ParsedOutput<K>>> {
const parsedUtxos = await Promise.all(
allIAssetOrefs.map(async (oref) => {
const utxo = matchSingle(
await lucid.utxosByOutRef([oref]),
(_) => new Error('Expected a single UTXO'),
);
const datum = parseDatum(utxo);
return { datum: datum, utxo: utxo };
}),
);
// The iasset just before the new token name based on assets ordering
return pipe(
parsedUtxos,
// Sort by the asset names
A.sort(
Ord.contramap<T, ParsedOutput<K>>((a) => getComparisonAsset(a.datum))(
assetOrd,
),
),
A.head,
);
}
/**
* Find the collateral asset that is first in the distributed list.
* In case there are no collateral assets, none should be returned.
*/
export async function findFirstCollateralAsset(
allCollateralAssetOrefs: OutRef[],
lucid: LucidEvolution,
): Promise<O.Option<CollateralAssetOutput>> {
return findFirstAsset(
allCollateralAssetOrefs,
lucid,
Ord.contramap<string, AssetClass>((x) => getAssetClassComparisonStr(x))(
S.Ord,
),
(d) => d.collateralAsset,
(utxo) => parseCollateralAssetDatumOrThrow(getInlineDatumOrThrow(utxo)),
);
}
type IAssetCreationDatumHelperReturnType = {
newIAsset: IAssetContent;
newReferencedIAsset: O.Option<IAssetContent>;
};
export function iassetCreationDatumHelper(
proposeAssetContent: ProposeAssetContent,
referencedIAsset: O.Option<IAssetContent>,
): IAssetCreationDatumHelperReturnType {
const newContent: IAssetContent = {
assetName: proposeAssetContent.asset,
collateralAssetsCount: 0n,
debtMintingFeeRatio: proposeAssetContent.debtMintingFeeRatio,
liquidationProcessingFeeRatio:
proposeAssetContent.liquidationProcessingFeeRatio,
stabilityPoolWithdrawalFeeRatio:
proposeAssetContent.stabilityPoolWithdrawalFeeRatio,
redemptionReimbursementRatio:
proposeAssetContent.redemptionReimbursementRatio,
redemptionProcessingFeeRatio:
proposeAssetContent.redemptionProcessingFeeRatio,
firstIAsset: true,
nextIAsset: null,
};
return F.pipe(
referencedIAsset,
O.match<IAssetContent, IAssetCreationDatumHelperReturnType>(
() => ({
newIAsset: newContent,
newReferencedIAsset: O.none,
}),
(referencedIA) => {
if (
toText(toHex(proposeAssetContent.asset)) <
toText(toHex(referencedIA.assetName))
) {
return {
newIAsset: {
...newContent,
firstIAsset: true,
nextIAsset: referencedIA.assetName,
},
newReferencedIAsset: O.some({
...referencedIA,
firstIAsset: false,
}),
};
} else {
return {
newIAsset: {
...newContent,
firstIAsset: false,
nextIAsset: referencedIA.nextIAsset,
},
newReferencedIAsset: O.some({
...referencedIA,
nextIAsset: proposeAssetContent.asset,
}),
};
}
},
),
);
}
type CollateralAssetCreationDatumHelperReturnType = {
newCollateralAsset: CollateralAssetContent;
newReferencedCollateralAsset: O.Option<CollateralAssetContent>;
};
export function collateralAssetCreationDatumHelper(
addAssetContent: AddCollateralAsssetContent,
referencedCollateralAsset: O.Option<CollateralAssetContent>,
): CollateralAssetCreationDatumHelperReturnType {
const newContent: CollateralAssetContent = {
iasset: addAssetContent.correspondingIAsset,
collateralAsset: addAssetContent.collateralAsset,
extraDecimals: addAssetContent.assetExtraDecimals,
priceInfo: addAssetContent.assetPriceInfo,
interestOracleNft: addAssetContent.interestOracleNft,
redemptionRatio: addAssetContent.redemptionRatio,
maintenanceRatio: addAssetContent.maintenanceRatio,
liquidationRatio: addAssetContent.liquidationRatio,
minCollateralAmt: addAssetContent.minCollateralAmt,
firstCollateralAsset: true,
nextCollateralAsset: null,
};
return F.pipe(
referencedCollateralAsset,
O.match<
CollateralAssetContent,
CollateralAssetCreationDatumHelperReturnType
>(
() => ({
newCollateralAsset: newContent,
newReferencedCollateralAsset: O.none,
}),
(referencedCA) => {
if (
getAssetClassComparisonStr(addAssetContent.collateralAsset) <
getAssetClassComparisonStr(referencedCA.collateralAsset)
) {
return {
newCollateralAsset: {
...newContent,
firstCollateralAsset: true,
nextCollateralAsset: referencedCA.collateralAsset,
},
newReferencedCollateralAsset: O.some({
...referencedCA,
firstCollateralAsset: false,
}),
};
} else {
return {
newCollateralAsset: {
...newContent,
firstCollateralAsset: false,
nextCollateralAsset: referencedCA.nextCollateralAsset,
},
newReferencedCollateralAsset: O.some({
...referencedCA,
nextCollateralAsset: addAssetContent.collateralAsset,
}),
};
}
},
),
);
}