@indigo-labs/indigo-sdk 0.2.41 → 0.3.0

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 (201) hide show
  1. package/.github/workflows/ci.yml +4 -2
  2. package/dist/index.d.mts +3008 -2194
  3. package/dist/index.d.ts +3008 -2194
  4. package/dist/index.js +9827 -6194
  5. package/dist/index.mjs +8591 -4809
  6. package/package.json +14 -3
  7. package/src/contracts/cdp/helpers.ts +68 -72
  8. package/src/contracts/cdp/scripts.ts +50 -13
  9. package/src/contracts/cdp/transactions.ts +831 -545
  10. package/src/contracts/cdp/types-new.ts +256 -0
  11. package/src/contracts/cdp/types.ts +26 -144
  12. package/src/contracts/cdp-creator/scripts.ts +15 -9
  13. package/src/contracts/cdp-creator/types-new.ts +50 -0
  14. package/src/contracts/cdp-creator/types.ts +5 -31
  15. package/src/contracts/collector/scripts.ts +1 -1
  16. package/src/contracts/collector/transactions.ts +23 -13
  17. package/src/contracts/collector/types-new.ts +17 -0
  18. package/src/contracts/execute/scripts.ts +19 -10
  19. package/src/contracts/execute/types-new.ts +44 -0
  20. package/src/contracts/execute/types.ts +5 -38
  21. package/src/contracts/gov/helpers.ts +187 -51
  22. package/src/contracts/gov/scripts.ts +17 -10
  23. package/src/contracts/gov/transactions.ts +599 -271
  24. package/src/contracts/gov/types-new.ts +253 -100
  25. package/src/contracts/gov/types.ts +4 -71
  26. package/src/contracts/iasset/helpers.ts +172 -0
  27. package/src/contracts/iasset/scripts.ts +38 -0
  28. package/src/contracts/iasset/types.ts +154 -0
  29. package/src/contracts/initialize/actions.ts +768 -0
  30. package/src/contracts/initialize/helpers.ts +611 -36
  31. package/src/contracts/initialize/types.ts +102 -28
  32. package/src/contracts/interest-collection/helpers.ts +19 -0
  33. package/src/contracts/interest-collection/scripts.ts +44 -0
  34. package/src/contracts/interest-collection/transactions.ts +436 -0
  35. package/src/contracts/interest-collection/types-new.ts +50 -0
  36. package/src/contracts/interest-collection/types.ts +26 -0
  37. package/src/contracts/interest-oracle/helpers.ts +2 -30
  38. package/src/contracts/interest-oracle/scripts.ts +1 -1
  39. package/src/contracts/interest-oracle/transactions.ts +21 -16
  40. package/src/contracts/interest-oracle/types-new.ts +32 -0
  41. package/src/contracts/interest-oracle/types.ts +1 -40
  42. package/src/contracts/one-shot/transactions.ts +1 -2
  43. package/src/contracts/poll/helpers.ts +5 -23
  44. package/src/contracts/poll/scripts.ts +12 -13
  45. package/src/contracts/poll/types-poll-manager.ts +1 -19
  46. package/src/contracts/poll/types-poll-new.ts +170 -0
  47. package/src/contracts/poll/types-poll-shard.ts +2 -24
  48. package/src/contracts/price-oracle/helpers.ts +1 -4
  49. package/src/contracts/price-oracle/scripts.ts +3 -8
  50. package/src/contracts/price-oracle/transactions.ts +32 -25
  51. package/src/contracts/price-oracle/types-new.ts +50 -0
  52. package/src/contracts/price-oracle/types.ts +2 -36
  53. package/src/contracts/pyth-feed/helpers.ts +58 -0
  54. package/src/contracts/pyth-feed/scripts.ts +15 -0
  55. package/src/contracts/pyth-feed/types.ts +181 -0
  56. package/src/contracts/rob/helpers.ts +405 -0
  57. package/src/contracts/rob/scripts.ts +35 -0
  58. package/src/contracts/rob/transactions.ts +410 -0
  59. package/src/contracts/rob/types-new.ts +128 -0
  60. package/src/contracts/rob/types.ts +16 -0
  61. package/src/contracts/rob-leverage/helpers.ts +424 -0
  62. package/src/contracts/{leverage → rob-leverage}/transactions.ts +68 -48
  63. package/src/contracts/stability-pool/helpers.ts +714 -230
  64. package/src/contracts/stability-pool/scripts.ts +20 -15
  65. package/src/contracts/stability-pool/transactions.ts +625 -495
  66. package/src/contracts/stability-pool/types-new.ts +237 -100
  67. package/src/contracts/stability-pool/types.ts +5 -22
  68. package/src/contracts/stableswap/helpers.ts +22 -0
  69. package/src/contracts/stableswap/scripts.ts +37 -0
  70. package/src/contracts/stableswap/transactions.ts +647 -0
  71. package/src/contracts/stableswap/types-new.ts +131 -0
  72. package/src/contracts/stableswap/types.ts +17 -0
  73. package/src/contracts/staking/helpers.ts +49 -34
  74. package/src/contracts/staking/scripts.ts +1 -1
  75. package/src/contracts/staking/transactions.ts +85 -130
  76. package/src/contracts/staking/types-new.ts +60 -28
  77. package/src/contracts/staking/types.ts +1 -28
  78. package/src/contracts/treasury/helpers.ts +21 -0
  79. package/src/contracts/treasury/scripts.ts +16 -26
  80. package/src/contracts/treasury/transactions.ts +256 -27
  81. package/src/contracts/treasury/types-new.ts +69 -0
  82. package/src/contracts/treasury/types.ts +2 -43
  83. package/src/contracts/version-registry/scripts.ts +2 -2
  84. package/src/contracts/version-registry/types-new.ts +6 -7
  85. package/src/index.ts +37 -20
  86. package/src/scripts/auth-token-policy.ts +3 -2
  87. package/src/scripts/iasset-policy.ts +3 -2
  88. package/src/types/evolution-schema-options.ts +3 -3
  89. package/src/types/generic.ts +17 -89
  90. package/src/types/multisig.ts +48 -0
  91. package/src/types/on-chain-decimal.ts +14 -7
  92. package/src/types/rational.ts +61 -0
  93. package/src/types/system-params.ts +237 -41
  94. package/src/utils/array-utils.ts +70 -1
  95. package/src/utils/bigint-utils.ts +12 -0
  96. package/src/utils/indigo-helpers.ts +8 -10
  97. package/src/utils/lucid-utils.ts +47 -40
  98. package/src/utils/oracle-helpers.ts +62 -0
  99. package/src/utils/pyth/decode.ts +223 -0
  100. package/src/utils/pyth/encode.ts +262 -0
  101. package/src/utils/pyth/index.ts +14 -0
  102. package/src/utils/pyth/types.ts +87 -0
  103. package/src/validators/always-succeed-validator.ts +6 -0
  104. package/src/validators/cdp-creator-validator.ts +2 -2
  105. package/src/validators/cdp-redeem-validator.ts +7 -0
  106. package/src/validators/cdp-validator.ts +2 -2
  107. package/src/validators/collector-validator.ts +2 -2
  108. package/src/validators/execute-validator.ts +2 -2
  109. package/src/validators/governance-validator.ts +2 -2
  110. package/src/validators/iasset-validator.ts +7 -0
  111. package/src/validators/interest-collection-validator.ts +7 -0
  112. package/src/validators/interest-oracle-validator.ts +2 -2
  113. package/src/validators/poll-manager-validator.ts +2 -2
  114. package/src/validators/poll-shard-validator.ts +2 -2
  115. package/src/validators/price-oracle-validator.ts +7 -0
  116. package/src/validators/pyth-feed-validator.ts +7 -0
  117. package/src/validators/rob-validator.ts +7 -0
  118. package/src/validators/stability-pool-validator.ts +2 -2
  119. package/src/validators/stableswap-validator.ts +7 -0
  120. package/src/validators/staking-validator.ts +2 -2
  121. package/src/validators/treasury-validator.ts +2 -2
  122. package/src/validators/version-record-policy.ts +2 -2
  123. package/src/validators/version-registry-validator.ts +2 -2
  124. package/tests/always-succeed/script.ts +7 -0
  125. package/tests/bigint-utils.test.ts +41 -0
  126. package/tests/cdp/actions.ts +611 -0
  127. package/tests/cdp/cdp-helpers.ts +55 -0
  128. package/tests/cdp/cdp-queries.ts +440 -0
  129. package/tests/cdp/cdp.test.ts +6087 -0
  130. package/tests/cdp/transactions-mutated.ts +1729 -0
  131. package/tests/data/system-params.json +177 -34
  132. package/tests/datums.test.ts +209 -210
  133. package/tests/endpoints/initialize.ts +68 -0
  134. package/tests/endpoints/interest-collector.ts +37 -0
  135. package/tests/endpoints/treasury.ts +70 -0
  136. package/tests/gov/actions.ts +406 -0
  137. package/tests/gov/gov.test.ts +4450 -0
  138. package/tests/{queries → gov}/governance-queries.ts +6 -3
  139. package/tests/hash-checks.test.ts +38 -11
  140. package/tests/indigo-test-helpers.ts +100 -0
  141. package/tests/initialize.test.ts +61 -9
  142. package/tests/interest-collection/interest-collection.test.ts +892 -0
  143. package/tests/interest-collection/interest-collector-queries.ts +49 -0
  144. package/tests/interest-collection/transactions-mutated.ts +260 -0
  145. package/tests/interest-oracle.test.ts +43 -35
  146. package/tests/mock/assets-mock.ts +234 -23
  147. package/tests/mock/protocol-params-mock.ts +21 -0
  148. package/tests/price-oracle/actions.ts +163 -0
  149. package/tests/price-oracle/price-oracle-queries.ts +12 -0
  150. package/tests/price-oracle/price-oracle.test.ts +240 -0
  151. package/tests/price-oracle/transactions-mutated.ts +62 -0
  152. package/tests/pyth/endpoints.ts +96 -0
  153. package/tests/pyth/helpers.ts +37 -0
  154. package/tests/pyth/pyth-encoding.test.ts +376 -0
  155. package/tests/pyth/pyth-indigo.test.ts +509 -0
  156. package/tests/pyth/pyth.test.ts +300 -0
  157. package/tests/queries/execute-queries.ts +6 -5
  158. package/tests/queries/iasset-queries.ts +175 -5
  159. package/tests/queries/interest-oracle-queries.ts +4 -2
  160. package/tests/queries/poll-queries.ts +8 -9
  161. package/tests/queries/stability-pool-queries.ts +95 -48
  162. package/tests/queries/staking-queries.ts +4 -2
  163. package/tests/queries/treasury-queries.ts +80 -5
  164. package/tests/rob/actions.ts +58 -0
  165. package/tests/{lrp-leverage.test.ts → rob/rob-leverage.test.ts} +393 -296
  166. package/tests/rob/rob-queries.ts +95 -0
  167. package/tests/rob/rob.test.ts +3762 -0
  168. package/tests/rob/transactions-mutated.ts +853 -0
  169. package/tests/script-size.test.ts +240 -0
  170. package/tests/setup.ts +135 -0
  171. package/tests/stability-pool/actions.ts +210 -0
  172. package/tests/stability-pool.test.ts +5469 -666
  173. package/tests/stableswap/stableswap-actions.ts +84 -0
  174. package/tests/stableswap/stableswap-queries.ts +89 -0
  175. package/tests/stableswap/stableswap.test.ts +3891 -0
  176. package/tests/stableswap/transactions-mutated.ts +348 -0
  177. package/tests/staking.test.ts +82 -99
  178. package/tests/test-helpers.ts +58 -11
  179. package/tests/treasury.test.ts +242 -0
  180. package/tests/utils/asserts.ts +74 -0
  181. package/tests/utils/benchmark-utils.ts +81 -0
  182. package/tests/utils/index.ts +122 -4
  183. package/tsconfig.json +9 -1
  184. package/vitest.config.ts +3 -1
  185. package/src/contracts/collector/types.ts +0 -16
  186. package/src/contracts/initialize/transactions.ts +0 -891
  187. package/src/contracts/leverage/helpers.ts +0 -424
  188. package/src/contracts/lrp/helpers.ts +0 -294
  189. package/src/contracts/lrp/scripts.ts +0 -27
  190. package/src/contracts/lrp/transactions.ts +0 -250
  191. package/src/contracts/lrp/types.ts +0 -131
  192. package/src/contracts/poll/types-poll.ts +0 -88
  193. package/src/contracts/vesting/helpers.ts +0 -218
  194. package/src/utils/value-helpers.ts +0 -37
  195. package/src/validators/lrp-validator.ts +0 -7
  196. package/tests/cdp.test.ts +0 -1528
  197. package/tests/gov.test.ts +0 -2011
  198. package/tests/lrp.test.ts +0 -673
  199. package/tests/queries/cdp-queries.ts +0 -220
  200. package/tests/queries/lrp-queries.ts +0 -76
  201. package/tests/queries/price-oracle-queries.ts +0 -10
