@indigo-labs/indigo-sdk 0.1.28 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.github/workflows/ci.yml +8 -3
  2. package/dist/index.d.mts +1300 -671
  3. package/dist/index.d.ts +1300 -671
  4. package/dist/index.js +4663 -2220
  5. package/dist/index.mjs +4594 -2190
  6. package/eslint.config.mjs +7 -1
  7. package/package.json +9 -4
  8. package/src/contracts/cdp/helpers.ts +167 -0
  9. package/src/contracts/cdp/scripts.ts +33 -0
  10. package/src/contracts/cdp/transactions.ts +1310 -0
  11. package/src/contracts/cdp/types.ts +161 -0
  12. package/src/contracts/cdp-creator/scripts.ts +39 -0
  13. package/src/{types/indigo/cdp-creator.ts → contracts/cdp-creator/types.ts} +6 -4
  14. package/src/contracts/collector/scripts.ts +32 -0
  15. package/src/contracts/collector/transactions.ts +44 -0
  16. package/src/contracts/execute/scripts.ts +48 -0
  17. package/src/contracts/execute/types.ts +57 -0
  18. package/src/contracts/gov/helpers.ts +157 -0
  19. package/src/contracts/gov/scripts.ts +34 -0
  20. package/src/contracts/gov/transactions.ts +1224 -0
  21. package/src/contracts/gov/types-new.ts +115 -0
  22. package/src/contracts/gov/types.ts +89 -0
  23. package/src/{helpers/interest-oracle.ts → contracts/interest-oracle/helpers.ts} +37 -9
  24. package/src/contracts/interest-oracle/scripts.ts +18 -0
  25. package/src/contracts/interest-oracle/transactions.ts +149 -0
  26. package/src/{types/indigo/interest-oracle.ts → contracts/interest-oracle/types.ts} +1 -1
  27. package/src/contracts/lrp/scripts.ts +27 -0
  28. package/src/contracts/{lrp.ts → lrp/transactions.ts} +14 -14
  29. package/src/{types/indigo/lrp.ts → contracts/lrp/types.ts} +2 -2
  30. package/src/{scripts/one-shot-policy.ts → contracts/one-shot/scripts.ts} +1 -1
  31. package/src/contracts/{one-shot.ts → one-shot/transactions.ts} +3 -3
  32. package/src/contracts/poll/helpers.ts +55 -0
  33. package/src/contracts/poll/scripts.ts +72 -0
  34. package/src/contracts/poll/types-poll-manager.ts +38 -0
  35. package/src/contracts/poll/types-poll-shard.ts +38 -0
  36. package/src/contracts/poll/types-poll.ts +88 -0
  37. package/src/{scripts/price-oracle-validator.ts → contracts/price-oracle/scripts.ts} +1 -4
  38. package/src/contracts/price-oracle/transactions.ts +112 -0
  39. package/src/{types/indigo/price-oracle.ts → contracts/price-oracle/types.ts} +16 -4
  40. package/src/{helpers/stability-pool-helpers.ts → contracts/stability-pool/helpers.ts} +110 -6
  41. package/src/contracts/stability-pool/scripts.ts +46 -0
  42. package/src/contracts/stability-pool/transactions.ts +676 -0
  43. package/src/contracts/stability-pool/types-new.ts +208 -0
  44. package/src/contracts/stability-pool/types.ts +42 -0
  45. package/src/contracts/staking/helpers.ts +116 -0
  46. package/src/contracts/staking/scripts.ts +41 -0
  47. package/src/contracts/staking/transactions.ts +268 -0
  48. package/src/contracts/staking/types-new.ts +81 -0
  49. package/src/contracts/staking/types.ts +41 -0
  50. package/src/contracts/treasury/scripts.ts +37 -0
  51. package/src/contracts/treasury/transactions.ts +44 -0
  52. package/src/contracts/treasury/types.ts +55 -0
  53. package/src/contracts/version-registry/scripts.ts +29 -0
  54. package/src/contracts/version-registry/types-new.ts +19 -0
  55. package/src/{types/indigo/version-record.ts → contracts/version-registry/types.ts} +1 -1
  56. package/src/contracts/vesting/helpers.ts +267 -0
  57. package/src/index.ts +39 -33
  58. package/src/types/evolution-schema-options.ts +16 -0
  59. package/src/types/generic.ts +78 -60
  60. package/src/types/on-chain-decimal.ts +22 -0
  61. package/src/types/system-params.ts +22 -11
  62. package/src/utils/bigint-utils.ts +7 -0
  63. package/src/{helpers → utils}/helper-txs.ts +1 -0
  64. package/src/utils/time-helpers.ts +4 -0
  65. package/src/{helpers/helpers.ts → utils/utils.ts} +0 -10
  66. package/src/{helpers → utils}/value-helpers.ts +10 -0
  67. package/src/{scripts → validators}/cdp-creator-validator.ts +4 -50
  68. package/src/{scripts → validators}/cdp-validator.ts +3 -5
  69. package/src/{scripts → validators}/collector-validator.ts +2 -3
  70. package/src/{scripts/version-registry.ts → validators/execute-validator.ts} +3 -11
  71. package/src/{scripts/gov-validator.ts → validators/governance-validator.ts} +3 -40
  72. package/src/{scripts → validators}/interest-oracle-validator.ts +4 -20
  73. package/src/validators/lrp-validator.ts +7 -0
  74. package/src/validators/poll-manager-validator.ts +7 -0
  75. package/src/{scripts → validators}/poll-shard-validator.ts +3 -43
  76. package/src/{scripts → validators}/stability-pool-validator.ts +4 -57
  77. package/src/{scripts → validators}/staking-validator.ts +2 -3
  78. package/src/{scripts → validators}/treasury-validator.ts +2 -3
  79. package/src/{scripts → validators}/version-record-policy.ts +4 -23
  80. package/src/validators/version-registry-validator.ts +7 -0
  81. package/tests/cdp.test.ts +1565 -0
  82. package/tests/datums.test.ts +125 -108
  83. package/tests/endpoints/initialize.ts +240 -338
  84. package/tests/gov.test.ts +1874 -0
  85. package/tests/hash-checks.test.ts +26 -21
  86. package/tests/indigo-test-helpers.ts +1 -55
  87. package/tests/initialize.test.ts +10 -5
  88. package/tests/interest-calculations.test.ts +18 -18
  89. package/tests/interest-oracle.test.ts +20 -18
  90. package/tests/lrp.test.ts +191 -65
  91. package/tests/mock/assets-mock.ts +59 -0
  92. package/tests/queries/cdp-queries.ts +144 -0
  93. package/tests/queries/collector-queries.ts +26 -0
  94. package/tests/queries/execute-queries.ts +46 -0
  95. package/tests/queries/governance-queries.ts +19 -16
  96. package/tests/queries/iasset-queries.ts +46 -23
  97. package/tests/queries/interest-oracle-queries.ts +3 -6
  98. package/tests/queries/lrp-queries.ts +2 -2
  99. package/tests/queries/poll-queries.ts +97 -0
  100. package/tests/queries/price-oracle-queries.ts +5 -22
  101. package/tests/queries/stability-pool-queries.ts +10 -8
  102. package/tests/queries/staking-queries.ts +28 -19
  103. package/tests/queries/treasury-queries.ts +19 -0
  104. package/tests/stability-pool.test.ts +186 -71
  105. package/tests/staking.test.ts +30 -23
  106. package/tests/test-helpers.ts +11 -2
  107. package/tests/utils/asserts.ts +13 -0
  108. package/tests/utils/index.ts +50 -0
  109. package/tsconfig.json +3 -1
  110. package/vitest.config.ts +1 -1
  111. package/.github/workflows/test.yml +0 -44
  112. package/src/contracts/cdp.ts +0 -752
  113. package/src/contracts/collector.ts +0 -98
  114. package/src/contracts/gov.ts +0 -1
  115. package/src/contracts/interest-oracle.ts +0 -149
  116. package/src/contracts/stability-pool.ts +0 -690
  117. package/src/contracts/staking.ts +0 -348
  118. package/src/contracts/treasury.ts +0 -112
  119. package/src/helpers/asset-helpers.ts +0 -62
  120. package/src/helpers/staking-helpers.ts +0 -94
  121. package/src/helpers/time-helpers.ts +0 -4
  122. package/src/scripts/execute-validator.ts +0 -52
  123. package/src/scripts/lrp-validator.ts +0 -40
  124. package/src/scripts/poll-manager-validator.ts +0 -52
  125. package/src/types/indigo/cdp.ts +0 -88
  126. package/src/types/indigo/execute.ts +0 -21
  127. package/src/types/indigo/gov.ts +0 -51
  128. package/src/types/indigo/poll-manager.ts +0 -21
  129. package/src/types/indigo/poll-shard.ts +0 -16
  130. package/src/types/indigo/stability-pool.ts +0 -233
  131. package/src/types/indigo/staking.ts +0 -99
  132. /package/src/{types/one-shot.ts → contracts/one-shot/types.ts} +0 -0
  133. /package/src/{helpers/price-oracle-helpers.ts → contracts/price-oracle/helpers.ts} +0 -0
  134. /package/src/{helpers → utils}/indigo-helpers.ts +0 -0
  135. /package/src/{helpers → utils}/lucid-utils.ts +0 -0
