@indigo-labs/indigo-sdk
Version:
Indigo SDK for interacting with Indigo endpoints via lucid-evolution
335 lines (302 loc) • 9.23 kB
text/typescript
import {
addAssets,
Assets,
Data,
fromHex,
LucidEvolution,
OutRef,
TxBuilder,
UTxO,
} from '@lucid-evolution/lucid';
import {
fromSystemParamsScriptRef,
SystemParams,
} from '../../types/system-params';
import { matchSingle } from '../../utils/utils';
import {
adaAssetClass,
AssetClass,
estimateUtxoMinLovelace,
mkAssetsOf,
mkLovelacesOf,
negateAssets,
} from '@3rd-eye-labs/cardano-offchain-common';
import { serialiseTreasuryDatum, serialiseTreasuryRedeemer } from './types-new';
import { array as A, function as F } from 'fp-ts';
import { parseExecuteDatumOrThrow } from '../execute/types-new';
import { getInlineDatumOrThrow } from '../../utils/lucid-utils';
import { createValueFromWithdrawal } from '../gov/helpers';
import { mkTreasuryAddr } from './helpers';
export async function treasuryFeeTx(
assetToPay: AssetClass,
amountToPay: bigint,
extraLovelaces: bigint,
lucid: LucidEvolution,
sysParams: SystemParams,
tx: TxBuilder,
actionOref: OutRef,
/**
* `undefined` in case using direct treasury payment.
*/
treasuryOref: OutRef | undefined,
): Promise<UTxO | null> {
const [asset, amt, extraLov] = ((): [AssetClass, bigint, bigint] => {
if (amountToPay <= 0 && extraLovelaces > 0) {
return [adaAssetClass, extraLovelaces, 0n];
}
return [assetToPay, amountToPay, extraLovelaces];
})();
// If no fee is to be collected, do not include treasury collection.
if (amt <= 0n) return null;
let treasuryRefScriptUtxo = null;
if (treasuryOref !== undefined) {
const treasuryUtxo = matchSingle(
await lucid.utxosByOutRef([treasuryOref]),
(_) => new Error('Expected a single treasury UTXO'),
);
treasuryRefScriptUtxo = matchSingle(
await lucid.utxosByOutRef([
fromSystemParamsScriptRef(
sysParams.scriptReferences.treasuryValidatorRef,
),
]),
(_) => new Error('Expected a single treasury Ref Script UTXO'),
);
tx.readFrom([treasuryRefScriptUtxo])
.collectFrom(
[treasuryUtxo],
serialiseTreasuryRedeemer({
Collect: {
assetToCollect: asset,
amountToCollect: amt,
extraLovelaces: extraLov,
actionInputOref: {
txHash: fromHex(actionOref.txHash),
outputIndex: BigInt(actionOref.outputIndex),
},
},
}),
)
.pay.ToContract(
mkTreasuryAddr(lucid, sysParams),
{
kind: 'inline',
value: serialiseTreasuryDatum({
treasuryInputOref: {
txHash: fromHex(treasuryOref.txHash),
outputIndex: BigInt(treasuryOref.outputIndex),
},
actionInputOref: {
txHash: fromHex(actionOref.txHash),
outputIndex: BigInt(actionOref.outputIndex),
},
}),
},
addAssets(
treasuryUtxo.assets,
mkAssetsOf(asset, amt),
mkLovelacesOf(extraLov),
),
);
} else {
tx.pay.ToContract(
mkTreasuryAddr(lucid, sysParams),
{
kind: 'inline',
value: serialiseTreasuryDatum({
treasuryInputOref: null,
actionInputOref: {
txHash: fromHex(actionOref.txHash),
outputIndex: BigInt(actionOref.outputIndex),
},
}),
},
addAssets(mkAssetsOf(asset, amt), mkLovelacesOf(extraLov)),
);
}
return treasuryRefScriptUtxo;
}
export async function treasuryCollect(
assetToPay: AssetClass,
amountToPay: bigint,
extraLovelaces: bigint,
lucid: LucidEvolution,
sysParams: SystemParams,
actionOref: OutRef,
treasuryOref: OutRef,
): Promise<TxBuilder> {
const treasuryUtxo = matchSingle(
await lucid.utxosByOutRef([treasuryOref]),
(_) => new Error('Expected a single treasury UTXO'),
);
const treasuryRefScriptUtxo = matchSingle(
await lucid.utxosByOutRef([
fromSystemParamsScriptRef(
sysParams.scriptReferences.treasuryValidatorRef,
),
]),
(_) => new Error('Expected a single treasury Ref Script UTXO'),
);
return lucid
.newTx()
.readFrom([treasuryRefScriptUtxo])
.collectFrom(
[treasuryUtxo],
serialiseTreasuryRedeemer({
Collect: {
assetToCollect: assetToPay,
amountToCollect: amountToPay,
extraLovelaces: extraLovelaces,
actionInputOref: {
txHash: fromHex(actionOref.txHash),
outputIndex: BigInt(actionOref.outputIndex),
},
},
}),
)
.pay.ToContract(
mkTreasuryAddr(lucid, sysParams),
{
kind: 'inline',
value: serialiseTreasuryDatum({
treasuryInputOref: {
txHash: fromHex(treasuryOref.txHash),
outputIndex: BigInt(treasuryOref.outputIndex),
},
actionInputOref: {
txHash: fromHex(actionOref.txHash),
outputIndex: BigInt(actionOref.outputIndex),
},
}),
},
addAssets(
treasuryUtxo.assets,
mkAssetsOf(assetToPay, amountToPay),
mkLovelacesOf(extraLovelaces),
),
);
}
export async function treasuryMerge(
treasuryOutRefs: OutRef[],
lucid: LucidEvolution,
sysParams: SystemParams,
): Promise<TxBuilder> {
const treasuryScriptRefUtxo = matchSingle(
await lucid.utxosByOutRef([
fromSystemParamsScriptRef(
sysParams.scriptReferences.treasuryValidatorRef,
),
]),
(_) => new Error('Expected a single treasury Ref Script UTXO'),
);
const treasuryUtxos = await lucid.utxosByOutRef(treasuryOutRefs);
const totalAssets = F.pipe(
treasuryUtxos,
A.reduce<UTxO, Assets>({}, (acc, utxo) => addAssets(acc, utxo.assets)),
);
return lucid
.newTx()
.readFrom([treasuryScriptRefUtxo])
.collectFrom(treasuryUtxos, serialiseTreasuryRedeemer('Merge'))
.pay.ToContract(
mkTreasuryAddr(lucid, sysParams),
{ kind: 'inline', value: Data.void() },
totalAssets,
);
}
export async function treasurySplit(
treasuryOutRef: OutRef,
lucid: LucidEvolution,
sysParams: SystemParams,
): Promise<TxBuilder> {
const treasuryScriptRefUtxo = matchSingle(
await lucid.utxosByOutRef([
fromSystemParamsScriptRef(
sysParams.scriptReferences.treasuryValidatorRef,
),
]),
(_) => new Error('Expected a single treasury Ref Script UTXO'),
);
const treasuryUtxo = matchSingle(
await lucid.utxosByOutRef([treasuryOutRef]),
(_) => new Error('Expected a single treasury UTXO'),
);
const [adaAsset, ...nonAdaAssets] = Object.keys(treasuryUtxo.assets);
const tx = lucid
.newTx()
.collectFrom([treasuryUtxo], serialiseTreasuryRedeemer('Split'))
.readFrom([treasuryScriptRefUtxo]);
let paidLovelaces = 0n;
for (const asset of A.reverse(nonAdaAssets.sort())) {
const utxoMinLovelace = estimateUtxoMinLovelace(
lucid.config().protocolParameters!,
mkTreasuryAddr(lucid, sysParams),
{ [asset]: treasuryUtxo.assets[asset] },
{ InlineDatum: { datum: Data.void() } },
);
paidLovelaces += utxoMinLovelace;
tx.pay.ToContract(
mkTreasuryAddr(lucid, sysParams),
{ kind: 'inline', value: Data.void() },
addAssets(
{ [asset]: treasuryUtxo.assets[asset] },
mkLovelacesOf(utxoMinLovelace),
),
);
}
tx.pay.ToContract(
mkTreasuryAddr(lucid, sysParams),
{ kind: 'inline', value: Data.void() },
{ [adaAsset]: treasuryUtxo.assets[adaAsset] - paidLovelaces },
);
return tx;
}
export async function treasuryPrepareWithdrawal(
treasuryOutRefs: OutRef[],
upgradeOutRef: OutRef,
lucid: LucidEvolution,
sysParams: SystemParams,
): Promise<TxBuilder> {
const treasuryScriptRefUtxo = matchSingle(
await lucid.utxosByOutRef([
fromSystemParamsScriptRef(
sysParams.scriptReferences.treasuryValidatorRef,
),
]),
(_) => new Error('Expected a single treasury Ref Script UTXO'),
);
const treasuryUtxos = await lucid.utxosByOutRef(treasuryOutRefs);
const upgradeUtxo = matchSingle(
await lucid.utxosByOutRef([upgradeOutRef]),
(_) => new Error('Expected a single upgrade UTXO'),
);
const treasuryAddress = mkTreasuryAddr(lucid, sysParams);
const totalAssets = F.pipe(
treasuryUtxos,
A.reduce<UTxO, Assets>({}, (acc, utxo) => addAssets(acc, utxo.assets)),
);
const executeDatum = parseExecuteDatumOrThrow(
getInlineDatumOrThrow(upgradeUtxo),
);
if (!executeDatum.treasuryWithdrawal)
throw new Error('Expected a treasury withdrawal in the execute datum');
const withdrawalVal = createValueFromWithdrawal(
executeDatum.treasuryWithdrawal,
);
const change = addAssets(totalAssets, negateAssets(withdrawalVal));
const tx = lucid
.newTx()
.collectFrom(treasuryUtxos, serialiseTreasuryRedeemer('PrepareWithdraw'))
.readFrom([treasuryScriptRefUtxo, upgradeUtxo])
.pay.ToContract(
treasuryAddress,
{ kind: 'inline', value: Data.void() },
withdrawalVal,
)
.pay.ToContract(
treasuryAddress,
{ kind: 'inline', value: Data.void() },
change,
);
return tx;
}