@@ -1,10 +1,15 @@
1
1
  import {
2
2
  addAssets,
3
3
  Assets,
4
+ credentialToRewardAddress,
4
5
  Data,
6
+ fromHex,
7
+ getInputIndices,
5
8
  LucidEvolution,
6
9
  OutRef,
10
+ scriptHashToCredential,
7
11
  slotToUnixTime,
12
+ toHex,
8
13
  TxBuilder,
9
14
  UTxO,
10
15
  } from '@lucid-evolution/lucid';
@@ -17,59 +22,73 @@ import {
17
22
  addrDetails,
18
23
  createScriptAddress,
19
24
  getInlineDatumOrThrow,
20
- resolveUtxo,
21
- UtxoOrOutRef,
22
25
  } from '../../utils/lucid-utils';
23
26
  import { matchSingle } from '../../utils/utils';
24
- import {
25
- CDPContent,
26
- parseCdpDatumOrThrow,
27
- parseIAssetDatumOrThrow,
28
- serialiseCdpDatum,
29
- serialiseCdpRedeemer,
30
- } from './types';
31
- import { parsePriceOracleDatum } from '../price-oracle/types';
32
- import { parseInterestOracleDatum } from '../interest-oracle/types';
33
- import { parseGovDatumOrThrow } from '../gov/types';
34
27
  import {
35
28
  calculateAccruedInterest,
36
29
  calculateUnitaryInterestSinceOracleLastUpdated,
37
- computeInterestLovelacesFor100PercentCR,
38
30
  } from '../interest-oracle/helpers';
39
31
  import { oracleExpirationAwareValidity } from '../price-oracle/helpers';
40
32
  import { match, P } from 'ts-pattern';
41
- import { serialiseCDPCreatorRedeemer } from '../cdp-creator/types';
42
- import {
43
- assetClassValueOf,
44
- lovelacesAmt,
45
- mkAssetsOf,
46
- mkLovelacesOf,
47
- } from '../../utils/value-helpers';
33
+
48
34
  import { calculateMinCollateralCappedIAssetRedemptionAmt } from './helpers';
49
- import { bigintMin } from '../../utils/bigint-utils';
50
- import { ocdMul } from '../../types/on-chain-decimal';
35
+ import { bigintMax, bigintMin } from '../../utils/bigint-utils';
51
36
  import {
52
- parseStabilityPoolDatum,
37
+ parseStabilityPoolDatumOrThrow,
53
38
  serialiseStabilityPoolDatum,
54
39
  serialiseStabilityPoolRedeemer,
55
40
  } from '../stability-pool/types-new';
56
41
  import { liquidationHelper } from '../stability-pool/helpers';
57
- import { array as A, function as F } from 'fp-ts';
58
- import { calculateFeeFromPercentage } from '../../utils/indigo-helpers';
59
- import { collectorFeeTx } from '../collector/transactions';
42
+ import { array as A, function as F, option as O } from 'fp-ts';
43
+ import { calculateFeeFromRatio } from '../../utils/indigo-helpers';
44
+ import { collectInterestTx } from '../interest-collection/transactions';
45
+ import {
46
+ CDPContent,
47
+ parseCdpDatumOrThrow,
48
+ serialiseCdpDatum,
49
+ serialiseCdpRedeemer,
50
+ serialiseRedeemCdpWithdrawalRedeemer,
51
+ } from './types-new';
52
+ import { parseGovDatumOrThrow } from '../gov/types-new';
53
+ import {
54
+ adaAssetClass,
55
+ assetClassValueOf,
56
+ mkAssetsOf,
57
+ negateAssets,
58
+ } from '@3rd-eye-labs/cardano-offchain-common';
59
+ import { parsePriceOracleDatum } from '../price-oracle/types-new';
60
+ import { parseInterestOracleDatum } from '../interest-oracle/types-new';
61
+ import {
62
+ serialiseCDPCreatorDatum,
63
+ serialiseCDPCreatorRedeemer,
64
+ } from '../cdp-creator/types-new';
65
+ import {
66
+ parseCollateralAssetDatumOrThrow,
67
+ parseIAssetDatumOrThrow,
68
+ } from '../iasset/types';
60
69
  import { treasuryFeeTx } from '../treasury/transactions';
70
+ import { attachOracle } from '../iasset/helpers';
71
+ import {
72
+ rationalFloor,
73
+ rationalFromInt,
74
+ rationalMul,
75
+ } from '../../types/rational';
76
+ import { retrieveAdjustedPrice } from '../../utils/oracle-helpers';
61
77
 