@@ -0,0 +1,1310 @@
1
+ import {
2
+ addAssets,
3
+ Assets,
4
+ Data,
5
+ LucidEvolution,
6
+ OutRef,
7
+ slotToUnixTime,
8
+ TxBuilder,
9
+ UTxO,
10
+ } from '@lucid-evolution/lucid';
11
+ import {
12
+ fromSystemParamsAsset,
13
+ fromSystemParamsScriptRef,
14
+ SystemParams,
15
+ } from '../../types/system-params';
16
+ import {
17
+ addrDetails,
18
+ createScriptAddress,
19
+ getInlineDatumOrThrow,
20
+ } from '../../utils/lucid-utils';
21
+ import { matchSingle } from '../../utils/utils';
22
+ import {
23
+ CDPContent,
24
+ parseCdpDatumOrThrow,
25
+ parseIAssetDatumOrThrow,
26
+ serialiseCdpDatum,
27
+ serialiseCdpRedeemer,
28
+ } from './types';
29
+ import { parsePriceOracleDatum } from '../price-oracle/types';
30
+ import { parseInterestOracleDatum } from '../interest-oracle/types';
31
+ import { parseGovDatumOrThrow } from '../gov/types';
32
+ import {
33
+ calculateAccruedInterest,
34
+ calculateUnitaryInterestSinceOracleLastUpdated,
35
+ computeInterestLovelacesFor100PercentCR,
36
+ } from '../interest-oracle/helpers';
37
+ import { oracleExpirationAwareValidity } from '../price-oracle/helpers';
38
+ import { match, P } from 'ts-pattern';
39
+ import { serialiseCDPCreatorRedeemer } from '../cdp-creator/types';
40
+ import {
41
+ assetClassValueOf,
42
+ lovelacesAmt,
43
+ mkAssetsOf,
44
+ mkLovelacesOf,
45
+ } from '../../utils/value-helpers';
46
+ import { calculateMinCollateralCappedIAssetRedemptionAmt } from './helpers';
47
+ import { bigintMin } from '../../utils/bigint-utils';
48
+ import { ocdMul } from '../../types/on-chain-decimal';
49
+ import {
50
+ parseStabilityPoolDatum,
51
+ serialiseStabilityPoolDatum,
52
+ serialiseStabilityPoolRedeemer,
53
+ } from '../stability-pool/types-new';
54
+ import { liquidationHelper } from '../stability-pool/helpers';
55
+ import { array as A, function as F } from 'fp-ts';
56
+ import { calculateFeeFromPercentage } from '../../utils/indigo-helpers';
57
+ import { collectorFeeTx } from '../collector/transactions';
58
+ import { treasuryFeeTx } from '../treasury/transactions';
59
+
60
+ export async function openCdp(
61
+ collateralAmount: bigint,
62
+ mintedAmount: bigint,
63
+ sysParams: SystemParams,
64
+ cdpCreatorOref: OutRef,
65
+ iassetOref: OutRef,
66
+ priceOracleOref: OutRef,
67
+ interestOracleOref: OutRef,
68
+ collectorOref: OutRef,
69
+ lucid: LucidEvolution,
70
+ currentSlot: number,
71
+ ): Promise<TxBuilder> {
72
+ const network = lucid.config().network!;
73
+ const currentTime = BigInt(slotToUnixTime(network, currentSlot));
74
+
75
+ const [pkh, skh] = await addrDetails(lucid);
76
+
77
+ const cdpCreatorRefScriptUtxo = matchSingle(
78
+ await lucid.utxosByOutRef([
79
+ fromSystemParamsScriptRef(
80
+ sysParams.scriptReferences.cdpCreatorValidatorRef,
81
+ ),
82
+ ]),
83
+ (_) => new Error('Expected a single cdp creator Ref Script UTXO'),
84
+ );
85
+ const cdpAuthTokenPolicyRefScriptUtxo = matchSingle(
86
+ await lucid.utxosByOutRef([
87
+ fromSystemParamsScriptRef(
88
+ sysParams.scriptReferences.authTokenPolicies.cdpAuthTokenRef,
89
+ ),
90
+ ]),
91
+ (_) => new Error('Expected a single cdp auth token policy Ref Script UTXO'),
92
+ );
93
+ const iAssetTokenPolicyRefScriptUtxo = matchSingle(
94
+ await lucid.utxosByOutRef([
95
+ fromSystemParamsScriptRef(
96
+ sysParams.scriptReferences.iAssetTokenPolicyRef,
97
+ ),
98
+ ]),
99
+ (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
100
+ );
101
+
102
+ const iassetUtxo = matchSingle(
103
+ await lucid.utxosByOutRef([iassetOref]),
104
+ (_) => new Error('Expected a single iasset UTXO'),
105
+ );
106
+ const iassetDatum = parseIAssetDatumOrThrow(
107
+ getInlineDatumOrThrow(iassetUtxo),
108
+ );
109
+
110
+ const priceOracleUtxo = matchSingle(
111
+ await lucid.utxosByOutRef([priceOracleOref]),
112
+ (_) => new Error('Expected a single price oracle UTXO'),
113
+ );
114
+ const priceOracleDatum = parsePriceOracleDatum(
115
+ getInlineDatumOrThrow(priceOracleUtxo),
116
+ );
117
+
118
+ const interestOracleUtxo = matchSingle(
119
+ await lucid.utxosByOutRef([interestOracleOref]),
120
+ (_) => new Error('Expected a single interest oracle UTXO'),
121
+ );
122
+ const interestOracleDatum = parseInterestOracleDatum(
123
+ getInlineDatumOrThrow(interestOracleUtxo),
124
+ );
125
+
126
+ const cdpCreatorUtxo = matchSingle(
127
+ await lucid.utxosByOutRef([cdpCreatorOref]),
128
+ (_) => new Error('Expected a single CDP creator UTXO'),
129
+ );
130
+
131
+ match(iassetDatum.price)
132
+ .with({ Delisted: P.any }, () => {
133
+ throw new Error("Can't open CDP of delisted asset");
134
+ })
135
+ .otherwise(() => {});
136
+
137
+ const cdpNftVal = mkAssetsOf(
138
+ fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
139
+ 1n,
140
+ );
141
+
142
+ const iassetTokensVal = mkAssetsOf(
143
+ {
144
+ currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
145
+ tokenName: iassetDatum.assetName,
146
+ },
147
+ mintedAmount,
148
+ );
149
+
150
+ const txValidity = oracleExpirationAwareValidity(
151
+ currentSlot,
152
+ Number(sysParams.cdpCreatorParams.biasTime),
153
+ Number(priceOracleDatum.expiration),
154
+ network,
155
+ );
156
+
157
+ const tx = lucid
158
+ .newTx()
159
+ .validFrom(txValidity.validFrom)
160
+ .validTo(txValidity.validTo)
161
+ .readFrom([
162
+ cdpCreatorRefScriptUtxo,
163
+ cdpAuthTokenPolicyRefScriptUtxo,
164
+ iAssetTokenPolicyRefScriptUtxo,
165
+ ])
166
+ .mintAssets(cdpNftVal, Data.void())
167
+ .mintAssets(iassetTokensVal, Data.void())
168
+ .collectFrom(
169
+ [cdpCreatorUtxo],
170
+ serialiseCDPCreatorRedeemer({
171
+ CreateCDP: {
172
+ cdpOwner: pkh.hash,
173
+ minted: mintedAmount,
174
+ collateral: collateralAmount,
175
+ currentTime: currentTime,
176
+ },
177
+ }),
178
+ )
179
+ .pay.ToContract(
180
+ createScriptAddress(network, sysParams.validatorHashes.cdpHash, skh),
181
+ {
182
+ kind: 'inline',
183
+ value: serialiseCdpDatum({
184
+ cdpOwner: pkh.hash,
185
+ iasset: iassetDatum.assetName,
186
+ mintedAmt: mintedAmount,
187
+ cdpFees: {
188
+ ActiveCDPInterestTracking: {
189
+ lastSettled: currentTime,
190
+ unitaryInterestSnapshot:
191
+ calculateUnitaryInterestSinceOracleLastUpdated(
192
+ currentTime,
193
+ interestOracleDatum,
194
+ ) + interestOracleDatum.unitaryInterest,
195
+ },
196
+ },
197
+ }),
198
+ },
199
+ addAssets(cdpNftVal, mkLovelacesOf(collateralAmount)),
200
+ )
201
+ .pay.ToContract(
202
+ cdpCreatorUtxo.address,
203
+ { kind: 'inline', value: Data.void() },
204
+ cdpCreatorUtxo.assets,
205
+ )
206
+ .readFrom([priceOracleUtxo, interestOracleUtxo, iassetUtxo])
207
+ .addSignerKey(pkh.hash);
208
+
209
+ const debtMintingFee = calculateFeeFromPercentage(
210
+ iassetDatum.debtMintingFeePercentage,
211
+ (mintedAmount * priceOracleDatum.price.getOnChainInt) / 1_000_000n,
212
+ );
213
+
214
+ if (debtMintingFee > 0) {
215
+ await collectorFeeTx(debtMintingFee, lucid, sysParams, tx, collectorOref);
216
+ }
217
+
218
+ return tx;
219
+ }
220
+
221
+ async function adjustCdp(
222
+ collateralAmount: bigint,
223
+ mintAmount: bigint,
224
+ cdpOref: OutRef,
225
+ iassetOref: OutRef,
226
+ priceOracleOref: OutRef,
227
+ interestOracleOref: OutRef,
228
+ collectorOref: OutRef,
229
+ govOref: OutRef,
230
+ treasuryOref: OutRef,
231
+ sysParams: SystemParams,
232
+ lucid: LucidEvolution,
233
+ currentSlot: number,
234
+ ): Promise<TxBuilder> {
235
+ const network = lucid.config().network!;
236
+ const currentTime = BigInt(slotToUnixTime(network, currentSlot));
237
+
238
+ const cdpRefScriptUtxo = matchSingle(
239
+ await lucid.utxosByOutRef([
240
+ fromSystemParamsScriptRef(sysParams.scriptReferences.cdpValidatorRef),
241
+ ]),
242
+ (_) => new Error('Expected a single cdp Ref Script UTXO'),
243
+ );
244
+
245
+ const cdpUtxo = matchSingle(
246
+ await lucid.utxosByOutRef([cdpOref]),
247
+ (_) => new Error('Expected a single cdp UTXO'),
248
+ );
249
+ const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
250
+
251
+ const iassetUtxo = matchSingle(
252
+ await lucid.utxosByOutRef([iassetOref]),
253
+ (_) => new Error('Expected a single iasset UTXO'),
254
+ );
255
+ const iassetDatum = parseIAssetDatumOrThrow(
256
+ getInlineDatumOrThrow(iassetUtxo),
257
+ );
258
+
259
+ const govUtxo = matchSingle(
260
+ await lucid.utxosByOutRef([govOref]),
261
+ (_) => new Error('Expected a single gov UTXO'),
262
+ );
263
+ const govDatum = parseGovDatumOrThrow(getInlineDatumOrThrow(govUtxo));
264
+
265
+ const priceOracleUtxo = matchSingle(
266
+ await lucid.utxosByOutRef([priceOracleOref]),
267
+ (_) => new Error('Expected a single price oracle UTXO'),
268
+ );
269
+ const priceOracleDatum = parsePriceOracleDatum(
270
+ getInlineDatumOrThrow(priceOracleUtxo),
271
+ );
272
+
273
+ const interestOracleUtxo = matchSingle(
274
+ await lucid.utxosByOutRef([interestOracleOref]),
275
+ (_) => new Error('Expected a single interest oracle UTXO'),
276
+ );
277
+ const interestOracleDatum = parseInterestOracleDatum(
278
+ getInlineDatumOrThrow(interestOracleUtxo),
279
+ );
280
+
281
+ match(iassetDatum.price)
282
+ .with({ Delisted: P.any }, () => {
283
+ throw new Error("Can't adjust CDP of delisted asset");
284
+ })
285
+ .otherwise(() => {});
286
+
287
+ const txValidity = oracleExpirationAwareValidity(
288
+ currentSlot,
289
+ Number(sysParams.cdpCreatorParams.biasTime),
290
+ Number(priceOracleDatum.expiration),
291
+ network,
292
+ );
293
+
294
+ const tx = lucid
295
+ .newTx()
296
+ .validFrom(txValidity.validFrom)
297
+ .validTo(txValidity.validTo)
298
+ .collectFrom(
299
+ [cdpUtxo],
300
+ serialiseCdpRedeemer({
301
+ AdjustCdp: {
302
+ collateralAmtChange: collateralAmount,
303
+ currentTime: currentTime,
304
+ mintedAmtChange: mintAmount,
305
+ },
306
+ }),
307
+ )
308
+ .readFrom([cdpRefScriptUtxo])
309
+ .readFrom([iassetUtxo, govUtxo, priceOracleUtxo, interestOracleUtxo])
310
+ .pay.ToContract(
311
+ cdpUtxo.address,
312
+ {
313
+ kind: 'inline',
314
+ value: serialiseCdpDatum({
315
+ ...cdpDatum,
316
+ mintedAmt: cdpDatum.mintedAmt + mintAmount,
317
+ cdpFees: {
318
+ ActiveCDPInterestTracking: {
319
+ lastSettled: currentTime,
320
+ unitaryInterestSnapshot:
321
+ calculateUnitaryInterestSinceOracleLastUpdated(
322
+ currentTime,
323
+ interestOracleDatum,
324
+ ) + interestOracleDatum.unitaryInterest,
325
+ },
326
+ },
327
+ }),
328
+ },
329
+ addAssets(cdpUtxo.assets, mkLovelacesOf(collateralAmount)),
330
+ );
331
+
332
+ if (!cdpDatum.cdpOwner) {
333
+ throw new Error('Expected active CDP');
334
+ }
335
+
336
+ tx.addSignerKey(cdpDatum.cdpOwner);
337
+
338
+ if (mintAmount !== 0n) {
339
+ const iAssetTokenPolicyRefScriptUtxo = matchSingle(
340
+ await lucid.utxosByOutRef([
341
+ fromSystemParamsScriptRef(
342
+ sysParams.scriptReferences.iAssetTokenPolicyRef,
343
+ ),
344
+ ]),
345
+ (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
346
+ );
347
+
348
+ const iassetTokensVal = mkAssetsOf(
349
+ {
350
+ currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
351
+ tokenName: iassetDatum.assetName,
352
+ },
353
+ mintAmount,
354
+ );
355
+
356
+ tx.readFrom([iAssetTokenPolicyRefScriptUtxo]).mintAssets(
357
+ iassetTokensVal,
358
+ Data.void(),
359
+ );
360
+ }
361
+
362
+ const interestAdaAmt = match(cdpDatum.cdpFees)
363
+ .with({ FrozenCDPAccumulatedFees: P.any }, () => {
364
+ throw new Error('CDP fees wrong');
365
+ })
366
+ .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
367
+ const interestPaymentIAssetAmt = calculateAccruedInterest(
368
+ currentTime,
369
+ interest.unitaryInterestSnapshot,
370
+ cdpDatum.mintedAmt,
371
+ interest.lastSettled,
372
+ interestOracleDatum,
373
+ );
374
+
375
+ return (
376
+ (interestPaymentIAssetAmt * priceOracleDatum.price.getOnChainInt) /
377
+ 1_000_000n
378
+ );
379
+ })
380
+ .exhaustive();
381
+
382
+ const interestCollectorAdaAmt = calculateFeeFromPercentage(
383
+ iassetDatum.interestCollectorPortionPercentage,
384
+ interestAdaAmt,
385
+ );
386
+ const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
387
+
388
+ if (interestTreasuryAdaAmt > 0) {
389
+ await treasuryFeeTx(
390
+ interestTreasuryAdaAmt,
391
+ lucid,
392
+ sysParams,
393
+ tx,
394
+ treasuryOref,
395
+ );
396
+ }
397
+
398
+ let collectorFee = interestCollectorAdaAmt;
399
+
400
+ // when mint
401
+ if (mintAmount > 0n) {
402
+ collectorFee += calculateFeeFromPercentage(
403
+ iassetDatum.debtMintingFeePercentage,
404
+ (mintAmount * priceOracleDatum.price.getOnChainInt) / 1_000_000n,
405
+ );
406
+ }
407
+
408
+ // when withdraw
409
+ if (collateralAmount < 0n) {
410
+ collectorFee += calculateFeeFromPercentage(
411
+ govDatum.protocolParams.collateralFeePercentage,
412
+ -collateralAmount,
413
+ );
414
+ }
415
+
416
+ if (collectorFee > 0n) {
417
+ await collectorFeeTx(collectorFee, lucid, sysParams, tx, collectorOref);
418
+ }
419
+
420
+ return tx;
421
+ }
422
+
423
+ export async function depositCdp(
424
+ amount: bigint,
425
+ cdpOref: OutRef,
426
+ iassetOref: OutRef,
427
+ priceOracleOref: OutRef,
428
+ interestOracleOref: OutRef,
429
+ collectorOref: OutRef,
430
+ govOref: OutRef,
431
+ treasuryOref: OutRef,
432
+ params: SystemParams,
433
+ lucid: LucidEvolution,
434
+ currentSlot: number,
435
+ ): Promise<TxBuilder> {
436
+ return adjustCdp(
437
+ amount,
438
+ 0n,
439
+ cdpOref,
440
+ iassetOref,
441
+ priceOracleOref,
442
+ interestOracleOref,
443
+ collectorOref,
444
+ govOref,
445
+ treasuryOref,
446
+ params,
447
+ lucid,
448
+ currentSlot,
449
+ );
450
+ }
451
+
452
+ export async function withdrawCdp(
453
+ amount: bigint,
454
+ cdpOref: OutRef,
455
+ iassetOref: OutRef,
456
+ priceOracleOref: OutRef,
457
+ interestOracleOref: OutRef,
458
+ collectorOref: OutRef,
459
+ govOref: OutRef,
460
+ treasuryOref: OutRef,
461
+ params: SystemParams,
462
+ lucid: LucidEvolution,
463
+ currentSlot: number,
464
+ ): Promise<TxBuilder> {
465
+ return adjustCdp(
466
+ -amount,
467
+ 0n,
468
+ cdpOref,
469
+ iassetOref,
470
+ priceOracleOref,
471
+ interestOracleOref,
472
+ collectorOref,
473
+ govOref,
474
+ treasuryOref,
475
+ params,
476
+ lucid,
477
+ currentSlot,
478
+ );
479
+ }
480
+
481
+ export async function mintCdp(
482
+ amount: bigint,
483
+ cdpOref: OutRef,
484
+ iassetOref: OutRef,
485
+ priceOracleOref: OutRef,
486
+ interestOracleOref: OutRef,
487
+ collectorOref: OutRef,
488
+ govOref: OutRef,
489
+ treasuryOref: OutRef,
490
+ params: SystemParams,
491
+ lucid: LucidEvolution,
492
+ currentSlot: number,
493
+ ): Promise<TxBuilder> {
494
+ return adjustCdp(
495
+ 0n,
496
+ amount,
497
+ cdpOref,
498
+ iassetOref,
499
+ priceOracleOref,
500
+ interestOracleOref,
501
+ collectorOref,
502
+ govOref,
503
+ treasuryOref,
504
+ params,
505
+ lucid,
506
+ currentSlot,
507
+ );
508
+ }
509
+
510
+ export async function burnCdp(
511
+ amount: bigint,
512
+ cdpOref: OutRef,
513
+ iassetOref: OutRef,
514
+ priceOracleOref: OutRef,
515
+ interestOracleOref: OutRef,
516
+ collectorOref: OutRef,
517
+ govOref: OutRef,
518
+ treasuryOref: OutRef,
519
+ params: SystemParams,
520
+ lucid: LucidEvolution,
521
+ currentSlot: number,
522
+ ): Promise<TxBuilder> {
523
+ return adjustCdp(
524
+ 0n,
525
+ -amount,
526
+ cdpOref,
527
+ iassetOref,
528
+ priceOracleOref,
529
+ interestOracleOref,
530
+ collectorOref,
531
+ govOref,
532
+ treasuryOref,
533
+ params,
534
+ lucid,
535
+ currentSlot,
536
+ );
537
+ }
538
+
539
+ export async function closeCdp(
540
+ cdpOref: OutRef,
541
+ iassetOref: OutRef,
542
+ priceOracleOref: OutRef,
543
+ interestOracleOref: OutRef,
544
+ collectorOref: OutRef,
545
+ govOref: OutRef,
546
+ treasuryOref: OutRef,
547
+ sysParams: SystemParams,
548
+ lucid: LucidEvolution,
549
+ currentSlot: number,
550
+ ): Promise<TxBuilder> {
551
+ const network = lucid.config().network!;
552
+ const currentTime = BigInt(slotToUnixTime(network, currentSlot));
553
+
554
+ const cdpRefScriptUtxo = matchSingle(
555
+ await lucid.utxosByOutRef([
556
+ fromSystemParamsScriptRef(sysParams.scriptReferences.cdpValidatorRef),
557
+ ]),
558
+ (_) => new Error('Expected a single cdp Ref Script UTXO'),
559
+ );
560
+
561
+ const cdpAuthTokenPolicyRefScriptUtxo = matchSingle(
562
+ await lucid.utxosByOutRef([
563
+ fromSystemParamsScriptRef(
564
+ sysParams.scriptReferences.authTokenPolicies.cdpAuthTokenRef,
565
+ ),
566
+ ]),
567
+ (_) => new Error('Expected a single cdp auth token policy Ref Script UTXO'),
568
+ );
569
+
570
+ const iAssetTokenPolicyRefScriptUtxo = matchSingle(
571
+ await lucid.utxosByOutRef([
572
+ fromSystemParamsScriptRef(
573
+ sysParams.scriptReferences.iAssetTokenPolicyRef,
574
+ ),
575
+ ]),
576
+ (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
577
+ );
578
+
579
+ const cdpUtxo = matchSingle(
580
+ await lucid.utxosByOutRef([cdpOref]),
581
+ (_) => new Error('Expected a single cdp UTXO'),
582
+ );
583
+ const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
584
+
585
+ const iassetUtxo = matchSingle(
586
+ await lucid.utxosByOutRef([iassetOref]),
587
+ (_) => new Error('Expected a single iasset UTXO'),
588
+ );
589
+ const iassetDatum = parseIAssetDatumOrThrow(
590
+ getInlineDatumOrThrow(iassetUtxo),
591
+ );
592
+
593
+ const govUtxo = matchSingle(
594
+ await lucid.utxosByOutRef([govOref]),
595
+ (_) => new Error('Expected a single gov UTXO'),
596
+ );
597
+ const govDatum = parseGovDatumOrThrow(getInlineDatumOrThrow(govUtxo));
598
+
599
+ const priceOracleUtxo = matchSingle(
600
+ await lucid.utxosByOutRef([priceOracleOref]),
601
+ (_) => new Error('Expected a single price oracle UTXO'),
602
+ );
603
+ const priceOracleDatum = parsePriceOracleDatum(
604
+ getInlineDatumOrThrow(priceOracleUtxo),
605
+ );
606
+
607
+ const interestOracleUtxo = matchSingle(
608
+ await lucid.utxosByOutRef([interestOracleOref]),
609
+ (_) => new Error('Expected a single interest oracle UTXO'),
610
+ );
611
+ const interestOracleDatum = parseInterestOracleDatum(
612
+ getInlineDatumOrThrow(interestOracleUtxo),
613
+ );
614
+
615
+ const txValidity = oracleExpirationAwareValidity(
616
+ currentSlot,
617
+ Number(sysParams.cdpCreatorParams.biasTime),
618
+ Number(priceOracleDatum.expiration),
619
+ network,
620
+ );
621
+
622
+ const tx = lucid
623
+ .newTx()
624
+ .readFrom([
625
+ cdpRefScriptUtxo,
626
+ iAssetTokenPolicyRefScriptUtxo,
627
+ cdpAuthTokenPolicyRefScriptUtxo,
628
+ ])
629
+ .readFrom([iassetUtxo, govUtxo, priceOracleUtxo, interestOracleUtxo])
630
+ .validFrom(txValidity.validFrom)
631
+ .validTo(txValidity.validTo)
632
+ .mintAssets(
633
+ mkAssetsOf(
634
+ {
635
+ currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
636
+ tokenName: iassetDatum.assetName,
637
+ },
638
+ -cdpDatum.mintedAmt,
639
+ ),
640
+ Data.void(),
641
+ )
642
+ .mintAssets(
643
+ mkAssetsOf(fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken), -1n),
644
+ Data.void(),
645
+ )
646
+ .collectFrom(
647
+ [cdpUtxo],
648
+ serialiseCdpRedeemer({ CloseCdp: { currentTime: currentTime } }),
649
+ );
650
+
651
+ if (!cdpDatum.cdpOwner) {
652
+ throw new Error('Expected active CDP');
653
+ }
654
+
655
+ tx.addSignerKey(cdpDatum.cdpOwner);
656
+
657
+ const interestAdaAmt = match(cdpDatum.cdpFees)
658
+ .with({ FrozenCDPAccumulatedFees: P.any }, () => {
659
+ throw new Error('CDP fees wrong');
660
+ })
661
+ .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
662
+ const interestPaymentIAssetAmt = calculateAccruedInterest(
663
+ currentTime,
664
+ interest.unitaryInterestSnapshot,
665
+ cdpDatum.mintedAmt,
666
+ interest.lastSettled,
667
+ interestOracleDatum,
668
+ );
669
+
670
+ return (
671
+ (interestPaymentIAssetAmt * priceOracleDatum.price.getOnChainInt) /
672
+ 1_000_000n
673
+ );
674
+ })
675
+ .exhaustive();
676
+
677
+ const interestCollectorAdaAmt = calculateFeeFromPercentage(
678
+ iassetDatum.interestCollectorPortionPercentage,
679
+ interestAdaAmt,
680
+ );
681
+ const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
682
+
683
+ if (interestTreasuryAdaAmt > 0) {
684
+ await treasuryFeeTx(
685
+ interestTreasuryAdaAmt,
686
+ lucid,
687
+ sysParams,
688
+ tx,
689
+ treasuryOref,
690
+ );
691
+ }
692
+
693
+ const collectorFee =
694
+ interestCollectorAdaAmt +
695
+ calculateFeeFromPercentage(
696
+ govDatum.protocolParams.collateralFeePercentage,
697
+ lovelacesAmt(cdpUtxo.assets) - interestAdaAmt,
698
+ );
699
+
700
+ if (collectorFee > 0n) {
701
+ await collectorFeeTx(collectorFee, lucid, sysParams, tx, collectorOref);
702
+ }
703
+
704
+ return tx;
705
+ }
706
+
707
+ export async function redeemCdp(
708
+ /**
709
+ * When the goal is to redeem the maximum possible, just pass in the total minted amount of the CDP.
710
+ * The logic will automatically cap the amount to the max.
711
+ */
712
+ attemptedRedemptionIAssetAmt: bigint,
713
+ cdpOref: OutRef,
714
+ iassetOref: OutRef,
715
+ priceOracleOref: OutRef,
716
+ interestOracleOref: OutRef,
717
+ collectorOref: OutRef,
718
+ treasuryOref: OutRef,
719
+ sysParams: SystemParams,
720
+ lucid: LucidEvolution,
721
+ currentSlot: number,
722
+ ): Promise<TxBuilder> {
723
+ const network = lucid.config().network!;
724
+ const currentTime = BigInt(slotToUnixTime(network, currentSlot));
725
+
726
+ const cdpRefScriptUtxo = matchSingle(
727
+ await lucid.utxosByOutRef([
728
+ fromSystemParamsScriptRef(sysParams.scriptReferences.cdpValidatorRef),
729
+ ]),
730
+ (_) => new Error('Expected a single cdp Ref Script UTXO'),
731
+ );
732
+
733
+ const iAssetTokenPolicyRefScriptUtxo = matchSingle(
734
+ await lucid.utxosByOutRef([
735
+ fromSystemParamsScriptRef(
736
+ sysParams.scriptReferences.iAssetTokenPolicyRef,
737
+ ),
738
+ ]),
739
+ (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
740
+ );
741
+
742
+ const cdpUtxo = matchSingle(
743
+ await lucid.utxosByOutRef([cdpOref]),
744
+ (_) => new Error('Expected a single cdp UTXO'),
745
+ );
746
+ const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
747
+
748
+ const iassetUtxo = matchSingle(
749
+ await lucid.utxosByOutRef([iassetOref]),
750
+ (_) => new Error('Expected a single iasset UTXO'),
751
+ );
752
+ const iassetDatum = parseIAssetDatumOrThrow(
753
+ getInlineDatumOrThrow(iassetUtxo),
754
+ );
755
+
756
+ const priceOracleUtxo = matchSingle(
757
+ await lucid.utxosByOutRef([priceOracleOref]),
758
+ (_) => new Error('Expected a single price oracle UTXO'),
759
+ );
760
+ const priceOracleDatum = parsePriceOracleDatum(
761
+ getInlineDatumOrThrow(priceOracleUtxo),
762
+ );
763
+
764
+ const interestOracleUtxo = matchSingle(
765
+ await lucid.utxosByOutRef([interestOracleOref]),
766
+ (_) => new Error('Expected a single interest oracle UTXO'),
767
+ );
768
+ const interestOracleDatum = parseInterestOracleDatum(
769
+ getInlineDatumOrThrow(interestOracleUtxo),
770
+ );
771
+
772
+ const interestAdaAmt = match(cdpDatum.cdpFees)
773
+ .with({ FrozenCDPAccumulatedFees: P.any }, () => {
774
+ throw new Error('CDP fees wrong');
775
+ })
776
+ .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
777
+ const interestPaymentIAssetAmt = calculateAccruedInterest(
778
+ currentTime,
779
+ interest.unitaryInterestSnapshot,
780
+ cdpDatum.mintedAmt,
781
+ interest.lastSettled,
782
+ interestOracleDatum,
783
+ );
784
+
785
+ return ocdMul(
786
+ { getOnChainInt: interestPaymentIAssetAmt },
787
+ priceOracleDatum.price,
788
+ ).getOnChainInt;
789
+ })
790
+ .exhaustive();
791
+
792
+ const interestCollectorAdaAmt = calculateFeeFromPercentage(
793
+ iassetDatum.interestCollectorPortionPercentage,
794
+ interestAdaAmt,
795
+ );
796
+ const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
797
+
798
+ const collateralAmtMinusInterest =
799
+ lovelacesAmt(cdpUtxo.assets) - interestAdaAmt;
800
+
801
+ const [isPartial, redemptionIAssetAmt] = (() => {
802
+ const res = calculateMinCollateralCappedIAssetRedemptionAmt(
803
+ collateralAmtMinusInterest,
804
+ cdpDatum.mintedAmt,
805
+ priceOracleDatum.price,
806
+ iassetDatum.redemptionRatio,
807
+ iassetDatum.redemptionReimbursementPercentage,
808
+ BigInt(sysParams.cdpParams.minCollateralInLovelace),
809
+ );
810
+
811
+ const redemptionAmt = bigintMin(
812
+ attemptedRedemptionIAssetAmt,
813
+ res.cappedIAssetRedemptionAmt,
814
+ );
815
+
816
+ return [redemptionAmt < res.cappedIAssetRedemptionAmt, redemptionAmt];
817
+ })();
818
+
819
+ if (redemptionIAssetAmt <= 0) {
820
+ throw new Error("There's no iAssets available for redemption.");
821
+ }
822
+
823
+ const redemptionLovelacesAmt = ocdMul(priceOracleDatum.price, {
824
+ getOnChainInt: redemptionIAssetAmt,
825
+ }).getOnChainInt;
826
+
827
+ const partialRedemptionFee = isPartial
828
+ ? BigInt(sysParams.cdpParams.partialRedemptionExtraFeeLovelace)
829
+ : 0n;
830
+
831
+ const processingFee = calculateFeeFromPercentage(
832
+ iassetDatum.redemptionProcessingFeePercentage,
833
+ redemptionLovelacesAmt,
834
+ );
835
+
836
+ const reimburstmentFee = calculateFeeFromPercentage(
837
+ iassetDatum.redemptionReimbursementPercentage,
838
+ redemptionLovelacesAmt,
839
+ );
840
+
841
+ const txValidity = oracleExpirationAwareValidity(
842
+ currentSlot,
843
+ Number(sysParams.cdpCreatorParams.biasTime),
844
+ Number(priceOracleDatum.expiration),
845
+ network,
846
+ );
847
+
848
+ const tx = lucid
849
+ .newTx()
850
+ // Ref Script
851
+ .readFrom([cdpRefScriptUtxo, iAssetTokenPolicyRefScriptUtxo])
852
+ // Ref inputs
853
+ .readFrom([iassetUtxo, priceOracleUtxo, interestOracleUtxo])
854
+ .validFrom(txValidity.validFrom)
855
+ .validTo(txValidity.validTo)
856
+ .collectFrom(
857
+ [cdpUtxo],
858
+ serialiseCdpRedeemer({ RedeemCdp: { currentTime: currentTime } }),
859
+ )
860
+ .mintAssets(
861
+ mkAssetsOf(
862
+ {
863
+ currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
864
+ tokenName: iassetDatum.assetName,
865
+ },
866
+ -redemptionIAssetAmt,
867
+ ),
868
+ Data.void(),
869
+ )
870
+ .pay.ToContract(
871
+ cdpUtxo.address,
872
+ {
873
+ kind: 'inline',
874
+ value: serialiseCdpDatum({
875
+ ...cdpDatum,
876
+ mintedAmt: cdpDatum.mintedAmt - redemptionIAssetAmt,
877
+ cdpFees: {
878
+ ActiveCDPInterestTracking: {
879
+ lastSettled: currentTime,
880
+ unitaryInterestSnapshot:
881
+ interestOracleDatum.unitaryInterest +
882
+ calculateUnitaryInterestSinceOracleLastUpdated(
883
+ currentTime,
884
+ interestOracleDatum,
885
+ ),
886
+ },
887
+ },
888
+ }),
889
+ },
890
+ addAssets(
891
+ cdpUtxo.assets,
892
+ mkLovelacesOf(-redemptionLovelacesAmt),
893
+ mkLovelacesOf(reimburstmentFee),
894
+ mkLovelacesOf(-interestAdaAmt),
895
+ ),
896
+ );
897
+
898
+ await collectorFeeTx(
899
+ processingFee + partialRedemptionFee + interestCollectorAdaAmt,
900
+ lucid,
901
+ sysParams,
902
+ tx,
903
+ collectorOref,
904
+ );
905
+
906
+ await treasuryFeeTx(
907
+ interestTreasuryAdaAmt,
908
+ lucid,
909
+ sysParams,
910
+ tx,
911
+ treasuryOref,
912
+ );
913
+
914
+ return tx;
915
+ }
916
+
917
+ export async function freezeCdp(
918
+ cdpOref: OutRef,
919
+ iassetOref: OutRef,
920
+ priceOracleOref: OutRef,
921
+ interestOracleOref: OutRef,
922
+ sysParams: SystemParams,
923
+ lucid: LucidEvolution,
924
+ currentSlot: number,
925
+ ): Promise<TxBuilder> {
926
+ const network = lucid.config().network!;
927
+ const currentTime = BigInt(slotToUnixTime(network, currentSlot));
928
+
929
+ const cdpRefScriptUtxo = matchSingle(
930
+ await lucid.utxosByOutRef([
931
+ fromSystemParamsScriptRef(sysParams.scriptReferences.cdpValidatorRef),
932
+ ]),
933
+ (_) => new Error('Expected a single cdp Ref Script UTXO'),
934
+ );
935
+ const cdpUtxo = matchSingle(
936
+ await lucid.utxosByOutRef([cdpOref]),
937
+ (_) => new Error('Expected a single cdp UTXO'),
938
+ );
939
+ const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
940
+
941
+ const iassetUtxo = matchSingle(
942
+ await lucid.utxosByOutRef([iassetOref]),
943
+ (_) => new Error('Expected a single iasset UTXO'),
944
+ );
945
+ const iassetDatum = parseIAssetDatumOrThrow(
946
+ getInlineDatumOrThrow(iassetUtxo),
947
+ );
948
+
949
+ const priceOracleUtxo = matchSingle(
950
+ await lucid.utxosByOutRef([priceOracleOref]),
951
+ (_) => new Error('Expected a single price oracle UTXO'),
952
+ );
953
+ const priceOracleDatum = parsePriceOracleDatum(
954
+ getInlineDatumOrThrow(priceOracleUtxo),
955
+ );
956
+
957
+ const interestOracleUtxo = matchSingle(
958
+ await lucid.utxosByOutRef([interestOracleOref]),
959
+ (_) => new Error('Expected a single interest oracle UTXO'),
960
+ );
961
+ const interestOracleDatum = parseInterestOracleDatum(
962
+ getInlineDatumOrThrow(interestOracleUtxo),
963
+ );
964
+
965
+ const interestAdaAmt = match(cdpDatum.cdpFees)
966
+ .with({ FrozenCDPAccumulatedFees: P.any }, () => {
967
+ throw new Error('CDP fees wrong');
968
+ })
969
+ .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
970
+ const interestPaymentIAssetAmt = calculateAccruedInterest(
971
+ currentTime,
972
+ interest.unitaryInterestSnapshot,
973
+ cdpDatum.mintedAmt,
974
+ interest.lastSettled,
975
+ interestOracleDatum,
976
+ );
977
+
978
+ const maxInterestLovelaces = computeInterestLovelacesFor100PercentCR(
979
+ lovelacesAmt(cdpUtxo.assets),
980
+ cdpDatum.mintedAmt,
981
+ priceOracleDatum.price,
982
+ );
983
+
984
+ return bigintMin(
985
+ maxInterestLovelaces,
986
+ ocdMul(
987
+ { getOnChainInt: interestPaymentIAssetAmt },
988
+ priceOracleDatum.price,
989
+ ).getOnChainInt,
990
+ );
991
+ })
992
+ .exhaustive();
993
+
994
+ const interestCollectorAdaAmt = calculateFeeFromPercentage(
995
+ iassetDatum.interestCollectorPortionPercentage,
996
+ interestAdaAmt,
997
+ );
998
+
999
+ const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
1000
+
1001
+ const inputCollateralMinusInterest =
1002
+ lovelacesAmt(cdpUtxo.assets) - interestAdaAmt;
1003
+
1004
+ const cdpDebtAdaValue = ocdMul(
1005
+ { getOnChainInt: cdpDatum.mintedAmt },
1006
+ priceOracleDatum.price,
1007
+ ).getOnChainInt;
1008
+
1009
+ const liquidationProcessingFee = bigintMin(
1010
+ calculateFeeFromPercentage(
1011
+ iassetDatum.liquidationProcessingFeePercentage,
1012
+ inputCollateralMinusInterest,
1013
+ ),
1014
+ calculateFeeFromPercentage(
1015
+ iassetDatum.liquidationProcessingFeePercentage,
1016
+ cdpDebtAdaValue,
1017
+ ),
1018
+ );
1019
+
1020
+ const txValidity = oracleExpirationAwareValidity(
1021
+ currentSlot,
1022
+ Number(sysParams.cdpCreatorParams.biasTime),
1023
+ Number(priceOracleDatum.expiration),
1024
+ network,
1025
+ );
1026
+
1027
+ return (
1028
+ lucid
1029
+ .newTx()
1030
+ // Ref Script
1031
+ .readFrom([cdpRefScriptUtxo])
1032
+ // Ref inputs
1033
+ .readFrom([iassetUtxo, priceOracleUtxo, interestOracleUtxo])
1034
+ .validFrom(txValidity.validFrom)
1035
+ .validTo(txValidity.validTo)
1036
+ .collectFrom(
1037
+ [cdpUtxo],
1038
+ serialiseCdpRedeemer({ FreezeCdp: { currentTime: currentTime } }),
1039
+ )
1040
+ .pay.ToContract(
1041
+ createScriptAddress(network, sysParams.validatorHashes.cdpHash),
1042
+ {
1043
+ kind: 'inline',
1044
+ value: serialiseCdpDatum({
1045
+ ...cdpDatum,
1046
+ cdpOwner: null,
1047
+ cdpFees: {
1048
+ FrozenCDPAccumulatedFees: {
1049
+ lovelacesIndyStakers:
1050
+ liquidationProcessingFee + interestCollectorAdaAmt,
1051
+ lovelacesTreasury: interestTreasuryAdaAmt,
1052
+ },
1053
+ },
1054
+ }),
1055
+ },
1056
+ cdpUtxo.assets,
1057
+ )
1058
+ );
1059
+ }
1060
+
1061
+ export async function liquidateCdp(
1062
+ cdpOref: OutRef,
1063
+ stabilityPoolOref: OutRef,
1064
+ collectorOref: OutRef,
1065
+ treasuryOref: OutRef,
1066
+ sysParams: SystemParams,
1067
+ lucid: LucidEvolution,
1068
+ ): Promise<TxBuilder> {
1069
+ const cdpRefScriptUtxo = matchSingle(
1070
+ await lucid.utxosByOutRef([
1071
+ fromSystemParamsScriptRef(sysParams.scriptReferences.cdpValidatorRef),
1072
+ ]),
1073
+ (_) => new Error('Expected a single cdp Ref Script UTXO'),
1074
+ );
1075
+ const stabilityPoolRefScriptUtxo = matchSingle(
1076
+ await lucid.utxosByOutRef([
1077
+ fromSystemParamsScriptRef(
1078
+ sysParams.scriptReferences.stabilityPoolValidatorRef,
1079
+ ),
1080
+ ]),
1081
+ (_) => new Error('Expected a single stability pool Ref Script UTXO'),
1082
+ );
1083
+ const iAssetTokenPolicyRefScriptUtxo = matchSingle(
1084
+ await lucid.utxosByOutRef([
1085
+ fromSystemParamsScriptRef(
1086
+ sysParams.scriptReferences.iAssetTokenPolicyRef,
1087
+ ),
1088
+ ]),
1089
+ (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
1090
+ );
1091
+ const cdpAuthTokenPolicyRefScriptUtxo = matchSingle(
1092
+ await lucid.utxosByOutRef([
1093
+ fromSystemParamsScriptRef(
1094
+ sysParams.scriptReferences.authTokenPolicies.cdpAuthTokenRef,
1095
+ ),
1096
+ ]),
1097
+ (_) => new Error('Expected a single cdp auth token policy Ref Script UTXO'),
1098
+ );
1099
+
1100
+ const cdpUtxo = matchSingle(
1101
+ await lucid.utxosByOutRef([cdpOref]),
1102
+ (_) => new Error('Expected a single cdp UTXO'),
1103
+ );
1104
+ const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
1105
+
1106
+ const spUtxo = matchSingle(
1107
+ await lucid.utxosByOutRef([stabilityPoolOref]),
1108
+ (_) => new Error('Expected a single stability pool UTXO'),
1109
+ );
1110
+ const spDatum = parseStabilityPoolDatum(getInlineDatumOrThrow(spUtxo));
1111
+
1112
+ const [lovelacesForTreasury, lovelacesForCollector] = match(cdpDatum.cdpFees)
1113
+ .returnType<[bigint, bigint]>()
1114
+ .with({ FrozenCDPAccumulatedFees: P.select() }, (fees) => [
1115
+ fees.lovelacesTreasury,
1116
+ fees.lovelacesIndyStakers,
1117
+ ])
1118
+ .with({ ActiveCDPInterestTracking: P.any }, () => {
1119
+ throw new Error('CDP fees wrong');
1120
+ })
1121
+ .exhaustive();
1122
+
1123
+ const cdpNftAc = fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken);
1124
+ const iassetsAc = {
1125
+ currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
1126
+ tokenName: cdpDatum.iasset,
1127
+ };
1128
+
1129
+ const spIassetAmt = assetClassValueOf(spUtxo.assets, iassetsAc);
1130
+
1131
+ const iassetBurnAmt = bigintMin(cdpDatum.mintedAmt, spIassetAmt);
1132
+
1133
+ const collateralAvailable = lovelacesAmt(cdpUtxo.assets);
1134
+ const collateralAvailMinusFees =
1135
+ collateralAvailable - lovelacesForCollector - lovelacesForTreasury;
1136
+ const collateralAbsorbed =
1137
+ (collateralAvailMinusFees * iassetBurnAmt) / cdpDatum.mintedAmt;
1138
+
1139
+ const isPartial = spIassetAmt < cdpDatum.mintedAmt;
1140
+
1141
+ const tx = lucid
1142
+ .newTx()
1143
+ .readFrom([
1144
+ cdpRefScriptUtxo,
1145
+ stabilityPoolRefScriptUtxo,
1146
+ iAssetTokenPolicyRefScriptUtxo,
1147
+ cdpAuthTokenPolicyRefScriptUtxo,
1148
+ ])
1149
+ .collectFrom([spUtxo], serialiseStabilityPoolRedeemer('LiquidateCDP'))
1150
+ .collectFrom([cdpUtxo], serialiseCdpRedeemer('Liquidate'))
1151
+ .mintAssets(mkAssetsOf(iassetsAc, -iassetBurnAmt), Data.void())
1152
+ .pay.ToContract(
1153
+ spUtxo.address,
1154
+ {
1155
+ kind: 'inline',
1156
+ value: serialiseStabilityPoolDatum({
1157
+ StabilityPool: liquidationHelper(
1158
+ spDatum,
1159
+ iassetBurnAmt,
1160
+ collateralAbsorbed,
1161
+ ).newSpContent,
1162
+ }),
1163
+ },
1164
+ addAssets(
1165
+ spUtxo.assets,
1166
+ mkLovelacesOf(collateralAbsorbed),
1167
+ mkAssetsOf(iassetsAc, -iassetBurnAmt),
1168
+ ),
1169
+ );
1170
+
1171
+ if (isPartial) {
1172
+ tx.pay.ToContract(
1173
+ cdpUtxo.address,
1174
+ {
1175
+ kind: 'inline',
1176
+ value: serialiseCdpDatum({
1177
+ ...cdpDatum,
1178
+ mintedAmt: cdpDatum.mintedAmt - spIassetAmt,
1179
+ cdpFees: {
1180
+ FrozenCDPAccumulatedFees: {
1181
+ lovelacesIndyStakers: 0n,
1182
+ lovelacesTreasury: 0n,
1183
+ },
1184
+ },
1185
+ }),
1186
+ },
1187
+ addAssets(
1188
+ mkAssetsOf(cdpNftAc, assetClassValueOf(cdpUtxo.assets, cdpNftAc)),
1189
+ mkLovelacesOf(collateralAvailable - collateralAbsorbed),
1190
+ ),
1191
+ );
1192
+ } else {
1193
+ tx.mintAssets(
1194
+ mkAssetsOf(cdpNftAc, -assetClassValueOf(cdpUtxo.assets, cdpNftAc)),
1195
+ Data.void(),
1196
+ );
1197
+ }
1198
+
1199
+ await collectorFeeTx(
1200
+ lovelacesForCollector,
1201
+ lucid,
1202
+ sysParams,
1203
+ tx,
1204
+ collectorOref,
1205
+ );
1206
+
1207
+ await treasuryFeeTx(lovelacesForTreasury, lucid, sysParams, tx, treasuryOref);
1208
+
1209
+ return tx;
1210
+ }
1211
+
1212
+ export async function mergeCdps(
1213
+ cdpsToMergeUtxos: OutRef[],
1214
+ sysParams: SystemParams,
1215
+ lucid: LucidEvolution,
1216
+ ): Promise<TxBuilder> {
1217
+ const cdpRefScriptUtxo = matchSingle(
1218
+ await lucid.utxosByOutRef([
1219
+ fromSystemParamsScriptRef(sysParams.scriptReferences.cdpValidatorRef),
1220
+ ]),
1221
+ (_) => new Error('Expected a single cdp Ref Script UTXO'),
1222
+ );
1223
+
1224
+ const cdpUtxos = await lucid.utxosByOutRef(cdpsToMergeUtxos);
1225
+ const cdpDatums = cdpUtxos.map((utxo) =>
1226
+ parseCdpDatumOrThrow(getInlineDatumOrThrow(utxo)),
1227
+ );
1228
+
1229
+ if (cdpUtxos.length !== cdpsToMergeUtxos.length) {
1230
+ throw new Error('Expected certain number of CDPs');
1231
+ }
1232
+
1233
+ const aggregatedVal = F.pipe(
1234
+ cdpUtxos,
1235
+ A.reduce<UTxO, Assets>({}, (acc, utxo) => addAssets(acc, utxo.assets)),
1236
+ );
1237
+
1238
+ const aggregatedMintedAmt = F.pipe(
1239
+ cdpDatums,
1240
+ A.reduce<CDPContent, bigint>(0n, (acc, cdpDat) => acc + cdpDat.mintedAmt),
1241
+ );
1242
+
1243
+ type AggregatedFees = {
1244
+ aggregatedFeeIndyStakers: bigint;
1245
+ aggregatedFeeTreasury: bigint;
1246
+ };
1247
+
1248
+ const { aggregatedFeeTreasury, aggregatedFeeIndyStakers } = F.pipe(
1249
+ cdpDatums,
1250
+ A.reduce<CDPContent, AggregatedFees>(
1251
+ { aggregatedFeeIndyStakers: 0n, aggregatedFeeTreasury: 0n },
1252
+ (acc, cdpDat) =>
1253
+ match(cdpDat.cdpFees)
1254
+ .returnType<AggregatedFees>()
1255
+ .with({ FrozenCDPAccumulatedFees: P.select() }, (fees) => ({
1256
+ aggregatedFeeIndyStakers:
1257
+ acc.aggregatedFeeIndyStakers + fees.lovelacesIndyStakers,
1258
+ aggregatedFeeTreasury:
1259
+ acc.aggregatedFeeTreasury + fees.lovelacesTreasury,
1260
+ }))
1261
+ .otherwise(() => acc),
1262
+ ),
1263
+ );
1264
+
1265
+ const [[mainMergeUtxo, mainCdpDatum], otherMergeUtxos] = match(
1266
+ A.zip(cdpUtxos, cdpDatums),
1267
+ )
1268
+ .returnType<[[UTxO, CDPContent], UTxO[]]>()
1269
+ .with([P._, ...P.array()], ([main, ...other]) => [
1270
+ main,
1271
+ other.map((a) => a[0]),
1272
+ ])
1273
+ .otherwise(() => {
1274
+ throw new Error('Expects more CDPs for merging');
1275
+ });
1276
+
1277
+ return lucid
1278
+ .newTx()
1279
+ .readFrom([cdpRefScriptUtxo])
1280
+ .collectFrom([mainMergeUtxo], serialiseCdpRedeemer('MergeCdps'))
1281
+ .collectFrom(
1282
+ otherMergeUtxos,
1283
+ serialiseCdpRedeemer({
1284
+ MergeAuxiliary: {
1285
+ mainMergeUtxo: {
1286
+ outputIndex: BigInt(mainMergeUtxo.outputIndex),
1287
+ txHash: { hash: mainMergeUtxo.txHash },
1288
+ },
1289
+ },
1290
+ }),
1291
+ )
1292
+ .pay.ToContract(
1293
+ mainMergeUtxo.address,
1294
+ {
1295
+ kind: 'inline',
1296
+ value: serialiseCdpDatum({
1297
+ cdpOwner: null,
1298
+ iasset: mainCdpDatum.iasset,
1299
+ mintedAmt: aggregatedMintedAmt,
1300
+ cdpFees: {
1301
+ FrozenCDPAccumulatedFees: {
1302
+ lovelacesIndyStakers: aggregatedFeeIndyStakers,
1303
+ lovelacesTreasury: aggregatedFeeTreasury,
1304
+ },
1305
+ },
1306
+ }),
1307
+ },
1308
+ aggregatedVal,
1309
+ );
1310
+ }