62
78
  export async function openCdp(
63
79
  collateralAmount: bigint,
64
80
  mintedAmount: bigint,
65
81
  sysParams: SystemParams,
66
- cdpCreator: UtxoOrOutRef,
67
- iasset: UtxoOrOutRef,
68
- priceOracle: UtxoOrOutRef,
69
- interestOracle: UtxoOrOutRef,
70
- collector: UtxoOrOutRef,
82
+ cdpCreatorOref: OutRef,
83
+ iassetOref: OutRef,
84
+ collateralAssetOref: OutRef,
85
+ priceOracleOref: OutRef | undefined,
86
+ interestOracleOref: OutRef,
87
+ treasuryOref: OutRef,
71
88
  lucid: LucidEvolution,
72
89
  currentSlot: number,
90
+ pythMessage: string | undefined = undefined,
91
+ pythStateOref: OutRef | undefined = undefined,
73
92
  ): Promise<TxBuilder> {
74
93
  const network = lucid.config().network!;
75
94
  const currentTime = BigInt(slotToUnixTime(network, currentSlot));
@@ -101,40 +120,36 @@ export async function openCdp(
101
120
  (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
102
121
  );
103
122
 
104
- const iassetUtxo = await resolveUtxo(
105
- iasset,
106
- lucid,
107
- 'Expected a single iasset UTXO',
123
+ const iassetUtxo = matchSingle(
124
+ await lucid.utxosByOutRef([iassetOref]),
125
+ (_) => new Error('Expected a single iasset UTXO'),
108
126
  );
109
127
  const iassetDatum = parseIAssetDatumOrThrow(
110
128
  getInlineDatumOrThrow(iassetUtxo),
111
129
  );
112
130
 
113
- const priceOracleUtxo = await resolveUtxo(
114
- priceOracle,
115
- lucid,
116
- 'Expected a single price oracle UTXO',
131
+ const collateralAssetUtxo = matchSingle(
132
+ await lucid.utxosByOutRef([collateralAssetOref]),
133
+ (_) => new Error('Expected a single collateral asset UTXO'),
117
134
  );
118
- const priceOracleDatum = parsePriceOracleDatum(
119
- getInlineDatumOrThrow(priceOracleUtxo),
135
+ const collateralAssetDatum = parseCollateralAssetDatumOrThrow(
136
+ getInlineDatumOrThrow(collateralAssetUtxo),
120
137
  );
121
138
 
122
- const interestOracleUtxo = await resolveUtxo(
123
- interestOracle,
124
- lucid,
125
- 'Expected a single interest oracle UTXO',
139
+ const interestOracleUtxo = matchSingle(
140
+ await lucid.utxosByOutRef([interestOracleOref]),
141
+ (_) => new Error('Expected a single interest oracle UTXO'),
126
142
  );
127
143
  const interestOracleDatum = parseInterestOracleDatum(
128
144
  getInlineDatumOrThrow(interestOracleUtxo),
129
145
  );
130
146
 
131
- const cdpCreatorUtxo = await resolveUtxo(
132
- cdpCreator,
133
- lucid,
134
- 'Expected a single CDP creator UTXO',
147
+ const cdpCreatorUtxo = matchSingle(
148
+ await lucid.utxosByOutRef([cdpCreatorOref]),
149
+ (_) => new Error('Expected a single CDP creator UTXO'),
135
150
  );
136
151
 
137
- match(iassetDatum.price)
152
+ match(collateralAssetDatum.priceInfo)
138
153
  .with({ Delisted: P.any }, () => {
139
154
  throw new Error("Can't open CDP of delisted asset");
140
155
  })
@@ -145,50 +160,41 @@ export async function openCdp(
145
160
  1n,
146
161
  );
147
162
 
148
- const iassetTokensVal = mkAssetsOf(
149
- {
150
- currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
151
- tokenName: iassetDatum.assetName,
152
- },
153
- mintedAmount,
154
- );
163
+ const iassetClass = {
164
+ currencySymbol: fromHex(
165
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
166
+ ),
167
+ tokenName: iassetDatum.assetName,
168
+ };
155
169
 
156
- const txValidity = oracleExpirationAwareValidity(
157
- currentSlot,
158
- Number(sysParams.cdpCreatorParams.biasTime),
159
- Number(priceOracleDatum.expiration),
160
- network,
161
- );
170
+ const iassetTokensVal = mkAssetsOf(iassetClass, mintedAmount);
171
+
172
+ const refScripts: UTxO[] = [
173
+ cdpCreatorRefScriptUtxo,
174
+ cdpAuthTokenPolicyRefScriptUtxo,
175
+ iAssetTokenPolicyRefScriptUtxo,
176
+ ];
177
+
178
+ const referenceInputs: UTxO[] = [
179
+ interestOracleUtxo,
180
+ iassetUtxo,
181
+ collateralAssetUtxo,
182
+ ];
162
183
 
163
184
  const tx = lucid
164
185
  .newTx()
165
- .validFrom(txValidity.validFrom)
166
- .validTo(txValidity.validTo)
167
- .readFrom([
168
- cdpCreatorRefScriptUtxo,
169
- cdpAuthTokenPolicyRefScriptUtxo,
170
- iAssetTokenPolicyRefScriptUtxo,
171
- ])
186
+ // Ref scripts
187
+ .readFrom(refScripts)
172
188
  .mintAssets(cdpNftVal, Data.void())
173
189
  .mintAssets(iassetTokensVal, Data.void())
174
- .collectFrom(
175
- [cdpCreatorUtxo],
176
- serialiseCDPCreatorRedeemer({
177
- CreateCDP: {
178
- cdpOwner: pkh.hash,
179
- minted: mintedAmount,
180
- collateral: collateralAmount,
181
- currentTime: currentTime,
182
- },
183
- }),
184
- )
185
190
  .pay.ToContract(
186
191
  createScriptAddress(network, sysParams.validatorHashes.cdpHash, skh),
187
192
  {
188
193
  kind: 'inline',
189
194
  value: serialiseCdpDatum({
190
- cdpOwner: pkh.hash,
195
+ cdpOwner: fromHex(pkh.hash),
191
196
  iasset: iassetDatum.assetName,
197
+ collateralAsset: collateralAssetDatum.collateralAsset,
192
198
  mintedAmt: mintedAmount,
193
199
  cdpFees: {
194
200
  ActiveCDPInterestTracking: {
@@ -202,41 +208,115 @@ export async function openCdp(
202
208
  },
203
209
  }),
204
210
  },
205
- addAssets(cdpNftVal, mkLovelacesOf(collateralAmount)),
211
+ addAssets(
212
+ cdpNftVal,
213
+ mkAssetsOf(collateralAssetDatum.collateralAsset, collateralAmount),
214
+ ),
206
215
  )
207
216
  .pay.ToContract(
208
217
  cdpCreatorUtxo.address,
209
- { kind: 'inline', value: Data.void() },
218
+ {
219
+ kind: 'inline',
220
+ value: serialiseCDPCreatorDatum({
221
+ creatorInputOref: {
222
+ outputIndex: BigInt(cdpCreatorUtxo.outputIndex),
223
+ txHash: fromHex(cdpCreatorUtxo.txHash),
224
+ },
225
+ }),
226
+ },
210
227
  cdpCreatorUtxo.assets,
211
- )
212
- .readFrom([priceOracleUtxo, interestOracleUtxo, iassetUtxo])
213
- .addSignerKey(pkh.hash);
228
+ );
214
229
 
215
- const debtMintingFee = calculateFeeFromPercentage(
216
- iassetDatum.debtMintingFeePercentage,
217
- (mintedAmount * priceOracleDatum.price.getOnChainInt) / 1_000_000n,
230
+ const { interval, referenceInputs: refInputs } = await attachOracle(
231
+ iassetDatum.assetName,
232
+ collateralAssetDatum.collateralAsset,
233
+ collateralAssetDatum.priceInfo,
234
+ priceOracleOref,
235
+ pythStateOref,
236
+ pythMessage,
237
+ sysParams.pythConfig,
238
+ sysParams.cdpCreatorParams.biasTime,
239
+ currentSlot,
240
+ lucid,
241
+ tx,
218
242
  );
219
243
 
220
- if (debtMintingFee > 0) {
221
- await collectorFeeTx(debtMintingFee, lucid, sysParams, tx, collector);
222
- }
244
+ // Set the validity interval for the transaction
245
+ tx.validFrom(interval.validFrom).validTo(interval.validTo);
246
+
247
+ // Read from the reference inputs
248
+ referenceInputs.push(...refInputs);
249
+
250
+ tx.readFrom(referenceInputs);
251
+
252
+ const debtMintingFee = calculateFeeFromRatio(
253
+ iassetDatum.debtMintingFeeRatio,
254
+ mintedAmount,
255
+ );
256
+
257
+ const treasuryRefScriptUtxo =
258
+ debtMintingFee > 0
259
+ ? await treasuryFeeTx(
260
+ iassetClass,
261
+ debtMintingFee,
262
+ 0n,
263
+ lucid,
264
+ sysParams,
265
+ tx,
266
+ cdpCreatorUtxo,
267
+ treasuryOref,
268
+ )
269
+ : undefined;
270
+
271
+ // We need to take into account the treasury ref script as well.
272
+ const refInputsIndices = getInputIndices(referenceInputs, [
273
+ ...referenceInputs,
274
+ ...refScripts,
275
+ ...(treasuryRefScriptUtxo != null ? [treasuryRefScriptUtxo] : []),
276
+ ]);
277
+
278
+ tx.collectFrom([cdpCreatorUtxo], {
279
+ kind: 'self',
280
+ makeRedeemer: (inputIdx) => {
281
+ return serialiseCDPCreatorRedeemer({
282
+ CreateCDP: {
283
+ cdpOwner: fromHex(pkh.hash),
284
+ minted: mintedAmount,
285
+ collateralAmt: collateralAmount,
286
+ currentTime: currentTime,
287
+ creatorInputIdx: inputIdx,
288
+ creatorOutputIdx: 1n,
289
+ cdpOutputIdx: 0n,
290
+ iassetRefInputIdx: refInputsIndices[1],
291
+ collateralAssetRefInputIdx: refInputsIndices[2],
292
+ interestOracleRefInputIdx: refInputsIndices[0],
293
+ priceOracleIdx:
294
+ priceOracleOref !== undefined
295
+ ? { OracleRefInputIdx: refInputsIndices[3] }
296
+ : 'OracleVoid',
297
+ },
298
+ });
299
+ },
300
+ });
223
301
 
224
302
  return tx;
225
303
  }
226
304
 
227
- async function adjustCdp(
228
- collateralAmount: bigint,
229
- mintAmount: bigint,
230
- cdp: UtxoOrOutRef,
231
- iasset: UtxoOrOutRef,
232
- priceOracle: UtxoOrOutRef,
233
- interestOracle: UtxoOrOutRef,
234
- collector: UtxoOrOutRef,
235
- gov: UtxoOrOutRef,
236
- treasury: UtxoOrOutRef,
305
+ export async function adjustCdp(
306
+ collateralAdjustment: bigint,
307
+ debtAdjustment: bigint,
308
+ cdpOref: OutRef,
309
+ iassetOref: OutRef,
310
+ collateralAssetOref: OutRef,
311
+ priceOracleOref: OutRef | undefined,
312
+ interestOracleOref: OutRef,
313
+ treasuryOref: OutRef,
314
+ interestCollectorOref: OutRef,
237
315
  sysParams: SystemParams,
238
316
  lucid: LucidEvolution,
239
317
  currentSlot: number,
318
+ pythMessage: string | undefined = undefined,
319
+ pythStateOref: OutRef | undefined = undefined,
240
320
  ): Promise<TxBuilder> {
241
321
  const network = lucid.config().network!;
242
322
  const currentTime = BigInt(slotToUnixTime(network, currentSlot));
@@ -248,97 +328,178 @@ async function adjustCdp(
248
328
  (_) => new Error('Expected a single cdp Ref Script UTXO'),
249
329
  );
250
330
 
251
- const cdpUtxo = await resolveUtxo(cdp, lucid, 'Expected a single cdp UTXO');
331
+ const cdpUtxo = matchSingle(
332
+ await lucid.utxosByOutRef([cdpOref]),
333
+ (_) => new Error('Expected a single cdp UTXO'),
334
+ );
252
335
  const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
253
336
 
254
- const iassetUtxo = await resolveUtxo(
255
- iasset,
256
- lucid,
257
- 'Expected a single iasset UTXO',
337
+ const iassetUtxo = matchSingle(
338
+ await lucid.utxosByOutRef([iassetOref]),
339
+ (_) => new Error('Expected a single iasset UTXO'),
258
340
  );
341
+
259
342
  const iassetDatum = parseIAssetDatumOrThrow(
260
343
  getInlineDatumOrThrow(iassetUtxo),
261
344
  );
262
345
 
263
- const govUtxo = await resolveUtxo(gov, lucid, 'Expected a single gov UTXO');
264
- const govDatum = parseGovDatumOrThrow(getInlineDatumOrThrow(govUtxo));
265
-
266
- const priceOracleUtxo = await resolveUtxo(
267
- priceOracle,
268
- lucid,
269
- 'Expected a single price oracle UTXO',
346
+ const collateralAssetUtxo = matchSingle(
347
+ await lucid.utxosByOutRef([collateralAssetOref]),
348
+ (_) => new Error('Expected a single collateral asset UTXO'),
270
349
  );
271
- const priceOracleDatum = parsePriceOracleDatum(
272
- getInlineDatumOrThrow(priceOracleUtxo),
350
+ const collateralAssetDatum = parseCollateralAssetDatumOrThrow(
351
+ getInlineDatumOrThrow(collateralAssetUtxo),
273
352
  );
274
353
 
275
- const interestOracleUtxo = await resolveUtxo(
276
- interestOracle,
277
- lucid,
278
- 'Expected a single interest oracle UTXO',
354
+ const iAssetAc = {
355
+ currencySymbol: fromHex(
356
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
357
+ ),
358
+ tokenName: iassetDatum.assetName,
359
+ };
360
+
361
+ const isMintOrWithdraw = debtAdjustment > 0n || collateralAdjustment < 0n;
362
+
363
+ const interestOracleUtxo = matchSingle(
364
+ await lucid.utxosByOutRef([interestOracleOref]),
365
+ (_) => new Error('Expected a single interest oracle UTXO'),
279
366
  );
280
367
  const interestOracleDatum = parseInterestOracleDatum(
281
368
  getInlineDatumOrThrow(interestOracleUtxo),
282
369
  );
283
370
 
284
- match(iassetDatum.price)
285
- .with({ Delisted: P.any }, () => {
286
- throw new Error("Can't adjust CDP of delisted asset");
287
- })
288
- .otherwise(() => {});
289
-
290
- const txValidity = oracleExpirationAwareValidity(
291
- currentSlot,
292
- Number(sysParams.cdpCreatorParams.biasTime),
293
- Number(priceOracleDatum.expiration),
294
- network,
295
- );
296
-
297
- const interestAdaAmt = match(cdpDatum.cdpFees)
371
+ const interestAmt = match(cdpDatum.cdpFees)
298
372
  .with({ FrozenCDPAccumulatedFees: P.any }, () => {
299
373
  throw new Error('CDP fees wrong');
300
374
  })
301
375
  .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
302
- const interestPaymentIAssetAmt = calculateAccruedInterest(
376
+ return calculateAccruedInterest(
303
377
  currentTime,
304
378
  interest.unitaryInterestSnapshot,
305
379
  cdpDatum.mintedAmt,
306
380
  interest.lastSettled,
307
381
  interestOracleDatum,
308
382
  );
309
-
310
- return (
311
- (interestPaymentIAssetAmt * priceOracleDatum.price.getOnChainInt) /
312
- 1_000_000n
313
- );
314
383
  })
315
384
  .exhaustive();
316
385
 
317
- const newCollateralAmount = collateralAmount - interestAdaAmt;
386
+ const mintedAmountChange = debtAdjustment + interestAmt;
318
387
 
319
- const tx = lucid
320
- .newTx()
321
- .validFrom(txValidity.validFrom)
322
- .validTo(txValidity.validTo)
388
+ const referenceScripts = [cdpRefScriptUtxo];
389
+ let referenceInputs = [iassetUtxo, collateralAssetUtxo, interestOracleUtxo];
390
+
391
+ let treasuryFee = 0n;
392
+
393
+ const tx = lucid.newTx().readFrom(referenceScripts);
394
+
395
+ if (isMintOrWithdraw) {
396
+ const { interval, referenceInputs: oracleRefInputs } = await attachOracle(
397
+ iassetDatum.assetName,
398
+ collateralAssetDatum.collateralAsset,
399
+ collateralAssetDatum.priceInfo,
400
+ priceOracleOref,
401
+ pythStateOref,
402
+ pythMessage,
403
+ sysParams.pythConfig,
404
+ sysParams.cdpParams.biasTime,
405
+ currentSlot,
406
+ lucid,
407
+ tx,
408
+ );
409
+
410
+ referenceInputs = [...oracleRefInputs, ...referenceInputs];
411
+
412
+ // Set the validity interval for the transaction
413
+ tx.validFrom(interval.validFrom).validTo(interval.validTo);
414
+ } else {
415
+ // Set the validity interval for the transaction
416
+ tx.validFrom(
417
+ Number(currentTime - BigInt(sysParams.cdpParams.biasTime) / 2n),
418
+ ).validTo(Number(currentTime + BigInt(sysParams.cdpParams.biasTime) / 2n));
419
+ }
420
+
421
+ // when mint
422
+ if (debtAdjustment > 0n) {
423
+ treasuryFee += calculateFeeFromRatio(
424
+ iassetDatum.debtMintingFeeRatio,
425
+ debtAdjustment,
426
+ );
427
+ }
428
+
429
+ const treasuryRefScriptUtxo =
430
+ treasuryFee > 0
431
+ ? await treasuryFeeTx(
432
+ iAssetAc,
433
+ treasuryFee,
434
+ 0n,
435
+ lucid,
436
+ sysParams,
437
+ tx,
438
+ cdpUtxo,
439
+ treasuryOref,
440
+ )
441
+ : undefined;
442
+
443
+ const interestCollectorRefScriptUtxo =
444
+ interestAmt > 0n
445
+ ? await collectInterestTx(
446
+ mkAssetsOf(iAssetAc, interestAmt),
447
+ lucid,
448
+ sysParams,
449
+ tx,
450
+ interestCollectorOref,
451
+ )
452
+ : undefined;
453
+
454
+ const iAssetTokenPolicyRefScriptUtxo =
455
+ mintedAmountChange !== 0n
456
+ ? matchSingle(
457
+ await lucid.utxosByOutRef([
458
+ fromSystemParamsScriptRef(
459
+ sysParams.scriptReferences.iAssetTokenPolicyRef,
460
+ ),
461
+ ]),
462
+ (_) =>
463
+ new Error('Expected a single iasset token policy Ref Script UTXO'),
464
+ )
465
+ : undefined;
466
+
467
+ // We need to take into account the treasury, interest collector
468
+ // and iAsset policy ref scripts as well.
469
+ const refInputsIndices = getInputIndices(referenceInputs, [
470
+ ...referenceInputs,
471
+ ...referenceScripts,
472
+ ...(treasuryRefScriptUtxo != null ? [treasuryRefScriptUtxo] : []),
473
+ ...(interestCollectorRefScriptUtxo != null
474
+ ? [interestCollectorRefScriptUtxo]
475
+ : []),
476
+ ...(iAssetTokenPolicyRefScriptUtxo != null
477
+ ? [iAssetTokenPolicyRefScriptUtxo]
478
+ : []),
479
+ ]);
480
+
481
+ tx.readFrom(referenceInputs)
323
482
  .collectFrom(
324
483
  [cdpUtxo],
325
484
  serialiseCdpRedeemer({
326
485
  AdjustCdp: {
327
- collateralAmtChange: collateralAmount,
328
486
  currentTime: currentTime,
329
- mintedAmtChange: mintAmount,
487
+ debtAdjustment: debtAdjustment,
488
+ collateralAdjustment,
489
+ priceOracleIdx:
490
+ priceOracleOref !== undefined
491
+ ? { OracleRefInputIdx: refInputsIndices[0] }
492
+ : 'OracleVoid',
330
493
  },
331
494
  }),
332
495
  )
333
- .readFrom([cdpRefScriptUtxo])
334
- .readFrom([iassetUtxo, govUtxo, priceOracleUtxo, interestOracleUtxo])
335
496
  .pay.ToContract(
336
497
  cdpUtxo.address,
337
498
  {
338
499
  kind: 'inline',
339
500
  value: serialiseCdpDatum({
340
501
  ...cdpDatum,
341
- mintedAmt: cdpDatum.mintedAmt + mintAmount,
502
+ mintedAmt: cdpDatum.mintedAmt + mintedAmountChange,
342
503
  cdpFees: {
343
504
  ActiveCDPInterestTracking: {
344
505
  lastSettled: currentTime,
@@ -351,83 +512,38 @@ async function adjustCdp(
351
512
  },
352
513
  }),
353
514
  },
354
- addAssets(cdpUtxo.assets, mkLovelacesOf(newCollateralAmount)),
355
- );
356
-
357
- if (!cdpDatum.cdpOwner) {
358
- throw new Error('Expected active CDP');
359
- }
360
-
361
- tx.addSignerKey(cdpDatum.cdpOwner);
362
-
363
- if (mintAmount !== 0n) {
364
- const iAssetTokenPolicyRefScriptUtxo = matchSingle(
365
- await lucid.utxosByOutRef([
366
- fromSystemParamsScriptRef(
367
- sysParams.scriptReferences.iAssetTokenPolicyRef,
368
- ),
369
- ]),
370
- (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
515
+ addAssets(
516
+ cdpUtxo.assets,
517
+ mkAssetsOf(cdpDatum.collateralAsset, collateralAdjustment),
518
+ ),
371
519
  );
372
520
 
373
- const iassetTokensVal = mkAssetsOf(
374
- {
375
- currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
376
- tokenName: iassetDatum.assetName,
377
- },
378
- mintAmount,
379
- );
521
+ if (mintedAmountChange !== 0n) {
522
+ const iassetTokensVal = mkAssetsOf(iAssetAc, mintedAmountChange);
380
523
 
381
- tx.readFrom([iAssetTokenPolicyRefScriptUtxo]).mintAssets(
524
+ tx.readFrom([iAssetTokenPolicyRefScriptUtxo as UTxO]).mintAssets(
382
525
  iassetTokensVal,
383
526
  Data.void(),
384
527
  );
385
528
  }
386
529
 
387
- const interestCollectorAdaAmt = calculateFeeFromPercentage(
388
- iassetDatum.interestCollectorPortionPercentage,
389
- interestAdaAmt,
390
- );
391
- const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
392
-
393
- if (interestTreasuryAdaAmt > 0) {
394
- await treasuryFeeTx(interestTreasuryAdaAmt, lucid, sysParams, tx, treasury);
395
- }
396
-
397
- let collectorFee = interestCollectorAdaAmt;
398
-
399
- // when mint
400
- if (mintAmount > 0n) {
401
- collectorFee += calculateFeeFromPercentage(
402
- iassetDatum.debtMintingFeePercentage,
403
- (mintAmount * priceOracleDatum.price.getOnChainInt) / 1_000_000n,
404
- );
405
- }
406
-
407
- // when withdraw
408
- if (collateralAmount < 0n) {
409
- collectorFee += calculateFeeFromPercentage(
410
- govDatum.protocolParams.collateralFeePercentage,
411
- -collateralAmount,
412
- );
530
+ if (!cdpDatum.cdpOwner) {
531
+ throw new Error('Expected active CDP');
413
532
  }
414
533
 
415
- if (collectorFee > 0n) {
416
- await collectorFeeTx(collectorFee, lucid, sysParams, tx, collector);
417
- }
534
+ tx.addSignerKey(toHex(cdpDatum.cdpOwner));
418
535
 
419
536
  return tx;
420
537
  }
421
538
 
422
539
  export async function depositCdp(
423
540
  amount: bigint,
424
- cdp: UtxoOrOutRef,
425
- iasset: UtxoOrOutRef,
426
- priceOracle: UtxoOrOutRef,
427
- interestOracle: UtxoOrOutRef,
428
- collector: UtxoOrOutRef,
429
- gov: UtxoOrOutRef,
430
- treasury: UtxoOrOutRef,
541
+ cdpOref: OutRef,
542
+ iassetOref: OutRef,
543
+ collateralAssetOref: OutRef,
544
+ interestOracleOref: OutRef,
545
+ treasuryOref: OutRef,
546
+ interestCollectorOref: OutRef,
431
547
  params: SystemParams,
432
548
  lucid: LucidEvolution,
433
549
  currentSlot: number,
@@ -435,13 +551,13 @@ export async function depositCdp(
435
551
  return adjustCdp(
436
552
  amount,
437
553
  0n,
438
- cdp,
439
- iasset,
440
- priceOracle,
441
- interestOracle,
442
- collector,
443
- gov,
444
- treasury,
554
+ cdpOref,
555
+ iassetOref,
556
+ collateralAssetOref,
557
+ undefined,
558
+ interestOracleOref,
559
+ treasuryOref,
560
+ interestCollectorOref,
445
561
  params,
446
562
  lucid,
447
563
  currentSlot,
@@ -450,71 +566,78 @@ export async function depositCdp(
450
566
 
451
567
  export async function withdrawCdp(
452
568
  amount: bigint,
453
- cdp: UtxoOrOutRef,
454
- iasset: UtxoOrOutRef,
455
- priceOracle: UtxoOrOutRef,
456
- interestOracle: UtxoOrOutRef,
457
- collector: UtxoOrOutRef,
458
- gov: UtxoOrOutRef,
459
- treasury: UtxoOrOutRef,
569
+ cdpOref: OutRef,
570
+ iassetOref: OutRef,
571
+ collateralAssetOref: OutRef,
572
+ priceOracleOref: OutRef | undefined,
573
+ interestOracleOref: OutRef,
574
+ treasuryOref: OutRef,
575
+ interestCollectorOref: OutRef,
460
576
  params: SystemParams,
461
577
  lucid: LucidEvolution,
462
578
  currentSlot: number,
579
+ pythMessage: string | undefined = undefined,
580
+ pythStateOref: OutRef | undefined = undefined,
463
581
  ): Promise<TxBuilder> {
464
582
  return adjustCdp(
465
583
  -amount,
466
584
  0n,
467
- cdp,
468
- iasset,
469
- priceOracle,
470
- interestOracle,
471
- collector,
472
- gov,
473
- treasury,
585
+ cdpOref,
586
+ iassetOref,
587
+ collateralAssetOref,
588
+ priceOracleOref,
589
+ interestOracleOref,
590
+ treasuryOref,
591
+ interestCollectorOref,
474
592
  params,
475
593
  lucid,
476
594
  currentSlot,
595
+ pythMessage,
596
+ pythStateOref,
477
597
  );
478
598
  }
479
599
 
480
600
  export async function mintCdp(
481
601
  amount: bigint,
482
- cdp: UtxoOrOutRef,
483
- iasset: UtxoOrOutRef,
484
- priceOracle: UtxoOrOutRef,
485
- interestOracle: UtxoOrOutRef,
486
- collector: UtxoOrOutRef,
487
- gov: UtxoOrOutRef,
488
- treasury: UtxoOrOutRef,
602
+ cdpOref: OutRef,
603
+ iassetOref: OutRef,
604
+ collateralAssetOref: OutRef,
605
+ priceOracleOref: OutRef | undefined,
606
+ interestOracleOref: OutRef,
607
+ treasuryOref: OutRef,
608
+ interestCollectorOref: OutRef,
489
609
  params: SystemParams,
490
610
  lucid: LucidEvolution,
491
611
  currentSlot: number,
612
+ pythMessage: string | undefined = undefined,
613
+ pythStateOref: OutRef | undefined = undefined,
492
614
  ): Promise<TxBuilder> {
493
615
  return adjustCdp(
494
616
  0n,
495
617
  amount,
496
- cdp,
497
- iasset,
498
- priceOracle,
499
- interestOracle,
500
- collector,
501
- gov,
502
- treasury,
618
+ cdpOref,
619
+ iassetOref,
620
+ collateralAssetOref,
621
+ priceOracleOref,
622
+ interestOracleOref,
623
+ treasuryOref,
624
+ interestCollectorOref,
503
625
  params,
504
626
  lucid,
505
627
  currentSlot,
628
+ pythMessage,
629
+ pythStateOref,
506
630
  );
507
631
  }
508
632
 
509
633
  export async function burnCdp(
510
634
  amount: bigint,
511
- cdp: UtxoOrOutRef,
512
- iasset: UtxoOrOutRef,
513
- priceOracle: UtxoOrOutRef,
514
- interestOracle: UtxoOrOutRef,
515
- collector: UtxoOrOutRef,
516
- gov: UtxoOrOutRef,
517
- treasury: UtxoOrOutRef,
635
+ cdpOref: OutRef,
636
+ iassetOref: OutRef,
637
+ collateralAssetOref: OutRef,
638
+ interestOracleOref: OutRef,
639
+ treasuryOref: OutRef,
640
+ interestCollectorOref: OutRef,
518
641
  params: SystemParams,
519
642
  lucid: LucidEvolution,
520
643
  currentSlot: number,
@@ -522,13 +645,13 @@ export async function burnCdp(
522
645
  return adjustCdp(
523
646
  0n,
524
647
  -amount,
525
- cdp,
526
- iasset,
527
- priceOracle,
528
- interestOracle,
529
- collector,
530
- gov,
531
- treasury,
648
+ cdpOref,
649
+ iassetOref,
650
+ collateralAssetOref,
651
+ undefined,
652
+ interestOracleOref,
653
+ treasuryOref,
654
+ interestCollectorOref,
532
655
  params,
533
656
  lucid,
534
657
  currentSlot,
@@ -536,13 +659,10 @@ export async function burnCdp(
536
659
  }
537
660
 
538
661
  export async function closeCdp(
539
- cdp: UtxoOrOutRef,
540
- iasset: UtxoOrOutRef,
541
- priceOracle: UtxoOrOutRef,
542
- interestOracle: UtxoOrOutRef,
543
- collector: UtxoOrOutRef,
544
- gov: UtxoOrOutRef,
545
- treasury: UtxoOrOutRef,
662
+ cdpOref: OutRef,
663
+ collateralAssetOref: OutRef,
664
+ interestOracleOref: OutRef,
665
+ interestCollectorOref: OutRef,
546
666
  sysParams: SystemParams,
547
667
  lucid: LucidEvolution,
548
668
  currentSlot: number,
@@ -575,45 +695,30 @@ export async function closeCdp(
575
695
  (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
576
696
  );
577
697
 
578
- const cdpUtxo = await resolveUtxo(cdp, lucid, 'Expected a single cdp UTXO');
579
- const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
580
-
581
- const iassetUtxo = await resolveUtxo(
582
- iasset,
583
- lucid,
584
- 'Expected a single iasset UTXO',
698
+ const cdpUtxo = matchSingle(
699
+ await lucid.utxosByOutRef([cdpOref]),
700
+ (_) => new Error('Expected a single cdp UTXO'),
585
701
  );
586
- const iassetDatum = parseIAssetDatumOrThrow(
587
- getInlineDatumOrThrow(iassetUtxo),
588
- );
589
-
590
- const govUtxo = await resolveUtxo(gov, lucid, 'Expected a single gov UTXO');
591
- const govDatum = parseGovDatumOrThrow(getInlineDatumOrThrow(govUtxo));
702
+ const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
592
703
 
593
- const priceOracleUtxo = await resolveUtxo(
594
- priceOracle,
595
- lucid,
596
- 'Expected a single price oracle UTXO',
704
+ const collateralAssetUtxo = matchSingle(
705
+ await lucid.utxosByOutRef([collateralAssetOref]),
706
+ (_) => new Error('Expected a single collateral asset UTXO'),
597
707
  );
598
- const priceOracleDatum = parsePriceOracleDatum(
599
- getInlineDatumOrThrow(priceOracleUtxo),
708
+ const collateralDatum = parseCollateralAssetDatumOrThrow(
709
+ getInlineDatumOrThrow(collateralAssetUtxo),
600
710
  );
601
711
 
602
- const interestOracleUtxo = await resolveUtxo(
603
- interestOracle,
604
- lucid,
605
- 'Expected a single interest oracle UTXO',
712
+ const interestOracleUtxo = matchSingle(
713
+ await lucid.utxosByOutRef([interestOracleOref]),
714
+ (_) => new Error('Expected a single interest oracle UTXO'),
606
715
  );
607
716
  const interestOracleDatum = parseInterestOracleDatum(
608
717
  getInlineDatumOrThrow(interestOracleUtxo),
609
718
  );
610
719
 
611
- const txValidity = oracleExpirationAwareValidity(
612
- currentSlot,
613
- Number(sysParams.cdpCreatorParams.biasTime),
614
- Number(priceOracleDatum.expiration),
615
- network,
616
- );
720
+ const validateFrom = slotToUnixTime(network, currentSlot - 1);
721
+ const validateTo = validateFrom + Number(sysParams.cdpCreatorParams.biasTime);
617
722
 
618
723
  const tx = lucid
619
724
  .newTx()
@@ -622,14 +727,16 @@ export async function closeCdp(
622
727
  iAssetTokenPolicyRefScriptUtxo,
623
728
  cdpAuthTokenPolicyRefScriptUtxo,
624
729
  ])
625
- .readFrom([iassetUtxo, govUtxo, priceOracleUtxo, interestOracleUtxo])
626
- .validFrom(txValidity.validFrom)
627
- .validTo(txValidity.validTo)
730
+ .readFrom([collateralAssetUtxo, interestOracleUtxo])
731
+ .validFrom(validateFrom)
732
+ .validTo(validateTo)
628
733
  .mintAssets(
629
734
  mkAssetsOf(
630
735
  {
631
- currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
632
- tokenName: iassetDatum.assetName,
736
+ currencySymbol: fromHex(
737
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
738
+ ),
739
+ tokenName: collateralDatum.iasset,
633
740
  },
634
741
  -cdpDatum.mintedAmt,
635
742
  ),
@@ -648,47 +755,39 @@ export async function closeCdp(
648
755
  throw new Error('Expected active CDP');
649
756
  }
650
757
 
651
- tx.addSignerKey(cdpDatum.cdpOwner);
758
+ tx.addSignerKey(toHex(cdpDatum.cdpOwner));
652
759
 
653
- const interestAdaAmt = match(cdpDatum.cdpFees)
760
+ const interestAmt = match(cdpDatum.cdpFees)
654
761
  .with({ FrozenCDPAccumulatedFees: P.any }, () => {
655
762
  throw new Error('CDP fees wrong');
656
763
  })
657
764
  .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
658
- const interestPaymentIAssetAmt = calculateAccruedInterest(
765
+ return calculateAccruedInterest(
659
766
  currentTime,
660
767
  interest.unitaryInterestSnapshot,
661
768
  cdpDatum.mintedAmt,
662
769
  interest.lastSettled,
663
770
  interestOracleDatum,
664
771
  );
665
-
666
- return (
667
- (interestPaymentIAssetAmt * priceOracleDatum.price.getOnChainInt) /
668
- 1_000_000n
669
- );
670
772
  })
671
773
  .exhaustive();
672
774
 
673
- const interestCollectorAdaAmt = calculateFeeFromPercentage(
674
- iassetDatum.interestCollectorPortionPercentage,
675
- interestAdaAmt,
676
- );
677
- const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
678
-
679
- if (interestTreasuryAdaAmt > 0) {
680
- await treasuryFeeTx(interestTreasuryAdaAmt, lucid, sysParams, tx, treasury);
681
- }
682
-
683
- const collectorFee =
684
- interestCollectorAdaAmt +
685
- calculateFeeFromPercentage(
686
- govDatum.protocolParams.collateralFeePercentage,
687
- lovelacesAmt(cdpUtxo.assets) - interestAdaAmt,
775
+ if (interestAmt > 0n) {
776
+ await collectInterestTx(
777
+ mkAssetsOf(
778
+ {
779
+ currencySymbol: fromHex(
780
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
781
+ ),
782
+ tokenName: collateralDatum.iasset,
783
+ },
784
+ interestAmt,
785
+ ),
786
+ lucid,
787
+ sysParams,
788
+ tx,
789
+ interestCollectorOref,
688
790
  );
689
-
690
- if (collectorFee > 0n) {
691
- await collectorFeeTx(collectorFee, lucid, sysParams, tx, collector);
692
791
  }
693
792
 
694
793
  return tx;
@@ -700,15 +799,19 @@ export async function redeemCdp(
700
799
  * The logic will automatically cap the amount to the max.
701
800
  */
702
801
  attemptedRedemptionIAssetAmt: bigint,
703
- cdp: UtxoOrOutRef,
704
- iasset: UtxoOrOutRef,
705
- priceOracle: UtxoOrOutRef,
706
- interestOracle: UtxoOrOutRef,
707
- collector: UtxoOrOutRef,
708
- treasury: UtxoOrOutRef,
802
+ cdpOref: OutRef,
803
+ iassetOref: OutRef,
804
+ collateralAssetOref: OutRef,
805
+ priceOracleOref: OutRef | undefined,
806
+ interestOracleOref: OutRef,
807
+ interestCollectorOref: OutRef,
808
+ treasuryOref: OutRef,
809
+ govOref: OutRef,
709
810
  sysParams: SystemParams,
710
811
  lucid: LucidEvolution,
711
812
  currentSlot: number,
813
+ pythMessage: string | undefined = undefined,
814
+ _pythStateOref: OutRef | undefined = undefined,
712
815
  ): Promise<TxBuilder> {
713
816
  const network = lucid.config().network!;
714
817
  const currentTime = BigInt(slotToUnixTime(network, currentSlot));
@@ -720,6 +823,15 @@ export async function redeemCdp(
720
823
  (_) => new Error('Expected a single cdp Ref Script UTXO'),
721
824
  );
722
825
 
826
+ const cdpRedeemRefScriptUtxo = matchSingle(
827
+ await lucid.utxosByOutRef([
828
+ fromSystemParamsScriptRef(
829
+ sysParams.scriptReferences.cdpRedeemValidatorRef,
830
+ ),
831
+ ]),
832
+ (_) => new Error('Expected a single cdp redeem Ref Script UTXO'),
833
+ );
834
+
723
835
  const iAssetTokenPolicyRefScriptUtxo = matchSingle(
724
836
  await lucid.utxosByOutRef([
725
837
  fromSystemParamsScriptRef(
@@ -729,73 +841,91 @@ export async function redeemCdp(
729
841
  (_) => new Error('Expected a single iasset token policy Ref Script UTXO'),
730
842
  );
731
843
 
732
- const cdpUtxo = await resolveUtxo(cdp, lucid, 'Expected a single cdp UTXO');
844
+ const cdpUtxo = matchSingle(
845
+ await lucid.utxosByOutRef([cdpOref]),
846
+ (_) => new Error('Expected a single cdp UTXO'),
847
+ );
733
848
  const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
734
849
 
735
- const iassetUtxo = await resolveUtxo(
736
- iasset,
737
- lucid,
738
- 'Expected a single iasset UTXO',
850
+ const iassetUtxo = matchSingle(
851
+ await lucid.utxosByOutRef([iassetOref]),
852
+ (_) => new Error('Expected a single iasset UTXO'),
739
853
  );
740
854
  const iassetDatum = parseIAssetDatumOrThrow(
741
855
  getInlineDatumOrThrow(iassetUtxo),
742
856
  );
743
857
 
744
- const priceOracleUtxo = await resolveUtxo(
745
- priceOracle,
746
- lucid,
747
- 'Expected a single price oracle UTXO',
858
+ const collateralAssetUtxo = matchSingle(
859
+ await lucid.utxosByOutRef([collateralAssetOref]),
860
+ (_) => new Error('Expected a single collateral asset UTXO'),
748
861
  );
749
- const priceOracleDatum = parsePriceOracleDatum(
750
- getInlineDatumOrThrow(priceOracleUtxo),
862
+ const collateralAssetDatum = parseCollateralAssetDatumOrThrow(
863
+ getInlineDatumOrThrow(collateralAssetUtxo),
751
864
  );
752
865
 
753
- const interestOracleUtxo = await resolveUtxo(
754
- interestOracle,
866
+ const isDelisted = match(collateralAssetDatum.priceInfo)
867
+ .with({ Delisted: P.any }, () => true)
868
+ .otherwise(() => false);
869
+
870
+ if (!isDelisted && priceOracleOref === undefined) {
871
+ throw new Error('Missing price oracle');
872
+ }
873
+
874
+ const [adjustedPrice, priceOracleUtxo] = await retrieveAdjustedPrice(
875
+ iassetDatum.assetName,
876
+ collateralAssetDatum.collateralAsset,
877
+ collateralAssetDatum.priceInfo,
878
+ collateralAssetDatum.extraDecimals,
879
+ priceOracleOref,
880
+ pythMessage,
881
+ sysParams.pythConfig,
755
882
  lucid,
756
- 'Expected a single interest oracle UTXO',
883
+ );
884
+
885
+ const interestOracleUtxo = matchSingle(
886
+ await lucid.utxosByOutRef([interestOracleOref]),
887
+ (_) => new Error('Expected a single interest oracle UTXO'),
757
888
  );
758
889
  const interestOracleDatum = parseInterestOracleDatum(
759
890
  getInlineDatumOrThrow(interestOracleUtxo),
760
891
  );
761
892
 
762
- const interestAdaAmt = match(cdpDatum.cdpFees)
893
+ const govUtxo = matchSingle(
894
+ await lucid.utxosByOutRef([govOref]),
895
+ (_) => new Error('Expected a single gov UTXO'),
896
+ );
897
+ const govDatum = parseGovDatumOrThrow(getInlineDatumOrThrow(govUtxo));
898
+
899
+ const interestAmt = match(cdpDatum.cdpFees)
763
900
  .with({ FrozenCDPAccumulatedFees: P.any }, () => {
764
901
  throw new Error('CDP fees wrong');
765
902
  })
766
903
  .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
767
- const interestPaymentIAssetAmt = calculateAccruedInterest(
904
+ return calculateAccruedInterest(
768
905
  currentTime,
769
906
  interest.unitaryInterestSnapshot,
770
907
  cdpDatum.mintedAmt,
771
908
  interest.lastSettled,
772
909
  interestOracleDatum,
773
910
  );
774
-
775
- return ocdMul(
776
- { getOnChainInt: interestPaymentIAssetAmt },
777
- priceOracleDatum.price,
778
- ).getOnChainInt;
779
911
  })
780
912
  .exhaustive();
781
913
 
782
- const interestCollectorAdaAmt = calculateFeeFromPercentage(
783
- iassetDatum.interestCollectorPortionPercentage,
784
- interestAdaAmt,
914
+ const collateralAmt = assetClassValueOf(
915
+ cdpUtxo.assets,
916
+ cdpDatum.collateralAsset,
785
917
  );
786
- const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
787
918
 
788
- const collateralAmtMinusInterest =
789
- lovelacesAmt(cdpUtxo.assets) - interestAdaAmt;
919
+ const totalCdpDebt = cdpDatum.mintedAmt + interestAmt;
790
920
 
791
921
  const [isPartial, redemptionIAssetAmt] = (() => {
792
922
  const res = calculateMinCollateralCappedIAssetRedemptionAmt(
793
- collateralAmtMinusInterest,
794
- cdpDatum.mintedAmt,
795
- priceOracleDatum.price,
796
- iassetDatum.redemptionRatio,
797
- iassetDatum.redemptionReimbursementPercentage,
798
- BigInt(sysParams.cdpParams.minCollateralInLovelace),
923
+ collateralAmt,
924
+ totalCdpDebt,
925
+ adjustedPrice,
926
+ collateralAssetDatum.redemptionRatio,
927
+ iassetDatum.redemptionReimbursementRatio,
928
+ BigInt(collateralAssetDatum.minCollateralAmt),
799
929
  );
800
930
 
801
931
  const redemptionAmt = bigintMin(
@@ -810,50 +940,162 @@ export async function redeemCdp(
810
940
  throw new Error("There's no iAssets available for redemption.");
811
941
  }
812
942
 
813
- const redemptionLovelacesAmt = ocdMul(priceOracleDatum.price, {
814
- getOnChainInt: redemptionIAssetAmt,
815
- }).getOnChainInt;
816
-
817
- const partialRedemptionFee = isPartial
818
- ? BigInt(sysParams.cdpParams.partialRedemptionExtraFeeLovelace)
819
- : 0n;
943
+ const redemptionCollateralAmt = rationalFloor(
944
+ rationalMul(adjustedPrice, rationalFromInt(redemptionIAssetAmt)),
945
+ );
820
946
 
821
- const processingFee = calculateFeeFromPercentage(
822
- iassetDatum.redemptionProcessingFeePercentage,
823
- redemptionLovelacesAmt,
947
+ const processingFee = calculateFeeFromRatio(
948
+ iassetDatum.redemptionProcessingFeeRatio,
949
+ redemptionCollateralAmt,
824
950
  );
825
951
 
826
- const reimburstmentFee = calculateFeeFromPercentage(
827
- iassetDatum.redemptionReimbursementPercentage,
828
- redemptionLovelacesAmt,
952
+ const reimburstmentFee = calculateFeeFromRatio(
953
+ iassetDatum.redemptionReimbursementRatio,
954
+ redemptionCollateralAmt,
829
955
  );
830
956
 
831
- const txValidity = oracleExpirationAwareValidity(
832
- currentSlot,
833
- Number(sysParams.cdpCreatorParams.biasTime),
834
- Number(priceOracleDatum.expiration),
835
- network,
957
+ const referenceScripts = [
958
+ cdpRefScriptUtxo,
959
+ iAssetTokenPolicyRefScriptUtxo,
960
+ cdpRedeemRefScriptUtxo,
961
+ ];
962
+
963
+ const referenceInputs = [
964
+ iassetUtxo,
965
+ collateralAssetUtxo,
966
+ interestOracleUtxo,
967
+ govUtxo,
968
+ ];
969
+
970
+ const tx = lucid.newTx().readFrom(referenceScripts);
971
+
972
+ if (priceOracleUtxo !== undefined) {
973
+ const priceOracleDatum = parsePriceOracleDatum(
974
+ getInlineDatumOrThrow(priceOracleUtxo),
975
+ );
976
+
977
+ const txValidity = oracleExpirationAwareValidity(
978
+ currentSlot,
979
+ Number(sysParams.cdpCreatorParams.biasTime),
980
+ Number(priceOracleDatum.expirationTime),
981
+ network,
982
+ );
983
+
984
+ referenceInputs.push(priceOracleUtxo);
985
+
986
+ tx.validFrom(txValidity.validFrom).validTo(txValidity.validTo);
987
+ } else {
988
+ const validateFrom = slotToUnixTime(network, currentSlot - 1);
989
+ const validateTo =
990
+ validateFrom + Number(sysParams.cdpCreatorParams.biasTime);
991
+
992
+ tx.validFrom(validateFrom).validTo(validateTo);
993
+ }
994
+
995
+ const partialRedemptionFee = F.pipe(
996
+ govDatum.protocolParams.cdpRedemptionRequiredSignature,
997
+ O.fromNullable,
998
+ O.match(
999
+ // When public redemptions
1000
+ () => {
1001
+ return isPartial
1002
+ ? BigInt(sysParams.cdpRedeemParams.partialRedemptionExtraFeeLovelace)
1003
+ : 0n;
1004
+ },
1005
+ // When private redemptions
1006
+ (requiredSignature) => {
1007
+ tx.addSignerKey(toHex(requiredSignature));
1008
+ return 0n;
1009
+ },
1010
+ ),
836
1011
  );
837
1012
 
838
- const tx = lucid
839
- .newTx()
840
- // Ref Script
841
- .readFrom([cdpRefScriptUtxo, iAssetTokenPolicyRefScriptUtxo])
1013
+ const treasuryRefScriptUtxo =
1014
+ processingFee > 0n
1015
+ ? await treasuryFeeTx(
1016
+ cdpDatum.collateralAsset,
1017
+ processingFee,
1018
+ partialRedemptionFee,
1019
+ lucid,
1020
+ sysParams,
1021
+ tx,
1022
+ cdpOref,
1023
+ treasuryOref,
1024
+ )
1025
+ : partialRedemptionFee > 0n
1026
+ ? await treasuryFeeTx(
1027
+ adaAssetClass,
1028
+ partialRedemptionFee,
1029
+ 0n,
1030
+ lucid,
1031
+ sysParams,
1032
+ tx,
1033
+ cdpOref,
1034
+ treasuryOref,
1035
+ )
1036
+ : undefined;
1037
+
1038
+ const interestCollectorRefScriptUtxo =
1039
+ interestAmt > 0n
1040
+ ? await collectInterestTx(
1041
+ mkAssetsOf(
1042
+ {
1043
+ currencySymbol: fromHex(
1044
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
1045
+ ),
1046
+ tokenName: iassetDatum.assetName,
1047
+ },
1048
+ interestAmt,
1049
+ ),
1050
+ lucid,
1051
+ sysParams,
1052
+ tx,
1053
+ interestCollectorOref,
1054
+ )
1055
+ : undefined;
1056
+
1057
+ // We need to take into account the treasury and interest collector ref scripts as well.
1058
+ const refInputsIndices = getInputIndices(referenceInputs, [
1059
+ ...referenceInputs,
1060
+ ...referenceScripts,
1061
+ ...(treasuryRefScriptUtxo != null ? [treasuryRefScriptUtxo] : []),
1062
+ ...(interestCollectorRefScriptUtxo != null
1063
+ ? [interestCollectorRefScriptUtxo]
1064
+ : []),
1065
+ ]);
1066
+
1067
+ tx
842
1068
  // Ref inputs
843
- .readFrom([iassetUtxo, priceOracleUtxo, interestOracleUtxo])
844
- .validFrom(txValidity.validFrom)
845
- .validTo(txValidity.validTo)
846
- .collectFrom(
847
- [cdpUtxo],
848
- serialiseCdpRedeemer({ RedeemCdp: { currentTime: currentTime } }),
1069
+ .readFrom(referenceInputs)
1070
+ // Trigger CDP Redeem Withdrawal validator
1071
+ .withdraw(
1072
+ credentialToRewardAddress(
1073
+ lucid.config().network!,
1074
+ scriptHashToCredential(sysParams.cdpParams.cdpRedeemValHash),
1075
+ ),
1076
+ 0n,
1077
+ serialiseRedeemCdpWithdrawalRedeemer({
1078
+ cdpOutReference: {
1079
+ txHash: fromHex(cdpUtxo.txHash),
1080
+ outputIndex: BigInt(cdpUtxo.outputIndex),
1081
+ },
1082
+ currentTime: currentTime,
1083
+ priceOracleIdx:
1084
+ priceOracleOref !== undefined
1085
+ ? { OracleRefInputIdx: refInputsIndices[4] }
1086
+ : 'OracleVoid',
1087
+ }),
849
1088
  )
1089
+ .collectFrom([cdpUtxo], serialiseCdpRedeemer('RedeemCdp'))
850
1090
  .mintAssets(
851
1091
  mkAssetsOf(
852
1092
  {
853
- currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
1093
+ currencySymbol: fromHex(
1094
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
1095
+ ),
854
1096
  tokenName: iassetDatum.assetName,
855
1097
  },
856
- -redemptionIAssetAmt,
1098
+ interestAmt - redemptionIAssetAmt,
857
1099
  ),
858
1100
  Data.void(),
859
1101
  )
@@ -863,7 +1105,7 @@ export async function redeemCdp(
863
1105
  kind: 'inline',
864
1106
  value: serialiseCdpDatum({
865
1107
  ...cdpDatum,
866
- mintedAmt: cdpDatum.mintedAmt - redemptionIAssetAmt,
1108
+ mintedAmt: totalCdpDebt - redemptionIAssetAmt,
867
1109
  cdpFees: {
868
1110
  ActiveCDPInterestTracking: {
869
1111
  lastSettled: currentTime,
@@ -879,33 +1121,27 @@ export async function redeemCdp(
879
1121
  },
880
1122
  addAssets(
881
1123
  cdpUtxo.assets,
882
- mkLovelacesOf(-redemptionLovelacesAmt),
883
- mkLovelacesOf(reimburstmentFee),
884
- mkLovelacesOf(-interestAdaAmt),
1124
+ mkAssetsOf(
1125
+ cdpDatum.collateralAsset,
1126
+ -redemptionCollateralAmt + reimburstmentFee,
1127
+ ),
885
1128
  ),
886
1129
  );
887
1130
 
888
- await collectorFeeTx(
889
- processingFee + partialRedemptionFee + interestCollectorAdaAmt,
890
- lucid,
891
- sysParams,
892
- tx,
893
- collector,
894
- );
895
-
896
- await treasuryFeeTx(interestTreasuryAdaAmt, lucid, sysParams, tx, treasury);
897
-
898
1131
  return tx;
899
1132
  }
900
1133
 
901
1134
  export async function freezeCdp(
902
- cdp: UtxoOrOutRef,
903
- iasset: UtxoOrOutRef,
904
- priceOracle: UtxoOrOutRef,
905
- interestOracle: UtxoOrOutRef,
1135
+ cdpOref: OutRef,
1136
+ iassetOref: OutRef,
1137
+ collateralAssetOref: OutRef,
1138
+ priceOracleOref: OutRef | undefined,
1139
+ interestOracleOref: OutRef,
906
1140
  sysParams: SystemParams,
907
1141
  lucid: LucidEvolution,
908
1142
  currentSlot: number,
1143
+ pythMessage: string | undefined = undefined,
1144
+ pythStateOref: OutRef | undefined = undefined,
909
1145
  ): Promise<TxBuilder> {
910
1146
  const network = lucid.config().network!;
911
1147
  const currentTime = BigInt(slotToUnixTime(network, currentSlot));
@@ -916,137 +1152,145 @@ export async function freezeCdp(
916
1152
  ]),
917
1153
  (_) => new Error('Expected a single cdp Ref Script UTXO'),
918
1154
  );
919
- const cdpUtxo = await resolveUtxo(cdp, lucid, 'Expected a single cdp UTXO');
1155
+ const cdpUtxo = matchSingle(
1156
+ await lucid.utxosByOutRef([cdpOref]),
1157
+ (_) => new Error('Expected a single cdp UTXO'),
1158
+ );
920
1159
  const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
921
1160
 
922
- const iassetUtxo = await resolveUtxo(
923
- iasset,
924
- lucid,
925
- 'Expected a single iasset UTXO',
1161
+ const iassetUtxo = matchSingle(
1162
+ await lucid.utxosByOutRef([iassetOref]),
1163
+ (_) => new Error('Expected a single iasset UTXO'),
926
1164
  );
927
1165
  const iassetDatum = parseIAssetDatumOrThrow(
928
1166
  getInlineDatumOrThrow(iassetUtxo),
929
1167
  );
930
-
931
- const priceOracleUtxo = await resolveUtxo(
932
- priceOracle,
933
- lucid,
934
- 'Expected a single price oracle UTXO',
1168
+ const collateralAssetUtxo = matchSingle(
1169
+ await lucid.utxosByOutRef([collateralAssetOref]),
1170
+ (_) => new Error('Expected a single collateral asset UTXO'),
935
1171
  );
936
- const priceOracleDatum = parsePriceOracleDatum(
937
- getInlineDatumOrThrow(priceOracleUtxo),
1172
+ const collateralAssetDatum = parseCollateralAssetDatumOrThrow(
1173
+ getInlineDatumOrThrow(collateralAssetUtxo),
938
1174
  );
939
1175
 
940
- const interestOracleUtxo = await resolveUtxo(
941
- interestOracle,
1176
+ const [adjustedPrice, _] = await retrieveAdjustedPrice(
1177
+ iassetDatum.assetName,
1178
+ collateralAssetDatum.collateralAsset,
1179
+ collateralAssetDatum.priceInfo,
1180
+ collateralAssetDatum.extraDecimals,
1181
+ priceOracleOref,
1182
+ pythMessage,
1183
+ sysParams.pythConfig,
942
1184
  lucid,
943
- 'Expected a single interest oracle UTXO',
1185
+ );
1186
+
1187
+ const interestOracleUtxo = matchSingle(
1188
+ await lucid.utxosByOutRef([interestOracleOref]),
1189
+ (_) => new Error('Expected a single interest oracle UTXO'),
944
1190
  );
945
1191
  const interestOracleDatum = parseInterestOracleDatum(
946
1192
  getInlineDatumOrThrow(interestOracleUtxo),
947
1193
  );
948
1194
 
949
- const interestAdaAmt = match(cdpDatum.cdpFees)
1195
+ const interestAmt = match(cdpDatum.cdpFees)
950
1196
  .with({ FrozenCDPAccumulatedFees: P.any }, () => {
951
1197
  throw new Error('CDP fees wrong');
952
1198
  })
953
1199
  .with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
954
- const interestPaymentIAssetAmt = calculateAccruedInterest(
1200
+ return calculateAccruedInterest(
955
1201
  currentTime,
956
1202
  interest.unitaryInterestSnapshot,
957
1203
  cdpDatum.mintedAmt,
958
1204
  interest.lastSettled,
959
1205
  interestOracleDatum,
960
1206
  );
961
-
962
- const maxInterestLovelaces = computeInterestLovelacesFor100PercentCR(
963
- lovelacesAmt(cdpUtxo.assets),
964
- cdpDatum.mintedAmt,
965
- priceOracleDatum.price,
966
- );
967
-
968
- return bigintMin(
969
- maxInterestLovelaces,
970
- ocdMul(
971
- { getOnChainInt: interestPaymentIAssetAmt },
972
- priceOracleDatum.price,
973
- ).getOnChainInt,
974
- );
975
1207
  })
976
1208
  .exhaustive();
977
1209
 
978
- const interestCollectorAdaAmt = calculateFeeFromPercentage(
979
- iassetDatum.interestCollectorPortionPercentage,
980
- interestAdaAmt,
1210
+ const inputCollateral = assetClassValueOf(
1211
+ cdpUtxo.assets,
1212
+ cdpDatum.collateralAsset,
981
1213
  );
982
1214
 
983
- const interestTreasuryAdaAmt = interestAdaAmt - interestCollectorAdaAmt;
984
-
985
- const inputCollateralMinusInterest =
986
- lovelacesAmt(cdpUtxo.assets) - interestAdaAmt;
987
-
988
- const cdpDebtAdaValue = ocdMul(
989
- { getOnChainInt: cdpDatum.mintedAmt },
990
- priceOracleDatum.price,
991
- ).getOnChainInt;
1215
+ const cdpDebtCollateralValue = rationalFloor(
1216
+ rationalMul(
1217
+ rationalFromInt(cdpDatum.mintedAmt + interestAmt),
1218
+ adjustedPrice,
1219
+ ),
1220
+ );
992
1221
 
993
1222
  const liquidationProcessingFee = bigintMin(
994
- calculateFeeFromPercentage(
995
- iassetDatum.liquidationProcessingFeePercentage,
996
- inputCollateralMinusInterest,
997
- ),
998
- calculateFeeFromPercentage(
999
- iassetDatum.liquidationProcessingFeePercentage,
1000
- cdpDebtAdaValue,
1223
+ calculateFeeFromRatio(
1224
+ iassetDatum.liquidationProcessingFeeRatio,
1225
+ inputCollateral,
1001
1226
  ),
1227
+ bigintMax(0n, inputCollateral - cdpDebtCollateralValue),
1002
1228
  );
1003
1229
 
1004
- const txValidity = oracleExpirationAwareValidity(
1230
+ let referenceInputs = [iassetUtxo, collateralAssetUtxo, interestOracleUtxo];
1231
+ const referenceScripts = [cdpRefScriptUtxo];
1232
+
1233
+ const tx = lucid.newTx();
1234
+
1235
+ const { interval, referenceInputs: oracleRefInputs } = await attachOracle(
1236
+ iassetDatum.assetName,
1237
+ collateralAssetDatum.collateralAsset,
1238
+ collateralAssetDatum.priceInfo,
1239
+ priceOracleOref,
1240
+ pythStateOref,
1241
+ pythMessage,
1242
+ sysParams.pythConfig,
1243
+ sysParams.cdpParams.biasTime,
1005
1244
  currentSlot,
1006
- Number(sysParams.cdpCreatorParams.biasTime),
1007
- Number(priceOracleDatum.expiration),
1008
- network,
1009
- );
1010
-
1011
- return (
1012
- lucid
1013
- .newTx()
1014
- // Ref Script
1015
- .readFrom([cdpRefScriptUtxo])
1016
- // Ref inputs
1017
- .readFrom([iassetUtxo, priceOracleUtxo, interestOracleUtxo])
1018
- .validFrom(txValidity.validFrom)
1019
- .validTo(txValidity.validTo)
1020
- .collectFrom(
1021
- [cdpUtxo],
1022
- serialiseCdpRedeemer({ FreezeCdp: { currentTime: currentTime } }),
1023
- )
1024
- .pay.ToContract(
1025
- createScriptAddress(network, sysParams.validatorHashes.cdpHash),
1026
- {
1027
- kind: 'inline',
1028
- value: serialiseCdpDatum({
1029
- ...cdpDatum,
1030
- cdpOwner: null,
1031
- cdpFees: {
1032
- FrozenCDPAccumulatedFees: {
1033
- lovelacesIndyStakers:
1034
- liquidationProcessingFee + interestCollectorAdaAmt,
1035
- lovelacesTreasury: interestTreasuryAdaAmt,
1036
- },
1037
- },
1038
- }),
1039
- },
1040
- cdpUtxo.assets,
1041
- )
1245
+ lucid,
1246
+ tx,
1042
1247
  );
1248
+
1249
+ referenceInputs = [...oracleRefInputs, ...referenceInputs];
1250
+
1251
+ const refInputsIndices = getInputIndices(referenceInputs, [
1252
+ ...referenceInputs,
1253
+ ...referenceScripts,
1254
+ ]);
1255
+
1256
+ return tx
1257
+ .readFrom(referenceScripts)
1258
+ .readFrom(referenceInputs)
1259
+ .validFrom(interval.validFrom)
1260
+ .validTo(interval.validTo)
1261
+ .collectFrom(
1262
+ [cdpUtxo],
1263
+ serialiseCdpRedeemer({
1264
+ FreezeCdp: {
1265
+ currentTime: currentTime,
1266
+ priceOracleIdx: { OracleRefInputIdx: refInputsIndices[0] },
1267
+ },
1268
+ }),
1269
+ )
1270
+ .pay.ToContract(
1271
+ createScriptAddress(network, sysParams.validatorHashes.cdpHash),
1272
+ {
1273
+ kind: 'inline',
1274
+ value: serialiseCdpDatum({
1275
+ ...cdpDatum,
1276
+ cdpOwner: null,
1277
+ cdpFees: {
1278
+ FrozenCDPAccumulatedFees: {
1279
+ iassetInterest: interestAmt,
1280
+ collateralTreasury: liquidationProcessingFee,
1281
+ },
1282
+ },
1283
+ }),
1284
+ },
1285
+ cdpUtxo.assets,
1286
+ );
1043
1287
  }
1044
1288
 
1045
1289
  export async function liquidateCdp(
1046
- cdp: UtxoOrOutRef,
1047
- stabilityPool: UtxoOrOutRef,
1048
- collector: UtxoOrOutRef,
1049
- treasury: UtxoOrOutRef,
1290
+ cdpOref: OutRef,
1291
+ stabilityPoolOref: OutRef,
1292
+ interestCollectorOref: OutRef,
1293
+ treasuryOref: OutRef,
1050
1294
  sysParams: SystemParams,
1051
1295
  lucid: LucidEvolution,
1052
1296
  ): Promise<TxBuilder> {
@@ -1081,21 +1325,23 @@ export async function liquidateCdp(
1081
1325
  (_) => new Error('Expected a single cdp auth token policy Ref Script UTXO'),
1082
1326
  );
1083
1327
 
1084
- const cdpUtxo = await resolveUtxo(cdp, lucid, 'Expected a single cdp UTXO');
1328
+ const cdpUtxo = matchSingle(
1329
+ await lucid.utxosByOutRef([cdpOref]),
1330
+ (_) => new Error('Expected a single cdp UTXO'),
1331
+ );
1085
1332
  const cdpDatum = parseCdpDatumOrThrow(getInlineDatumOrThrow(cdpUtxo));
1086
1333
 
1087
- const spUtxo = await resolveUtxo(
1088
- stabilityPool,
1089
- lucid,
1090
- 'Expected a single stability pool UTXO',
1334
+ const spUtxo = matchSingle(
1335
+ await lucid.utxosByOutRef([stabilityPoolOref]),
1336
+ (_) => new Error('Expected a single stability pool UTXO'),
1091
1337
  );
1092
- const spDatum = parseStabilityPoolDatum(getInlineDatumOrThrow(spUtxo));
1338
+ const spDatum = parseStabilityPoolDatumOrThrow(getInlineDatumOrThrow(spUtxo));
1093
1339
 
1094
- const [lovelacesForTreasury, lovelacesForCollector] = match(cdpDatum.cdpFees)
1340
+ const [frozenInterest, collateralForTreasury] = match(cdpDatum.cdpFees)
1095
1341
  .returnType<[bigint, bigint]>()
1096
1342
  .with({ FrozenCDPAccumulatedFees: P.select() }, (fees) => [
1097
- fees.lovelacesTreasury,
1098
- fees.lovelacesIndyStakers,
1343
+ fees.iassetInterest,
1344
+ fees.collateralTreasury,
1099
1345
  ])
1100
1346
  .with({ ActiveCDPInterestTracking: P.any }, () => {
1101
1347
  throw new Error('CDP fees wrong');
@@ -1104,21 +1350,36 @@ export async function liquidateCdp(
1104
1350
 
1105
1351
  const cdpNftAc = fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken);
1106
1352
  const iassetsAc = {
1107
- currencySymbol: sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
1353
+ currencySymbol: fromHex(
1354
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
1355
+ ),
1108
1356
  tokenName: cdpDatum.iasset,
1109
1357
  };
1110
1358
 
1111
1359
  const spIassetAmt = assetClassValueOf(spUtxo.assets, iassetsAc);
1112
1360
 
1113
- const iassetBurnAmt = bigintMin(cdpDatum.mintedAmt, spIassetAmt);
1361
+ const totalCdpDebt = cdpDatum.mintedAmt + frozenInterest;
1362
+
1363
+ // Take from the SP enough as much iAsset as possible to pay out the debt.
1364
+ const iassetUsedAmt = bigintMin(totalCdpDebt, spIassetAmt);
1114
1365
 
1115
- const collateralAvailable = lovelacesAmt(cdpUtxo.assets);
1116
- const collateralAvailMinusFees =
1117
- collateralAvailable - lovelacesForCollector - lovelacesForTreasury;
1366
+ const collateralAvailable = assetClassValueOf(
1367
+ cdpUtxo.assets,
1368
+ cdpDatum.collateralAsset,
1369
+ );
1370
+ const collateralAvailMinusFees = collateralAvailable - collateralForTreasury;
1118
1371
  const collateralAbsorbed =
1119
- (collateralAvailMinusFees * iassetBurnAmt) / cdpDatum.mintedAmt;
1372
+ (collateralAvailMinusFees * iassetUsedAmt) / totalCdpDebt;
1373
+
1374
+ const isPartial = spIassetAmt < totalCdpDebt;
1375
+
1376
+ // Interest partially paid if the liquidity in the SP is not enough to cover it.
1377
+ const remainingInterest = bigintMax(frozenInterest - spIassetAmt, 0n);
1378
+
1379
+ const payableInterest = frozenInterest - remainingInterest;
1120
1380
 
1121
- const isPartial = spIassetAmt < cdpDatum.mintedAmt;
1381
+ // All the iAsset taken from the SP and not used to pay out the interest must be burnt.
1382
+ const iassetBurnAmt = iassetUsedAmt - payableInterest;
1122
1383
 
1123
1384
  const tx = lucid
1124
1385
  .newTx()
@@ -1138,18 +1399,32 @@ export async function liquidateCdp(
1138
1399
  value: serialiseStabilityPoolDatum({
1139
1400
  StabilityPool: liquidationHelper(
1140
1401
  spDatum,
1141
- iassetBurnAmt,
1402
+ cdpDatum.collateralAsset,
1403
+ iassetUsedAmt,
1142
1404
  collateralAbsorbed,
1143
- ).newSpContent,
1405
+ ),
1144
1406
  }),
1145
1407
  },
1146
1408
  addAssets(
1147
1409
  spUtxo.assets,
1148
- mkLovelacesOf(collateralAbsorbed),
1149
- mkAssetsOf(iassetsAc, -iassetBurnAmt),
1410
+ mkAssetsOf(cdpDatum.collateralAsset, collateralAbsorbed),
1411
+ mkAssetsOf(iassetsAc, -iassetUsedAmt),
1150
1412
  ),
1151
1413
  );
1152
1414
 
1415
+ if (collateralForTreasury > 0n) {
1416
+ await treasuryFeeTx(
1417
+ cdpDatum.collateralAsset,
1418
+ collateralForTreasury,
1419
+ 0n,
1420
+ lucid,
1421
+ sysParams,
1422
+ tx,
1423
+ cdpOref,
1424
+ treasuryOref,
1425
+ );
1426
+ }
1427
+
1153
1428
  if (isPartial) {
1154
1429
  tx.pay.ToContract(
1155
1430
  cdpUtxo.address,
@@ -1157,18 +1432,23 @@ export async function liquidateCdp(
1157
1432
  kind: 'inline',
1158
1433
  value: serialiseCdpDatum({
1159
1434
  ...cdpDatum,
1160
- mintedAmt: cdpDatum.mintedAmt - spIassetAmt,
1435
+ mintedAmt: cdpDatum.mintedAmt - iassetBurnAmt,
1161
1436
  cdpFees: {
1162
1437
  FrozenCDPAccumulatedFees: {
1163
- lovelacesIndyStakers: 0n,
1164
- lovelacesTreasury: 0n,
1438
+ iassetInterest: remainingInterest,
1439
+ collateralTreasury: 0n,
1165
1440
  },
1166
1441
  },
1167
1442
  }),
1168
1443
  },
1169
1444
  addAssets(
1170
- mkAssetsOf(cdpNftAc, assetClassValueOf(cdpUtxo.assets, cdpNftAc)),
1171
- mkLovelacesOf(collateralAvailable - collateralAbsorbed),
1445
+ cdpUtxo.assets,
1446
+ negateAssets(
1447
+ mkAssetsOf(
1448
+ cdpDatum.collateralAsset,
1449
+ collateralForTreasury + collateralAbsorbed,
1450
+ ),
1451
+ ),
1172
1452
  ),
1173
1453
  );
1174
1454
  } else {
@@ -1178,9 +1458,15 @@ export async function liquidateCdp(
1178
1458
  );
1179
1459
  }
1180
1460
 
1181
- await collectorFeeTx(lovelacesForCollector, lucid, sysParams, tx, collector);
1182
-
1183
- await treasuryFeeTx(lovelacesForTreasury, lucid, sysParams, tx, treasury);
1461
+ if (payableInterest > 0) {
1462
+ await collectInterestTx(
1463
+ mkAssetsOf(iassetsAc, payableInterest),
1464
+ lucid,
1465
+ sysParams,
1466
+ tx,
1467
+ interestCollectorOref,
1468
+ );
1469
+ }
1184
1470
 
1185
1471
  return tx;
1186
1472
  }
@@ -1217,22 +1503,21 @@ export async function mergeCdps(
1217
1503
  );
1218
1504
 
1219
1505
  type AggregatedFees = {
1220
- aggregatedFeeIndyStakers: bigint;
1221
- aggregatedFeeTreasury: bigint;
1506
+ aggregatedCollateralTreasury: bigint;
1507
+ aggregatedInterest: bigint;
1222
1508
  };
1223
1509
 
1224
- const { aggregatedFeeTreasury, aggregatedFeeIndyStakers } = F.pipe(
1510
+ const { aggregatedInterest, aggregatedCollateralTreasury } = F.pipe(
1225
1511
  cdpDatums,
1226
1512
  A.reduce<CDPContent, AggregatedFees>(
1227
- { aggregatedFeeIndyStakers: 0n, aggregatedFeeTreasury: 0n },
1513
+ { aggregatedCollateralTreasury: 0n, aggregatedInterest: 0n },
1228
1514
  (acc, cdpDat) =>
1229
1515
  match(cdpDat.cdpFees)
1230
1516
  .returnType<AggregatedFees>()
1231
1517
  .with({ FrozenCDPAccumulatedFees: P.select() }, (fees) => ({
1232
- aggregatedFeeIndyStakers:
1233
- acc.aggregatedFeeIndyStakers + fees.lovelacesIndyStakers,
1234
- aggregatedFeeTreasury:
1235
- acc.aggregatedFeeTreasury + fees.lovelacesTreasury,
1518
+ aggregatedCollateralTreasury:
1519
+ acc.aggregatedCollateralTreasury + fees.collateralTreasury,
1520
+ aggregatedInterest: acc.aggregatedInterest + fees.iassetInterest,
1236
1521
  }))
1237
1522
  .otherwise(() => acc),
1238
1523
  ),
@@ -1260,7 +1545,7 @@ export async function mergeCdps(
1260
1545
  MergeAuxiliary: {
1261
1546
  mainMergeUtxo: {
1262
1547
  outputIndex: BigInt(mainMergeUtxo.outputIndex),
1263
- txHash: { hash: mainMergeUtxo.txHash },
1548
+ txHash: fromHex(mainMergeUtxo.txHash),
1264
1549
  },
1265
1550
  },
1266
1551
  }),
@@ -1272,11 +1557,12 @@ export async function mergeCdps(
1272
1557
  value: serialiseCdpDatum({
1273
1558
  cdpOwner: null,
1274
1559
  iasset: mainCdpDatum.iasset,
1560
+ collateralAsset: mainCdpDatum.collateralAsset,
1275
1561
  mintedAmt: aggregatedMintedAmt,
1276
1562
  cdpFees: {
1277
1563
  FrozenCDPAccumulatedFees: {
1278
- lovelacesIndyStakers: aggregatedFeeIndyStakers,
1279
- lovelacesTreasury: aggregatedFeeTreasury,
1564
+ collateralTreasury: aggregatedCollateralTreasury,
1565
+ iassetInterest: aggregatedInterest,
1280
1566
  },
1281
1567
  },
1282
1568
  }),