@indigo-labs/indigo-sdk 0.3.9 → 0.3.10

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.
@@ -1,59 +1,54 @@
1
- import { describe, test } from 'vitest';
2
-
3
- // Just a placeholder otherwise vitest wouldn't like it.
4
- describe('ROB leverage', () => {
5
- test.todo('Always pass', () => {});
6
- });
7
- // eslint-disable-next-line vitest/no-commented-out-tests
8
- /*import { beforeEach, expect, test, vi } from 'vitest';
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
9
2
  import {
10
3
  addrDetails,
4
+ calculateLeverageFromCollateralRatio,
5
+ calculateTotalCollateralForRedemption,
11
6
  cdpCollateralRatioPercentage,
12
7
  fromSystemParamsAsset,
13
8
  getInlineDatumOrThrow,
9
+ leverageCdpWithRob,
10
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
11
+ MIN_ROB_COLLATERAL_AMT,
14
12
  openRob,
15
- RobParamsSP,
13
+ randomRobsSubsetSatisfyingTargetCollateral,
14
+ Rational,
15
+ rationalToFloat,
16
+ robCollateralAmtToSpend,
17
+ RobDatum,
16
18
  SystemParams,
17
19
  } from '../../src';
18
20
  import {
19
21
  addAssets,
22
+ Assets,
20
23
  Emulator,
21
24
  EmulatorAccount,
22
25
  fromHex,
23
26
  fromText,
24
27
  generateEmulatorAccount,
25
28
  Lucid,
26
- toText,
29
+ slotToUnixTime,
30
+ toHex,
27
31
  UTxO,
28
32
  } from '@lucid-evolution/lucid';
29
33
  import { LucidContext, runAndAwaitTx } from '../test-helpers';
30
- import { describe } from 'vitest';
31
34
 
32
35
  import { init } from '../endpoints/initialize';
33
36
  import {
37
+ DEFAULT_PRICE,
34
38
  iusdInitialAssetCfg,
39
+ iusdInitialAssetCfgWithPyth,
35
40
  mkBaseCollateralAsset,
36
41
  } from '../mock/assets-mock';
37
- import { ocdFloor, OnChainDecimal } from '../../src/types/on-chain-decimal';
38
42
  import { assertValueInRange } from '../utils/asserts';
39
43
 
40
- import {
41
- calculateLeverageFromCollateralRatio,
42
- MAX_REDEMPTIONS_WITH_CDP_OPEN,
43
- } from '../../src/contracts/leverage/helpers';
44
- import { leverageCdpWithLrp } from '../../src/contracts/leverage/transactions';
45
- import {
46
- calculateTotalAdaForRedemption,
47
- lrpRedeemableLovelacesInclReimb,
48
- MIN_LRP_COLLATERAL_AMT,
49
- randomLrpsSubsetSatisfyingTargetLovelaces,
50
- } from '../../src/contracts/lrp/helpers';
51
- import { RobDatum, RobDatum } from '../../src/contracts/lrp/types-new';
52
44
  import {
53
45
  adaAssetClass,
54
46
  AssetClass,
47
+ assetClassToUnit,
55
48
  assetClassValueOf,
49
+ isSameAssetClass,
56
50
  lovelacesAmt,
51
+ mkAssetsOf,
57
52
  mkLovelacesOf,
58
53
  } from '@3rd-eye-labs/cardano-offchain-common';
59
54
  import {
@@ -64,20 +59,33 @@ import {
64
59
  import { parsePriceOracleDatum } from '../../src/contracts/price-oracle/types-new';
65
60
  import { parseInterestOracleDatum } from '../../src/contracts/interest-oracle/types-new';
66
61
  import { benchmarkAndAwaitTx } from '../utils/benchmark-utils';
67
- import { findAllRobs } from './lrp-queries';
62
+ import { findAllRobs } from './rob-queries';
63
+ import { MAINNET_PROTOCOL_PARAMETERS } from '../indigo-test-helpers';
64
+ import { match, P } from 'ts-pattern';
65
+ import { ParsedFeedPayload } from '@pythnetwork/pyth-lazer-sdk';
66
+ import { createPythMessage } from '../pyth/helpers';
67
+ import { retrieveAdjustedPrice } from '../../src/utils/oracle-helpers';
68
68
 
69
69
  type MyContext = LucidContext<{
70
70
  admin: EmulatorAccount;
71
71
  user: EmulatorAccount;
72
72
  }>;
73
73
 
74
+ const collateralAssetA: AssetClass = {
75
+ currencySymbol: fromHex(
76
+ // random generated
77
+ 'cc072059ae741791b7b9c23d9baea6a0b0d764dec617ce7e027a8dea',
78
+ ),
79
+ tokenName: fromHex(fromText('A')),
80
+ };
81
+
74
82
  async function openBuyRobs(
75
83
  context: MyContext,
76
84
  sysParams: SystemParams,
77
85
  iasset: string,
78
86
  collateralAsset: AssetClass,
79
87
  amountsToSpend: bigint[],
80
- maxPrice: OnChainDecimal,
88
+ maxPrice: Rational,
81
89
  ): Promise<void> {
82
90
  for (const amt of amountsToSpend) {
83
91
  await runAndAwaitTx(
@@ -95,61 +103,65 @@ async function openBuyRobs(
95
103
 
96
104
  function hadRobRedemption(
97
105
  lrp: { utxo: UTxO; datum: RobDatum },
98
- robParams: RobParamsSP,
106
+ sysParams: SystemParams,
99
107
  ): boolean {
100
108
  return (
101
109
  assetClassValueOf(lrp.utxo.assets, {
102
- currencySymbol: fromHex(robParams.iassetPolicyId.unCurrencySymbol),
110
+ currencySymbol: fromHex(
111
+ sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
112
+ ),
103
113
  tokenName: lrp.datum.iasset,
104
114
  }) > 0
105
115
  );
106
116
  }
107
117
 
108
- describe('randomLrpsSubsetSatisfyingTargetLovelaces', () => {
109
- const mockUtxo = (ada: bigint): UTxO => ({
118
+ describe('randomRobsSubsetSatisfyingTargetCollateral', () => {
119
+ const mockUtxo = (ada: bigint, otherAssets: Assets = {}): UTxO => ({
110
120
  address: '',
111
- assets: mkLovelacesOf(ada),
121
+ assets: addAssets(mkLovelacesOf(ada), otherAssets),
112
122
  outputIndex: 0,
113
123
  txHash: '',
114
124
  });
115
125
 
116
- const mockLrpParams: RobParamsSP = {
117
- iassetNft: [{ unCurrencySymbol: '' }, { unTokenName: '' }],
118
- iassetPolicyId: { unCurrencySymbol: '' },
119
- minRedemptionLovelacesAmt: 10n,
120
- versionRecordToken: [{ unCurrencySymbol: '' }, { unTokenName: '' }],
121
- iassetValHash: '',
122
- };
123
-
124
126
  test('1', () => {
125
127
  const lrps: [UTxO, RobDatum][] = [
126
128
  [
127
- mockUtxo(100n),
129
+ mockUtxo(100_000_000n),
128
130
  {
129
131
  iasset: fromHex(fromText('iUSD')),
130
- lovelacesToSpend: 100n,
131
- maxPrice: { getOnChainInt: 1_000_000n },
132
+ orderType: {
133
+ BuyIAssetOrder: {
134
+ collateralAsset: adaAssetClass,
135
+ maxPrice: { numerator: 1n, denominator: 1n },
136
+ },
137
+ },
132
138
  owner: fromHex(''),
139
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
133
140
  },
134
141
  ],
135
142
  [
136
- mockUtxo(100n),
143
+ mockUtxo(100_000_000n),
137
144
  {
138
145
  iasset: fromHex(fromText('iUSD')),
139
- lovelacesToSpend: 100n,
140
- maxPrice: { getOnChainInt: 1_000_000n },
146
+ orderType: {
147
+ BuyIAssetOrder: {
148
+ collateralAsset: adaAssetClass,
149
+ maxPrice: { numerator: 1n, denominator: 1n },
150
+ },
151
+ },
141
152
  owner: fromHex(''),
153
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
142
154
  },
143
155
  ],
144
156
  ];
145
157
 
146
158
  expect(
147
- randomLrpsSubsetSatisfyingTargetLovelaces(
159
+ randomRobsSubsetSatisfyingTargetCollateral(
148
160
  fromHex(fromText('iUSD')),
149
- 120n,
150
- { getOnChainInt: 1_000_000n },
161
+ adaAssetClass,
162
+ 120_000_000n,
163
+ { numerator: 1n, denominator: 1n },
151
164
  lrps,
152
- mockLrpParams,
153
165
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
154
166
  ),
155
167
  ).toEqual(expect.arrayContaining(lrps));
@@ -158,200 +170,215 @@ describe('randomLrpsSubsetSatisfyingTargetLovelaces', () => {
158
170
  test('2', () => {
159
171
  const lrps: [UTxO, RobDatum][] = [
160
172
  [
161
- mockUtxo(100n),
173
+ mockUtxo(150_000_000n),
162
174
  {
163
175
  iasset: fromHex(fromText('iUSD')),
164
- lovelacesToSpend: 100n,
165
- maxPrice: { getOnChainInt: 1_000_000n },
176
+ orderType: {
177
+ BuyIAssetOrder: {
178
+ collateralAsset: adaAssetClass,
179
+ maxPrice: { numerator: 1n, denominator: 1n },
180
+ },
181
+ },
166
182
  owner: fromHex(''),
183
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
167
184
  },
168
185
  ],
169
186
  ];
170
187
 
171
188
  expect(
172
- randomLrpsSubsetSatisfyingTargetLovelaces(
189
+ randomRobsSubsetSatisfyingTargetCollateral(
173
190
  fromHex(fromText('iUSD')),
174
- 100n,
175
- { getOnChainInt: 1_000_000n },
191
+ adaAssetClass,
192
+ 100_000_000n,
193
+ { numerator: 1n, denominator: 1n },
176
194
  lrps,
177
- mockLrpParams,
178
195
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
179
196
  ),
180
197
  ).toEqual(lrps);
181
198
  });
182
199
 
183
- test('filtering by iasset 1', () => {
200
+ test('3', () => {
184
201
  const lrps: [UTxO, RobDatum][] = [
185
202
  [
186
- mockUtxo(100n),
203
+ mockUtxo(100_000_000n),
187
204
  {
188
205
  iasset: fromHex(fromText('iUSD')),
189
- lovelacesToSpend: 100n,
190
- maxPrice: { getOnChainInt: 1_000_000n },
206
+ orderType: {
207
+ BuyIAssetOrder: {
208
+ collateralAsset: adaAssetClass,
209
+ maxPrice: { numerator: 1n, denominator: 1n },
210
+ },
211
+ },
191
212
  owner: fromHex(''),
213
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
192
214
  },
193
215
  ],
194
216
  [
195
- mockUtxo(100n),
217
+ mockUtxo(10_000_000n),
196
218
  {
197
- iasset: fromHex(fromText('iBTC')),
198
- lovelacesToSpend: 100n,
199
- maxPrice: { getOnChainInt: 1_000_000n },
219
+ iasset: fromHex(fromText('iUSD')),
220
+ orderType: {
221
+ BuyIAssetOrder: {
222
+ collateralAsset: adaAssetClass,
223
+ maxPrice: { numerator: 1n, denominator: 1n },
224
+ },
225
+ },
200
226
  owner: fromHex(''),
227
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
201
228
  },
202
229
  ],
203
230
  [
204
- mockUtxo(100n),
231
+ mockUtxo(100_000_000n),
205
232
  {
206
233
  iasset: fromHex(fromText('iUSD')),
207
- lovelacesToSpend: 100n,
208
- maxPrice: { getOnChainInt: 1_000_000n },
234
+ orderType: {
235
+ BuyIAssetOrder: {
236
+ collateralAsset: adaAssetClass,
237
+ maxPrice: { numerator: 1n, denominator: 1n },
238
+ },
239
+ },
209
240
  owner: fromHex(''),
241
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
210
242
  },
211
243
  ],
212
244
  ];
213
245
 
246
+ const mockedShuffle = vi.fn().mockImplementation(() => lrps);
247
+
214
248
  expect(
215
- randomLrpsSubsetSatisfyingTargetLovelaces(
249
+ randomRobsSubsetSatisfyingTargetCollateral(
216
250
  fromHex(fromText('iUSD')),
217
- 110n,
218
- { getOnChainInt: 1_000_000n },
251
+ adaAssetClass,
252
+ 120_000_000n,
253
+ { numerator: 1n, denominator: 1n },
219
254
  lrps,
220
- mockLrpParams,
221
255
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
256
+ mockedShuffle,
222
257
  ),
223
- ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
258
+ ).toEqual(expect.arrayContaining(lrps));
224
259
  });
225
260
 
226
- test('filtering by iasset 2', () => {
261
+ test('4 (min rob collateral causes less redeemable)', () => {
227
262
  const lrps: [UTxO, RobDatum][] = [
228
263
  [
229
- mockUtxo(100n),
264
+ mockUtxo(100_000_000n),
230
265
  {
231
266
  iasset: fromHex(fromText('iUSD')),
232
- lovelacesToSpend: 100n,
233
- maxPrice: { getOnChainInt: 1_000_000n },
234
- owner: fromHex(''),
235
- },
236
- ],
237
- [
238
- mockUtxo(100n),
239
- {
240
- iasset: fromHex(fromText('iBTC')),
241
- lovelacesToSpend: 100n,
242
- maxPrice: { getOnChainInt: 1_000_000n },
267
+ orderType: {
268
+ BuyIAssetOrder: {
269
+ collateralAsset: adaAssetClass,
270
+ maxPrice: { numerator: 1n, denominator: 1n },
271
+ },
272
+ },
243
273
  owner: fromHex(''),
274
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
244
275
  },
245
276
  ],
246
277
  ];
247
278
 
279
+ const mockedShuffle = vi.fn().mockImplementation(() => lrps);
280
+
248
281
  expect(() =>
249
- randomLrpsSubsetSatisfyingTargetLovelaces(
282
+ randomRobsSubsetSatisfyingTargetCollateral(
250
283
  fromHex(fromText('iUSD')),
251
- 110n,
252
- { getOnChainInt: 1_000_000n },
284
+ adaAssetClass,
285
+ 100_000_000n,
286
+ { numerator: 1n, denominator: 1n },
253
287
  lrps,
254
- mockLrpParams,
255
288
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
289
+ mockedShuffle,
256
290
  ),
257
- ).toThrowError("Couldn't achieve target lovelaces");
291
+ ).toThrow(new Error("Couldn't achieve target lovelaces"));
258
292
  });
259
293
 
260
- test('filtering by price 1', () => {
294
+ test('5 (too small amount to redeem - payout 0)', () => {
261
295
  const lrps: [UTxO, RobDatum][] = [
262
296
  [
263
- mockUtxo(100n),
264
- {
265
- iasset: fromHex(fromText('iUSD')),
266
- lovelacesToSpend: 100n,
267
- maxPrice: { getOnChainInt: 1_500_000n },
268
- owner: fromHex(''),
269
- },
270
- ],
271
- [
272
- mockUtxo(100n),
297
+ mockUtxo(100_000_000n),
273
298
  {
274
299
  iasset: fromHex(fromText('iUSD')),
275
- lovelacesToSpend: 100n,
276
- maxPrice: { getOnChainInt: 1_000_000n },
300
+ orderType: {
301
+ BuyIAssetOrder: {
302
+ collateralAsset: adaAssetClass,
303
+ maxPrice: { numerator: 1n, denominator: 1n },
304
+ },
305
+ },
277
306
  owner: fromHex(''),
307
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
278
308
  },
279
309
  ],
280
310
  ];
281
311
 
282
312
  expect(() =>
283
- randomLrpsSubsetSatisfyingTargetLovelaces(
313
+ randomRobsSubsetSatisfyingTargetCollateral(
284
314
  fromHex(fromText('iUSD')),
285
- 120n,
286
- { getOnChainInt: 1_100_000n },
315
+ adaAssetClass,
316
+ 5n,
317
+ { numerator: 10n, denominator: 1n },
287
318
  lrps,
288
- mockLrpParams,
289
319
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
290
320
  ),
291
- ).toThrowError("Couldn't achieve target lovelaces");
321
+ ).toThrow('Must redeem and payout more than 0.');
292
322
  });
293
323
 
294
- test('filtering by price 2', () => {
324
+ test('6 (too small amount to redeem - redeemable 0)', () => {
295
325
  const lrps: [UTxO, RobDatum][] = [
296
326
  [
297
- mockUtxo(100n),
298
- {
299
- iasset: fromHex(fromText('iUSD')),
300
- lovelacesToSpend: 50n,
301
- maxPrice: { getOnChainInt: 1_500_000n },
302
- owner: fromHex(''),
303
- },
304
- ],
305
- [
306
- mockUtxo(100n),
307
- {
308
- iasset: fromHex(fromText('iUSD')),
309
- lovelacesToSpend: 100n,
310
- maxPrice: { getOnChainInt: 1_000_000n },
311
- owner: fromHex(''),
312
- },
313
- ],
314
- [
315
- mockUtxo(100n),
327
+ mockUtxo(100_000_000n),
316
328
  {
317
329
  iasset: fromHex(fromText('iUSD')),
318
- lovelacesToSpend: 100n,
319
- maxPrice: { getOnChainInt: 1_300_000n },
330
+ orderType: {
331
+ BuyIAssetOrder: {
332
+ collateralAsset: adaAssetClass,
333
+ maxPrice: { numerator: 1n, denominator: 1n },
334
+ },
335
+ },
320
336
  owner: fromHex(''),
337
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
321
338
  },
322
339
  ],
323
340
  ];
324
341
 
325
- expect(
326
- randomLrpsSubsetSatisfyingTargetLovelaces(
342
+ expect(() =>
343
+ randomRobsSubsetSatisfyingTargetCollateral(
327
344
  fromHex(fromText('iUSD')),
328
- 120n,
329
- { getOnChainInt: 1_100_000n },
345
+ adaAssetClass,
346
+ 0n,
347
+ { numerator: 1n, denominator: 1n },
330
348
  lrps,
331
- mockLrpParams,
332
349
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
333
350
  ),
334
- ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
351
+ ).toThrow('Must redeem and payout more than 0.');
335
352
  });
336
353
 
337
- test('min redemption check 1', () => {
354
+ test('7 (dont pick fully redeemed rob)', () => {
338
355
  const lrps: [UTxO, RobDatum][] = [
339
356
  [
340
- mockUtxo(100n),
357
+ mockUtxo(5n + MIN_ROB_COLLATERAL_AMT),
341
358
  {
342
359
  iasset: fromHex(fromText('iUSD')),
343
- lovelacesToSpend: 100n,
344
- maxPrice: { getOnChainInt: 1_000_000n },
360
+ orderType: {
361
+ BuyIAssetOrder: {
362
+ collateralAsset: adaAssetClass,
363
+ maxPrice: { numerator: 20n, denominator: 1n },
364
+ },
365
+ },
345
366
  owner: fromHex(''),
367
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
346
368
  },
347
369
  ],
348
370
  [
349
- mockUtxo(100n),
371
+ mockUtxo(100_000_000n),
350
372
  {
351
373
  iasset: fromHex(fromText('iUSD')),
352
- lovelacesToSpend: 100n,
353
- maxPrice: { getOnChainInt: 1_000_000n },
374
+ orderType: {
375
+ BuyIAssetOrder: {
376
+ collateralAsset: adaAssetClass,
377
+ maxPrice: { numerator: 1n, denominator: 1n },
378
+ },
379
+ },
354
380
  owner: fromHex(''),
381
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
355
382
  },
356
383
  ],
357
384
  ];
@@ -359,91 +386,110 @@ describe('randomLrpsSubsetSatisfyingTargetLovelaces', () => {
359
386
  const mockedShuffle = vi.fn().mockImplementation(() => lrps);
360
387
 
361
388
  expect(
362
- randomLrpsSubsetSatisfyingTargetLovelaces(
389
+ randomRobsSubsetSatisfyingTargetCollateral(
363
390
  fromHex(fromText('iUSD')),
364
- 105n,
365
- { getOnChainInt: 1_000_000n },
391
+ adaAssetClass,
392
+ 100_000n,
393
+ { numerator: 10n, denominator: 1n },
366
394
  lrps,
367
- mockLrpParams,
368
395
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
369
396
  mockedShuffle,
370
397
  ),
371
- ).toEqual([lrps[0]]);
398
+ ).toEqual(expect.arrayContaining([lrps[1]]));
372
399
  });
373
400
 
374
- test('min redemption check 2', () => {
401
+ test('8 (prevent redeeming 0 or payout 0 to any ROB)', () => {
375
402
  const lrps: [UTxO, RobDatum][] = [
376
403
  [
377
- mockUtxo(100n),
404
+ mockUtxo(100n + MIN_ROB_COLLATERAL_AMT),
378
405
  {
379
406
  iasset: fromHex(fromText('iUSD')),
380
- lovelacesToSpend: 100n,
381
- maxPrice: { getOnChainInt: 1_000_000n },
407
+ orderType: {
408
+ BuyIAssetOrder: {
409
+ collateralAsset: adaAssetClass,
410
+ maxPrice: { numerator: 20n, denominator: 1n },
411
+ },
412
+ },
382
413
  owner: fromHex(''),
414
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
383
415
  },
384
416
  ],
385
417
  [
386
- mockUtxo(5n),
418
+ mockUtxo(10n + MIN_ROB_COLLATERAL_AMT),
387
419
  {
388
420
  iasset: fromHex(fromText('iUSD')),
389
- lovelacesToSpend: 5n,
390
- maxPrice: { getOnChainInt: 1_000_000n },
421
+ orderType: {
422
+ BuyIAssetOrder: {
423
+ collateralAsset: adaAssetClass,
424
+ maxPrice: { numerator: 20n, denominator: 1n },
425
+ },
426
+ },
391
427
  owner: fromHex(''),
428
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
392
429
  },
393
430
  ],
394
431
  [
395
- mockUtxo(100n),
432
+ mockUtxo(30n + MIN_ROB_COLLATERAL_AMT),
396
433
  {
397
434
  iasset: fromHex(fromText('iUSD')),
398
- lovelacesToSpend: 100n,
399
- maxPrice: { getOnChainInt: 1_000_000n },
435
+ orderType: {
436
+ BuyIAssetOrder: {
437
+ collateralAsset: adaAssetClass,
438
+ maxPrice: { numerator: 20n, denominator: 1n },
439
+ },
440
+ },
400
441
  owner: fromHex(''),
442
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
401
443
  },
402
444
  ],
403
445
  ];
404
446
 
405
447
  const mockedShuffle = vi.fn().mockImplementation(() => lrps);
406
448
 
449
+ // Since the second ROB would cause the target redeemable unachievable because otherwise,
450
+ // the remaining ROB would payout 0.
451
+ // Since there's a larger ROB, replace the second one with the last one achieving the target in full.
407
452
  expect(
408
- randomLrpsSubsetSatisfyingTargetLovelaces(
453
+ randomRobsSubsetSatisfyingTargetCollateral(
409
454
  fromHex(fromText('iUSD')),
410
- 120n,
411
- { getOnChainInt: 1_000_000n },
455
+ adaAssetClass,
456
+ 115n,
457
+ { numerator: 10n, denominator: 1n },
412
458
  lrps,
413
- mockLrpParams,
414
459
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
415
460
  mockedShuffle,
416
461
  ),
417
- ).toEqual([lrps[0], lrps[2]]);
462
+ ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
418
463
  });
419
464
 
420
- test('min redemption check 3', () => {
465
+ test('9 (cant redeem more to achieve target because it would cause 0 payout)', () => {
421
466
  const lrps: [UTxO, RobDatum][] = [
422
467
  [
423
- mockUtxo(100n),
424
- {
425
- iasset: fromHex(fromText('iUSD')),
426
- lovelacesToSpend: 100n,
427
- maxPrice: { getOnChainInt: 1_000_000n },
428
- owner: fromHex(''),
429
- },
430
- ],
431
- [
432
- mockUtxo(15n),
468
+ mockUtxo(100n + MIN_ROB_COLLATERAL_AMT),
433
469
  {
434
470
  iasset: fromHex(fromText('iUSD')),
435
- lovelacesToSpend: 15n,
436
- maxPrice: { getOnChainInt: 1_000_000n },
471
+ orderType: {
472
+ BuyIAssetOrder: {
473
+ collateralAsset: adaAssetClass,
474
+ maxPrice: { numerator: 20n, denominator: 1n },
475
+ },
476
+ },
437
477
  owner: fromHex(''),
478
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
438
479
  },
439
480
  ],
440
481
  [
441
- mockUtxo(100n),
482
+ mockUtxo(100n + MIN_ROB_COLLATERAL_AMT),
442
483
  {
443
484
  iasset: fromHex(fromText('iUSD')),
444
- lovelacesToSpend: 100n,
445
- maxPrice: { getOnChainInt: 1_000_000n },
485
+ orderType: {
486
+ BuyIAssetOrder: {
487
+ collateralAsset: adaAssetClass,
488
+ maxPrice: { numerator: 20n, denominator: 1n },
489
+ },
490
+ },
446
491
  owner: fromHex(''),
492
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
447
493
  },
448
494
  ],
449
495
  ];
@@ -451,451 +497,1340 @@ describe('randomLrpsSubsetSatisfyingTargetLovelaces', () => {
451
497
  const mockedShuffle = vi.fn().mockImplementation(() => lrps);
452
498
 
453
499
  expect(
454
- randomLrpsSubsetSatisfyingTargetLovelaces(
500
+ randomRobsSubsetSatisfyingTargetCollateral(
455
501
  fromHex(fromText('iUSD')),
456
- 120n,
457
- { getOnChainInt: 1_000_000n },
502
+ adaAssetClass,
503
+ 105n,
504
+ { numerator: 10n, denominator: 1n },
458
505
  lrps,
459
- mockLrpParams,
460
506
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
461
507
  mockedShuffle,
462
508
  ),
463
- ).toEqual([lrps[0], lrps[2]]);
509
+ ).toEqual(expect.arrayContaining([lrps[0]]));
464
510
  });
465
511
 
466
- test('max redemptions check 1', () => {
512
+ test('filtering by iasset 1', () => {
467
513
  const lrps: [UTxO, RobDatum][] = [
468
514
  [
469
- mockUtxo(100n),
515
+ mockUtxo(100_000_000n),
470
516
  {
471
517
  iasset: fromHex(fromText('iUSD')),
472
- lovelacesToSpend: 100n,
473
- maxPrice: { getOnChainInt: 1_000_000n },
518
+ orderType: {
519
+ BuyIAssetOrder: {
520
+ collateralAsset: adaAssetClass,
521
+ maxPrice: { numerator: 1n, denominator: 1n },
522
+ },
523
+ },
474
524
  owner: fromHex(''),
525
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
475
526
  },
476
527
  ],
477
528
  [
478
- mockUtxo(90n),
529
+ mockUtxo(100_000_000n),
479
530
  {
480
- iasset: fromHex(fromText('iUSD')),
481
- lovelacesToSpend: 90n,
482
- maxPrice: { getOnChainInt: 1_000_000n },
531
+ iasset: fromHex(fromText('iBTC')),
532
+ orderType: {
533
+ BuyIAssetOrder: {
534
+ collateralAsset: adaAssetClass,
535
+ maxPrice: { numerator: 1n, denominator: 1n },
536
+ },
537
+ },
483
538
  owner: fromHex(''),
539
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
484
540
  },
485
541
  ],
486
542
  [
487
- mockUtxo(80n),
543
+ mockUtxo(100_000_000n),
488
544
  {
489
545
  iasset: fromHex(fromText('iUSD')),
490
- lovelacesToSpend: 80n,
491
- maxPrice: { getOnChainInt: 1_000_000n },
546
+ orderType: {
547
+ BuyIAssetOrder: {
548
+ collateralAsset: adaAssetClass,
549
+ maxPrice: { numerator: 1n, denominator: 1n },
550
+ },
551
+ },
492
552
  owner: fromHex(''),
553
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
493
554
  },
494
555
  ],
556
+ ];
557
+
558
+ expect(
559
+ randomRobsSubsetSatisfyingTargetCollateral(
560
+ fromHex(fromText('iUSD')),
561
+ adaAssetClass,
562
+ 110_000_000n,
563
+ { numerator: 1n, denominator: 1n },
564
+ lrps,
565
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
566
+ ),
567
+ ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
568
+ });
569
+
570
+ test('filtering by iasset 2', () => {
571
+ const lrps: [UTxO, RobDatum][] = [
495
572
  [
496
- mockUtxo(70n),
573
+ mockUtxo(100_000_000n),
497
574
  {
498
575
  iasset: fromHex(fromText('iUSD')),
499
- lovelacesToSpend: 70n,
500
- maxPrice: { getOnChainInt: 1_000_000n },
576
+ orderType: {
577
+ BuyIAssetOrder: {
578
+ collateralAsset: adaAssetClass,
579
+ maxPrice: { numerator: 1n, denominator: 1n },
580
+ },
581
+ },
501
582
  owner: fromHex(''),
583
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
502
584
  },
503
585
  ],
504
586
  [
505
- mockUtxo(100n),
587
+ mockUtxo(100_000_000n),
506
588
  {
507
- iasset: fromHex(fromText('iUSD')),
508
- lovelacesToSpend: 100n,
509
- maxPrice: { getOnChainInt: 1_000_000n },
589
+ iasset: fromHex(fromText('iBTC')),
590
+ orderType: {
591
+ BuyIAssetOrder: {
592
+ collateralAsset: adaAssetClass,
593
+ maxPrice: { numerator: 1n, denominator: 1n },
594
+ },
595
+ },
510
596
  owner: fromHex(''),
597
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
511
598
  },
512
599
  ],
513
600
  ];
514
601
 
515
- const mockedShuffle = vi.fn().mockImplementation(() => lrps);
516
-
517
- expect(MAX_REDEMPTIONS_WITH_CDP_OPEN).toBe(4);
518
-
519
- expect(
520
- randomLrpsSubsetSatisfyingTargetLovelaces(
602
+ expect(() =>
603
+ randomRobsSubsetSatisfyingTargetCollateral(
521
604
  fromHex(fromText('iUSD')),
522
- 360n,
523
- { getOnChainInt: 1_000_000n },
605
+ adaAssetClass,
606
+ 110_000_000n,
607
+ { numerator: 1n, denominator: 1n },
524
608
  lrps,
525
- mockLrpParams,
526
609
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
527
- mockedShuffle,
528
- ),
529
- ).toEqual(expect.arrayContaining([lrps[0], lrps[1], lrps[2], lrps[4]]));
530
- });
531
- });
532
-
533
- describe('lrpRedeemableLovelacesInclReimb', () => {
534
- const mockUtxo = (ada: bigint): UTxO => ({
535
- address: '',
536
- assets: mkLovelacesOf(ada),
537
- outputIndex: 0,
538
- txHash: '',
539
- });
540
-
541
- const mockLrpParams: RobParamsSP = {
542
- iassetNft: [{ unCurrencySymbol: '' }, { unTokenName: '' }],
543
- iassetPolicyId: { unCurrencySymbol: '' },
544
- minRedemptionLovelacesAmt: 10n,
545
- versionRecordToken: [{ unCurrencySymbol: '' }, { unTokenName: '' }],
546
- iassetValHash: '',
547
- };
548
-
549
- test('1', () => {
550
- expect(
551
- lrpRedeemableLovelacesInclReimb(
552
- [
553
- mockUtxo(110n),
554
- {
555
- iasset: fromHex(fromText('iUSD')),
556
- lovelacesToSpend: 100n,
557
- maxPrice: { getOnChainInt: 1_000_000n },
558
- owner: fromHex(''),
559
- },
560
- ],
561
- mockLrpParams,
562
- ),
563
- ).toEqual<bigint>(100n);
564
- });
565
-
566
- test('capped to UTXO value', () => {
567
- expect(
568
- lrpRedeemableLovelacesInclReimb(
569
- [
570
- mockUtxo(20_000_000n),
571
- {
572
- iasset: fromHex(fromText('iUSD')),
573
- lovelacesToSpend: 100_000_000n,
574
- maxPrice: { getOnChainInt: 1_000_000n },
575
- owner: fromHex(''),
576
- },
577
- ],
578
- mockLrpParams,
579
- ),
580
- ).toEqual<bigint>(20_000_000n - MIN_LRP_COLLATERAL_AMT);
581
- });
582
-
583
- test('less than min redemption', () => {
584
- expect(
585
- lrpRedeemableLovelacesInclReimb(
586
- [
587
- mockUtxo(20n),
588
- {
589
- iasset: fromHex(fromText('iUSD')),
590
- lovelacesToSpend: 5n,
591
- maxPrice: { getOnChainInt: 1_000_000n },
592
- owner: fromHex(''),
593
- },
594
- ],
595
- mockLrpParams,
596
610
  ),
597
- ).toEqual<bigint>(0n);
598
- });
599
- });
600
-
601
- describe('calculateTotalAdaForRedemption', () => {
602
- const mockUtxo = (ada: bigint): UTxO => ({
603
- address: '',
604
- assets: mkLovelacesOf(ada),
605
- outputIndex: 0,
606
- txHash: '',
611
+ ).toThrow("Couldn't achieve target lovelaces");
607
612
  });
608
613
 
609
- const mockLrpParams: RobParamsSP = {
610
- iassetNft: [{ unCurrencySymbol: '' }, { unTokenName: '' }],
611
- iassetPolicyId: { unCurrencySymbol: '' },
612
- minRedemptionLovelacesAmt: 10n,
613
- versionRecordToken: [{ unCurrencySymbol: '' }, { unTokenName: '' }],
614
- iassetValHash: '',
615
- };
616
-
617
- test('1', () => {
614
+ test('filtering by collateral asset 1', () => {
618
615
  const lrps: [UTxO, RobDatum][] = [
619
616
  [
620
- mockUtxo(100n),
617
+ mockUtxo(100_000_000n, mkAssetsOf(collateralAssetA, 100n)),
621
618
  {
622
619
  iasset: fromHex(fromText('iUSD')),
623
- lovelacesToSpend: 100n,
624
- maxPrice: { getOnChainInt: 1_000_000n },
620
+ orderType: {
621
+ BuyIAssetOrder: {
622
+ collateralAsset: collateralAssetA,
623
+ maxPrice: { numerator: 1n, denominator: 1n },
624
+ },
625
+ },
625
626
  owner: fromHex(''),
627
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
626
628
  },
627
629
  ],
628
630
  [
629
- mockUtxo(100n),
631
+ mockUtxo(100_000_000n),
630
632
  {
631
633
  iasset: fromHex(fromText('iUSD')),
632
- lovelacesToSpend: 100n,
633
- maxPrice: { getOnChainInt: 1_000_000n },
634
+ orderType: {
635
+ BuyIAssetOrder: {
636
+ collateralAsset: adaAssetClass,
637
+ maxPrice: { numerator: 1n, denominator: 1n },
638
+ },
639
+ },
634
640
  owner: fromHex(''),
641
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
635
642
  },
636
643
  ],
637
- ];
638
-
639
- expect(
640
- calculateTotalAdaForRedemption(
641
- fromHex(fromText('iUSD')),
642
- { getOnChainInt: 1_000_000n },
643
- mockLrpParams,
644
- lrps,
645
- MAX_REDEMPTIONS_WITH_CDP_OPEN,
646
- ),
647
- // Because of rounding, the reimbursement isn't subtracted
648
- ).toEqual<bigint>(200n);
649
- });
650
-
651
- test('2', () => {
652
- const lrps: [UTxO, RobDatum][] = [
653
644
  [
654
- mockUtxo(1000n),
645
+ mockUtxo(100_000_000n),
655
646
  {
656
- iasset: fromHex(fromText('iUSD')),
657
- lovelacesToSpend: 1000n,
658
- maxPrice: { getOnChainInt: 1_000_000n },
647
+ iasset: fromHex(fromText('iBTC')),
648
+ orderType: {
649
+ BuyIAssetOrder: {
650
+ collateralAsset: adaAssetClass,
651
+ maxPrice: { numerator: 1n, denominator: 1n },
652
+ },
653
+ },
659
654
  owner: fromHex(''),
655
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
660
656
  },
661
657
  ],
662
658
  [
663
- mockUtxo(1000n),
659
+ mockUtxo(100_000_000n, mkAssetsOf(collateralAssetA, 100n)),
664
660
  {
665
661
  iasset: fromHex(fromText('iUSD')),
666
- lovelacesToSpend: 1000n,
667
- maxPrice: { getOnChainInt: 1_000_000n },
662
+ orderType: {
663
+ BuyIAssetOrder: {
664
+ collateralAsset: collateralAssetA,
665
+ maxPrice: { numerator: 1n, denominator: 1n },
666
+ },
667
+ },
668
668
  owner: fromHex(''),
669
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
669
670
  },
670
671
  ],
671
672
  ];
672
673
 
673
674
  expect(
674
- calculateTotalAdaForRedemption(
675
+ randomRobsSubsetSatisfyingTargetCollateral(
675
676
  fromHex(fromText('iUSD')),
676
- { getOnChainInt: 1_000_000n },
677
- mockLrpParams,
677
+ collateralAssetA,
678
+ 110n,
679
+ { numerator: 1n, denominator: 1n },
678
680
  lrps,
679
681
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
680
682
  ),
681
- ).toEqual<bigint>(2000n);
683
+ ).toEqual(expect.arrayContaining([lrps[0], lrps[3]]));
682
684
  });
683
685
 
684
- test('filtering by assets 1', () => {
686
+ test('filtering by collateral asset 2', () => {
685
687
  const lrps: [UTxO, RobDatum][] = [
686
688
  [
687
- mockUtxo(1000n),
689
+ mockUtxo(100n),
688
690
  {
689
691
  iasset: fromHex(fromText('iUSD')),
690
- lovelacesToSpend: 1000n,
691
- maxPrice: { getOnChainInt: 1_000_000n },
692
+ orderType: {
693
+ BuyIAssetOrder: {
694
+ collateralAsset: adaAssetClass,
695
+ maxPrice: { numerator: 1n, denominator: 1n },
696
+ },
697
+ },
692
698
  owner: fromHex(''),
699
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
693
700
  },
694
701
  ],
695
702
  [
696
- mockUtxo(1000n),
703
+ mockUtxo(100n),
704
+ {
705
+ iasset: fromHex(fromText('iUSD')),
706
+ orderType: {
707
+ BuyIAssetOrder: {
708
+ collateralAsset: adaAssetClass,
709
+ maxPrice: { numerator: 1n, denominator: 1n },
710
+ },
711
+ },
712
+ owner: fromHex(''),
713
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
714
+ },
715
+ ],
716
+ [
717
+ mockUtxo(100n),
697
718
  {
698
719
  iasset: fromHex(fromText('iBTC')),
699
- lovelacesToSpend: 1000n,
700
- maxPrice: { getOnChainInt: 1_000_000n },
720
+ orderType: {
721
+ BuyIAssetOrder: {
722
+ collateralAsset: adaAssetClass,
723
+ maxPrice: { numerator: 1n, denominator: 1n },
724
+ },
725
+ },
701
726
  owner: fromHex(''),
727
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
702
728
  },
703
729
  ],
704
730
  [
705
- mockUtxo(1000n),
731
+ mockUtxo(100n, mkAssetsOf(collateralAssetA, 100n)),
706
732
  {
707
- iasset: fromHex(fromText('iETH')),
708
- lovelacesToSpend: 1000n,
709
- maxPrice: { getOnChainInt: 1_000_000n },
733
+ iasset: fromHex(fromText('iUSD')),
734
+ orderType: {
735
+ BuyIAssetOrder: {
736
+ collateralAsset: collateralAssetA,
737
+ maxPrice: { numerator: 1n, denominator: 1n },
738
+ },
739
+ },
710
740
  owner: fromHex(''),
741
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
711
742
  },
712
743
  ],
713
744
  ];
714
745
 
715
- expect(
716
- calculateTotalAdaForRedemption(
746
+ expect(() =>
747
+ randomRobsSubsetSatisfyingTargetCollateral(
717
748
  fromHex(fromText('iUSD')),
718
- { getOnChainInt: 1_000_000n },
719
- mockLrpParams,
749
+ collateralAssetA,
750
+ 110n,
751
+ { numerator: 1n, denominator: 1n },
720
752
  lrps,
721
753
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
722
754
  ),
723
- ).toEqual<bigint>(1000n);
755
+ ).toThrow("Couldn't achieve target lovelaces");
724
756
  });
725
757
 
726
758
  test('filtering by price 1', () => {
727
759
  const lrps: [UTxO, RobDatum][] = [
728
760
  [
729
- mockUtxo(1000n),
761
+ mockUtxo(100n),
730
762
  {
731
763
  iasset: fromHex(fromText('iUSD')),
732
- lovelacesToSpend: 1000n,
733
- maxPrice: { getOnChainInt: 1_000_000n },
764
+ orderType: {
765
+ BuyIAssetOrder: {
766
+ collateralAsset: collateralAssetA,
767
+ maxPrice: { numerator: 15n, denominator: 10n },
768
+ },
769
+ },
734
770
  owner: fromHex(''),
771
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
735
772
  },
736
773
  ],
737
774
  [
738
- mockUtxo(1000n),
775
+ mockUtxo(100n),
739
776
  {
740
777
  iasset: fromHex(fromText('iUSD')),
741
- lovelacesToSpend: 1000n,
742
- maxPrice: { getOnChainInt: 1_500_000n },
778
+ orderType: {
779
+ BuyIAssetOrder: {
780
+ collateralAsset: collateralAssetA,
781
+ maxPrice: { numerator: 1n, denominator: 1n },
782
+ },
783
+ },
743
784
  owner: fromHex(''),
785
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
744
786
  },
745
787
  ],
746
- [
747
- mockUtxo(1000n),
748
- {
749
- iasset: fromHex(fromText('iUSD')),
750
- lovelacesToSpend: 1000n,
751
- maxPrice: { getOnChainInt: 800_000n },
752
- owner: fromHex(''),
788
+ ];
789
+
790
+ expect(() =>
791
+ randomRobsSubsetSatisfyingTargetCollateral(
792
+ fromHex(fromText('iUSD')),
793
+ adaAssetClass,
794
+ 120n,
795
+ { numerator: 1n, denominator: 1n },
796
+ lrps,
797
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
798
+ ),
799
+ ).toThrow("Couldn't achieve target lovelaces");
800
+ });
801
+
802
+ test('filtering by price 2', () => {
803
+ const lrps: [UTxO, RobDatum][] = [
804
+ [
805
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
806
+ {
807
+ iasset: fromHex(fromText('iUSD')),
808
+ orderType: {
809
+ BuyIAssetOrder: {
810
+ collateralAsset: collateralAssetA,
811
+ maxPrice: { numerator: 15n, denominator: 10n },
812
+ },
813
+ },
814
+ owner: fromHex(''),
815
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
816
+ },
817
+ ],
818
+ [
819
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
820
+ {
821
+ iasset: fromHex(fromText('iUSD')),
822
+ orderType: {
823
+ BuyIAssetOrder: {
824
+ collateralAsset: collateralAssetA,
825
+ maxPrice: { numerator: 1n, denominator: 1n },
826
+ },
827
+ },
828
+ owner: fromHex(''),
829
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
830
+ },
831
+ ],
832
+ [
833
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
834
+ {
835
+ iasset: fromHex(fromText('iUSD')),
836
+ orderType: {
837
+ BuyIAssetOrder: {
838
+ collateralAsset: collateralAssetA,
839
+ maxPrice: { numerator: 13n, denominator: 10n },
840
+ },
841
+ },
842
+ owner: fromHex(''),
843
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
753
844
  },
754
845
  ],
755
846
  ];
756
847
 
757
848
  expect(
758
- calculateTotalAdaForRedemption(
849
+ randomRobsSubsetSatisfyingTargetCollateral(
759
850
  fromHex(fromText('iUSD')),
760
- { getOnChainInt: 1_100_000n },
761
- mockLrpParams,
851
+ collateralAssetA,
852
+ 120n,
853
+ { numerator: 11n, denominator: 10n },
762
854
  lrps,
763
855
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
764
856
  ),
765
- ).toEqual<bigint>(1000n);
857
+ ).toEqual(expect.arrayContaining([lrps[0], lrps[2]]));
766
858
  });
767
859
 
768
- test('capping by max redemptions 1', () => {
860
+ test('max redemptions check 1', () => {
769
861
  const lrps: [UTxO, RobDatum][] = [
770
862
  [
771
- mockUtxo(1000n),
863
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
772
864
  {
773
865
  iasset: fromHex(fromText('iUSD')),
774
- lovelacesToSpend: 1000n,
775
- maxPrice: { getOnChainInt: 1_000_000n },
866
+ orderType: {
867
+ BuyIAssetOrder: {
868
+ collateralAsset: collateralAssetA,
869
+ maxPrice: { numerator: 13n, denominator: 10n },
870
+ },
871
+ },
776
872
  owner: fromHex(''),
873
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
777
874
  },
778
875
  ],
779
876
  [
780
- mockUtxo(1400n),
877
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 90n)),
781
878
  {
782
879
  iasset: fromHex(fromText('iUSD')),
783
- lovelacesToSpend: 1400n,
784
- maxPrice: { getOnChainInt: 1_000_000n },
880
+ orderType: {
881
+ BuyIAssetOrder: {
882
+ collateralAsset: collateralAssetA,
883
+ maxPrice: { numerator: 13n, denominator: 10n },
884
+ },
885
+ },
785
886
  owner: fromHex(''),
887
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
786
888
  },
787
889
  ],
788
890
  [
789
- mockUtxo(1600n),
891
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 80n)),
790
892
  {
791
893
  iasset: fromHex(fromText('iUSD')),
792
- lovelacesToSpend: 1600n,
793
- maxPrice: { getOnChainInt: 1_000_000n },
894
+ orderType: {
895
+ BuyIAssetOrder: {
896
+ collateralAsset: collateralAssetA,
897
+ maxPrice: { numerator: 13n, denominator: 10n },
898
+ },
899
+ },
794
900
  owner: fromHex(''),
901
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
795
902
  },
796
903
  ],
797
904
  [
798
- mockUtxo(1800n),
905
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 70n)),
799
906
  {
800
907
  iasset: fromHex(fromText('iUSD')),
801
- lovelacesToSpend: 1800n,
802
- maxPrice: { getOnChainInt: 1_000_000n },
908
+ orderType: {
909
+ BuyIAssetOrder: {
910
+ collateralAsset: collateralAssetA,
911
+ maxPrice: { numerator: 13n, denominator: 10n },
912
+ },
913
+ },
803
914
  owner: fromHex(''),
915
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
804
916
  },
805
917
  ],
806
918
  [
807
- mockUtxo(2000n),
919
+ mockUtxo(10n, mkAssetsOf(collateralAssetA, 100n)),
808
920
  {
809
921
  iasset: fromHex(fromText('iUSD')),
810
- lovelacesToSpend: 2000n,
811
- maxPrice: { getOnChainInt: 1_000_000n },
922
+ orderType: {
923
+ BuyIAssetOrder: {
924
+ collateralAsset: collateralAssetA,
925
+ maxPrice: { numerator: 13n, denominator: 10n },
926
+ },
927
+ },
812
928
  owner: fromHex(''),
929
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
813
930
  },
814
931
  ],
815
932
  ];
816
933
 
934
+ const mockedShuffle = vi.fn().mockImplementation(() => lrps);
935
+
817
936
  expect(MAX_REDEMPTIONS_WITH_CDP_OPEN).toBe(4);
818
937
 
938
+ // replaces the lrps[3] with lrps[4] since it's larger and already hitting max redemptions
819
939
  expect(
820
- calculateTotalAdaForRedemption(
940
+ randomRobsSubsetSatisfyingTargetCollateral(
821
941
  fromHex(fromText('iUSD')),
822
- { getOnChainInt: 1_000_000n },
823
- mockLrpParams,
942
+ collateralAssetA,
943
+ 360n,
944
+ { numerator: 1n, denominator: 1n },
824
945
  lrps,
825
946
  MAX_REDEMPTIONS_WITH_CDP_OPEN,
947
+ mockedShuffle,
826
948
  ),
827
- // I.e. the one with 1000n lovelaces is dropped
828
- ).toEqual<bigint>(6800n);
949
+ ).toEqual(expect.arrayContaining([lrps[0], lrps[1], lrps[2], lrps[4]]));
950
+ });
951
+ });
952
+
953
+ describe('robAmountToSpend', () => {
954
+ const mockUtxo = (ada: bigint, otherAssets: Assets = {}): UTxO => ({
955
+ address: '',
956
+ assets: addAssets(mkLovelacesOf(ada), otherAssets),
957
+ outputIndex: 0,
958
+ txHash: '',
959
+ });
960
+
961
+ test('1', () => {
962
+ expect(
963
+ robCollateralAmtToSpend(
964
+ mockUtxo(100n, mkAssetsOf(collateralAssetA, 30n)),
965
+ {
966
+ iasset: fromHex(fromText('iUSD')),
967
+ orderType: {
968
+ BuyIAssetOrder: {
969
+ collateralAsset: collateralAssetA,
970
+ maxPrice: { numerator: 13n, denominator: 10n },
971
+ },
972
+ },
973
+ owner: fromHex(''),
974
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
975
+ },
976
+ ),
977
+ ).toEqual<bigint>(30n);
978
+ });
979
+
980
+ test('2', () => {
981
+ expect(
982
+ robCollateralAmtToSpend(mockUtxo(20_000_000n), {
983
+ iasset: fromHex(fromText('iUSD')),
984
+ orderType: {
985
+ BuyIAssetOrder: {
986
+ collateralAsset: adaAssetClass,
987
+ maxPrice: { numerator: 13n, denominator: 10n },
988
+ },
989
+ },
990
+ owner: fromHex(''),
991
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
992
+ }),
993
+ ).toEqual<bigint>(20_000_000n - MIN_ROB_COLLATERAL_AMT);
994
+ });
995
+
996
+ test('Less than min rob collateral', () => {
997
+ expect(
998
+ robCollateralAmtToSpend(mockUtxo(2_000_000n), {
999
+ iasset: fromHex(fromText('iUSD')),
1000
+ orderType: {
1001
+ BuyIAssetOrder: {
1002
+ collateralAsset: adaAssetClass,
1003
+ maxPrice: { numerator: 13n, denominator: 10n },
1004
+ },
1005
+ },
1006
+ owner: fromHex(''),
1007
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1008
+ }),
1009
+ ).toEqual<bigint>(0n);
1010
+ });
1011
+ });
1012
+
1013
+ describe('calculateTotalCollateralForRedemption', () => {
1014
+ const mockUtxo = (ada: bigint, otherAssets: Assets = {}): UTxO => ({
1015
+ address: '',
1016
+ assets: addAssets(mkLovelacesOf(ada), otherAssets),
1017
+ outputIndex: 0,
1018
+ txHash: '',
829
1019
  });
830
1020
 
831
- test('incorrectly initialised LRPs 1', () => {
1021
+ test('1', () => {
832
1022
  const lrps: [UTxO, RobDatum][] = [
833
- // This one should be capped to the UTXO value
834
1023
  [
835
- mockUtxo(20_000_000n),
1024
+ mockUtxo(100_000_000n),
836
1025
  {
837
1026
  iasset: fromHex(fromText('iUSD')),
838
- lovelacesToSpend: 100_000_000n,
839
- maxPrice: { getOnChainInt: 1_000_000n },
1027
+ orderType: {
1028
+ BuyIAssetOrder: {
1029
+ collateralAsset: adaAssetClass,
1030
+ maxPrice: { numerator: 13n, denominator: 10n },
1031
+ },
1032
+ },
840
1033
  owner: fromHex(''),
1034
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
841
1035
  },
842
1036
  ],
843
1037
  [
844
- mockUtxo(1000n),
1038
+ mockUtxo(100_000_000n),
845
1039
  {
846
1040
  iasset: fromHex(fromText('iUSD')),
847
- lovelacesToSpend: 1000n,
848
- maxPrice: { getOnChainInt: 1_000_000n },
1041
+ orderType: {
1042
+ BuyIAssetOrder: {
1043
+ collateralAsset: adaAssetClass,
1044
+ maxPrice: { numerator: 13n, denominator: 10n },
1045
+ },
1046
+ },
849
1047
  owner: fromHex(''),
1048
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
850
1049
  },
851
1050
  ],
1051
+ ];
1052
+
1053
+ expect(
1054
+ calculateTotalCollateralForRedemption(
1055
+ fromHex(fromText('iUSD')),
1056
+ adaAssetClass,
1057
+ { numerator: 1n, denominator: 1n },
1058
+ lrps,
1059
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
1060
+ ),
1061
+ ).toEqual<bigint>(200_000_000n - MIN_ROB_COLLATERAL_AMT * 2n);
1062
+ });
1063
+
1064
+ test('2', () => {
1065
+ const lrps: [UTxO, RobDatum][] = [
852
1066
  [
853
- mockUtxo(1000n),
1067
+ mockUtxo(100_000_000n),
854
1068
  {
855
1069
  iasset: fromHex(fromText('iUSD')),
856
- lovelacesToSpend: 1000n,
857
- maxPrice: { getOnChainInt: 1_000_000n },
1070
+ orderType: {
1071
+ BuyIAssetOrder: {
1072
+ collateralAsset: adaAssetClass,
1073
+ maxPrice: { numerator: 13n, denominator: 10n },
1074
+ },
1075
+ },
858
1076
  owner: fromHex(''),
1077
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
859
1078
  },
860
1079
  ],
861
- // This one shold get dropped since less than min
862
1080
  [
863
1081
  mockUtxo(1000n),
864
1082
  {
865
1083
  iasset: fromHex(fromText('iUSD')),
866
- lovelacesToSpend: 5n,
867
- maxPrice: { getOnChainInt: 1_000_000n },
1084
+ orderType: {
1085
+ BuyIAssetOrder: {
1086
+ collateralAsset: collateralAssetA,
1087
+ maxPrice: { numerator: 13n, denominator: 10n },
1088
+ },
1089
+ },
1090
+ owner: fromHex(''),
1091
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1092
+ },
1093
+ ],
1094
+ ];
1095
+
1096
+ expect(
1097
+ calculateTotalCollateralForRedemption(
1098
+ fromHex(fromText('iUSD')),
1099
+ adaAssetClass,
1100
+ { numerator: 1n, denominator: 1n },
1101
+ lrps,
1102
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
1103
+ ),
1104
+ ).toEqual<bigint>(100_000_000n - MIN_ROB_COLLATERAL_AMT);
1105
+ });
1106
+
1107
+ test('filtering by assets 1', () => {
1108
+ const lrps: [UTxO, RobDatum][] = [
1109
+ [
1110
+ mockUtxo(100_000_000n),
1111
+ {
1112
+ iasset: fromHex(fromText('iUSD')),
1113
+ orderType: {
1114
+ BuyIAssetOrder: {
1115
+ collateralAsset: adaAssetClass,
1116
+ maxPrice: { numerator: 13n, denominator: 10n },
1117
+ },
1118
+ },
1119
+ owner: fromHex(''),
1120
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1121
+ },
1122
+ ],
1123
+ [
1124
+ mockUtxo(100_000_000n),
1125
+ {
1126
+ iasset: fromHex(fromText('iBTC')),
1127
+ orderType: {
1128
+ BuyIAssetOrder: {
1129
+ collateralAsset: adaAssetClass,
1130
+ maxPrice: { numerator: 13n, denominator: 10n },
1131
+ },
1132
+ },
1133
+ owner: fromHex(''),
1134
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1135
+ },
1136
+ ],
1137
+ [
1138
+ mockUtxo(100_000_000n),
1139
+ {
1140
+ iasset: fromHex(fromText('iETH')),
1141
+ orderType: {
1142
+ BuyIAssetOrder: {
1143
+ collateralAsset: adaAssetClass,
1144
+ maxPrice: { numerator: 13n, denominator: 10n },
1145
+ },
1146
+ },
1147
+ owner: fromHex(''),
1148
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1149
+ },
1150
+ ],
1151
+ ];
1152
+
1153
+ expect(
1154
+ calculateTotalCollateralForRedemption(
1155
+ fromHex(fromText('iUSD')),
1156
+ adaAssetClass,
1157
+ { numerator: 1n, denominator: 1n },
1158
+ lrps,
1159
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
1160
+ ),
1161
+ ).toEqual<bigint>(100_000_000n - MIN_ROB_COLLATERAL_AMT);
1162
+ });
1163
+
1164
+ test('filtering by price 1', () => {
1165
+ const lrps: [UTxO, RobDatum][] = [
1166
+ [
1167
+ mockUtxo(1000_000_000n),
1168
+ {
1169
+ iasset: fromHex(fromText('iUSD')),
1170
+ orderType: {
1171
+ BuyIAssetOrder: {
1172
+ collateralAsset: adaAssetClass,
1173
+ maxPrice: { numerator: 1n, denominator: 1n },
1174
+ },
1175
+ },
1176
+ owner: fromHex(''),
1177
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1178
+ },
1179
+ ],
1180
+ [
1181
+ mockUtxo(1000_000_000n),
1182
+ {
1183
+ iasset: fromHex(fromText('iUSD')),
1184
+ orderType: {
1185
+ BuyIAssetOrder: {
1186
+ collateralAsset: adaAssetClass,
1187
+ maxPrice: { numerator: 15n, denominator: 10n },
1188
+ },
1189
+ },
1190
+ owner: fromHex(''),
1191
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1192
+ },
1193
+ ],
1194
+ [
1195
+ mockUtxo(1000_000_000n),
1196
+ {
1197
+ iasset: fromHex(fromText('iUSD')),
1198
+ orderType: {
1199
+ BuyIAssetOrder: {
1200
+ collateralAsset: adaAssetClass,
1201
+ maxPrice: { numerator: 8n, denominator: 10n },
1202
+ },
1203
+ },
1204
+ owner: fromHex(''),
1205
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1206
+ },
1207
+ ],
1208
+ ];
1209
+
1210
+ expect(
1211
+ calculateTotalCollateralForRedemption(
1212
+ fromHex(fromText('iUSD')),
1213
+ adaAssetClass,
1214
+ { numerator: 11n, denominator: 10n },
1215
+ lrps,
1216
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
1217
+ ),
1218
+ ).toEqual<bigint>(1000_000_000n - MIN_ROB_COLLATERAL_AMT);
1219
+ });
1220
+
1221
+ test('capping by max redemptions 1', () => {
1222
+ const lrps: [UTxO, RobDatum][] = [
1223
+ [
1224
+ mockUtxo(100_000_000n),
1225
+ {
1226
+ iasset: fromHex(fromText('iUSD')),
1227
+ orderType: {
1228
+ BuyIAssetOrder: {
1229
+ collateralAsset: adaAssetClass,
1230
+ maxPrice: { numerator: 1n, denominator: 1n },
1231
+ },
1232
+ },
1233
+ owner: fromHex(''),
1234
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1235
+ },
1236
+ ],
1237
+ [
1238
+ mockUtxo(140_000_000n),
1239
+ {
1240
+ iasset: fromHex(fromText('iUSD')),
1241
+ orderType: {
1242
+ BuyIAssetOrder: {
1243
+ collateralAsset: adaAssetClass,
1244
+ maxPrice: { numerator: 1n, denominator: 1n },
1245
+ },
1246
+ },
1247
+ owner: fromHex(''),
1248
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1249
+ },
1250
+ ],
1251
+ [
1252
+ mockUtxo(160_000_000n),
1253
+ {
1254
+ iasset: fromHex(fromText('iUSD')),
1255
+ orderType: {
1256
+ BuyIAssetOrder: {
1257
+ collateralAsset: adaAssetClass,
1258
+ maxPrice: { numerator: 1n, denominator: 1n },
1259
+ },
1260
+ },
1261
+ owner: fromHex(''),
1262
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1263
+ },
1264
+ ],
1265
+ [
1266
+ mockUtxo(180_000_000n),
1267
+ {
1268
+ iasset: fromHex(fromText('iUSD')),
1269
+ orderType: {
1270
+ BuyIAssetOrder: {
1271
+ collateralAsset: adaAssetClass,
1272
+ maxPrice: { numerator: 1n, denominator: 1n },
1273
+ },
1274
+ },
1275
+ owner: fromHex(''),
1276
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
1277
+ },
1278
+ ],
1279
+ [
1280
+ mockUtxo(200_000_000n),
1281
+ {
1282
+ iasset: fromHex(fromText('iUSD')),
1283
+ orderType: {
1284
+ BuyIAssetOrder: {
1285
+ collateralAsset: adaAssetClass,
1286
+ maxPrice: { numerator: 1n, denominator: 1n },
1287
+ },
1288
+ },
868
1289
  owner: fromHex(''),
1290
+ robRefInput: { outputIndex: 0n, txHash: fromHex('') },
869
1291
  },
870
1292
  ],
871
1293
  ];
872
1294
 
873
- expect(
874
- calculateTotalAdaForRedemption(
875
- fromHex(fromText('iUSD')),
876
- { getOnChainInt: 1_000_000n },
877
- mockLrpParams,
878
- lrps,
879
- MAX_REDEMPTIONS_WITH_CDP_OPEN,
1295
+ expect(MAX_REDEMPTIONS_WITH_CDP_OPEN).toBe(4);
1296
+
1297
+ expect(
1298
+ calculateTotalCollateralForRedemption(
1299
+ fromHex(fromText('iUSD')),
1300
+ adaAssetClass,
1301
+ { numerator: 1n, denominator: 1n },
1302
+ lrps,
1303
+ MAX_REDEMPTIONS_WITH_CDP_OPEN,
1304
+ ),
1305
+ // I.e. the one with 1000n lovelaces is dropped
1306
+ ).toEqual<bigint>(680_000_000n - MIN_ROB_COLLATERAL_AMT * 4n);
1307
+ });
1308
+ });
1309
+
1310
+ describe('LRP leverage', () => {
1311
+ beforeEach<MyContext>(async (context: MyContext) => {
1312
+ context.users = {
1313
+ admin: generateEmulatorAccount(
1314
+ addAssets(
1315
+ mkLovelacesOf(100_000_000_000_000n),
1316
+ mkAssetsOf(collateralAssetA, 100_000_000_000n),
1317
+ ),
1318
+ ),
1319
+ user: generateEmulatorAccount(addAssets(mkLovelacesOf(150_000_000n))),
1320
+ };
1321
+
1322
+ context.emulator = new Emulator(
1323
+ [context.users.admin, context.users.user],
1324
+ MAINNET_PROTOCOL_PARAMETERS,
1325
+ );
1326
+ context.lucid = await Lucid(context.emulator, 'Custom');
1327
+ });
1328
+
1329
+ test<MyContext>('Open 2x leveraged CDP; 1 LRP; price ~1.1; f_r=.01; f_m=.005', async (context: MyContext) => {
1330
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1331
+
1332
+ const [sysParams, [iusdAssetInfo]] = await init(
1333
+ context.lucid,
1334
+ [
1335
+ {
1336
+ ...iusdInitialAssetCfg(),
1337
+ collateralAssets: [
1338
+ {
1339
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
1340
+ numerator: 1_104_093n,
1341
+ denominator: 1_000_000n,
1342
+ }),
1343
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
1344
+ },
1345
+ ],
1346
+ debtMintingFeeRatio: { numerator: 1n, denominator: 200n },
1347
+ redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
1348
+ },
1349
+ ],
1350
+ context.emulator.slot,
1351
+ );
1352
+
1353
+ await openBuyRobs(
1354
+ context,
1355
+ sysParams,
1356
+ iusdAssetInfo.iassetTokenNameAscii,
1357
+ adaAssetClass,
1358
+ [100_000_000n],
1359
+ { numerator: 15n, denominator: 10n },
1360
+ );
1361
+
1362
+ const allRobs = await findAllRobs(
1363
+ context.lucid,
1364
+ sysParams,
1365
+ iusdAssetInfo.iassetTokenNameAscii,
1366
+ );
1367
+
1368
+ const orefs = await findAllNecessaryOrefs(
1369
+ context.lucid,
1370
+ sysParams,
1371
+ iusdAssetInfo.iassetTokenNameAscii,
1372
+ adaAssetClass,
1373
+ );
1374
+
1375
+ const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1376
+ context.lucid,
1377
+ orefs.collateralAsset,
1378
+ );
1379
+
1380
+ if (priceOracleUtxo == null) {
1381
+ throw new Error('Expected oracle UTXO');
1382
+ }
1383
+
1384
+ const baseCollateral = 20_000_000n;
1385
+
1386
+ await benchmarkAndAwaitTx(
1387
+ 'Leverage - CDP open with 1 LRP',
1388
+ await leverageCdpWithRob(
1389
+ 2,
1390
+ baseCollateral,
1391
+ priceOracleUtxo,
1392
+ orefs.iasset.utxo,
1393
+ orefs.collateralAsset.utxo,
1394
+ orefs.cdpCreatorUtxo,
1395
+ orefs.interestOracleUtxo,
1396
+ orefs.treasuryUtxo,
1397
+ sysParams,
1398
+ context.lucid,
1399
+ allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
1400
+ context.emulator.slot,
1401
+ ),
1402
+ context.lucid,
1403
+ context.emulator,
1404
+ );
1405
+
1406
+ const [pkh, skh] = await addrDetails(context.lucid);
1407
+
1408
+ const res = await findCdp(
1409
+ context.lucid,
1410
+ sysParams.validatorHashes.cdpHash,
1411
+ fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
1412
+ pkh.hash,
1413
+ skh,
1414
+ );
1415
+
1416
+ // Assert leverage
1417
+ assertValueInRange(
1418
+ Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
1419
+ {
1420
+ min: 1.999,
1421
+ max: 2.0,
1422
+ },
1423
+ );
1424
+
1425
+ // Assert collateral ratio
1426
+ assertValueInRange(
1427
+ cdpCollateralRatioPercentage(
1428
+ context.emulator.slot,
1429
+ parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
1430
+ res.utxo,
1431
+ res.datum,
1432
+ parseInterestOracleDatum(
1433
+ getInlineDatumOrThrow(orefs.interestOracleUtxo),
1434
+ ),
1435
+ context.lucid.config().network!,
1436
+ ),
1437
+ {
1438
+ min: 197,
1439
+ max: 197.001,
1440
+ },
1441
+ );
1442
+ });
1443
+
1444
+ test<MyContext>('Pyth oracle - Open 2x leveraged CDP; 1 LRP; price ~1.1; f_r=.01; f_m=.005', async (context: MyContext) => {
1445
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1446
+
1447
+ const [sysParams, [iusdAssetInfo]] = await init(
1448
+ context.lucid,
1449
+ [],
1450
+ context.emulator.slot,
1451
+ (pythStateNft: AssetClass) => [
1452
+ iusdInitialAssetCfgWithPyth(pythStateNft, (pythStatePolicyId) => [
1453
+ mkBaseCollateralAsset(
1454
+ adaAssetClass,
1455
+ 0n,
1456
+ DEFAULT_PRICE,
1457
+ 0n,
1458
+ pythStatePolicyId,
1459
+ {
1460
+ tag: 'value',
1461
+ val: { priceFeedId: 1 },
1462
+ },
1463
+ ),
1464
+ ]),
1465
+ ],
1466
+ );
1467
+
1468
+ await openBuyRobs(
1469
+ context,
1470
+ sysParams,
1471
+ iusdAssetInfo.iassetTokenNameAscii,
1472
+ adaAssetClass,
1473
+ [100_000_000n],
1474
+ { numerator: 15n, denominator: 10n },
1475
+ );
1476
+
1477
+ const allRobs = await findAllRobs(
1478
+ context.lucid,
1479
+ sysParams,
1480
+ iusdAssetInfo.iassetTokenNameAscii,
1481
+ );
1482
+
1483
+ const orefs = await findAllNecessaryOrefs(
1484
+ context.lucid,
1485
+ sysParams,
1486
+ iusdAssetInfo.iassetTokenNameAscii,
1487
+ adaAssetClass,
1488
+ );
1489
+
1490
+ const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1491
+ context.lucid,
1492
+ orefs.collateralAsset,
1493
+ );
1494
+
1495
+ const currentTime = BigInt(
1496
+ slotToUnixTime(context.lucid.config().network!, context.emulator.slot),
1497
+ );
1498
+
1499
+ const iUsdFeed: ParsedFeedPayload = {
1500
+ priceFeedId: 1,
1501
+ price: '1104093',
1502
+ exponent: -6,
1503
+ };
1504
+ const message = toHex(
1505
+ await createPythMessage([iUsdFeed], currentTime * 1_000n),
1506
+ );
1507
+
1508
+ const pythStateOref = await context.lucid.utxoByUnit(
1509
+ assetClassToUnit(
1510
+ fromSystemParamsAsset(sysParams.pythConfig.pythStateAssetClass),
1511
+ ),
1512
+ );
1513
+
1514
+ const [price, _] = await retrieveAdjustedPrice(
1515
+ orefs.iasset.datum.assetName,
1516
+ orefs.collateralAsset.datum.collateralAsset,
1517
+ orefs.collateralAsset.datum.priceInfo,
1518
+ orefs.collateralAsset.datum.extraDecimals,
1519
+ priceOracleUtxo,
1520
+ message,
1521
+ sysParams.pythConfig,
1522
+ context.lucid,
1523
+ );
1524
+
1525
+ const baseCollateral = 20_000_000n;
1526
+
1527
+ await benchmarkAndAwaitTx(
1528
+ 'Leverage - CDP open with 1 LRP using Pyth oracle',
1529
+ await leverageCdpWithRob(
1530
+ 2,
1531
+ baseCollateral,
1532
+ priceOracleUtxo,
1533
+ orefs.iasset.utxo,
1534
+ orefs.collateralAsset.utxo,
1535
+ orefs.cdpCreatorUtxo,
1536
+ orefs.interestOracleUtxo,
1537
+ orefs.treasuryUtxo,
1538
+ sysParams,
1539
+ context.lucid,
1540
+ allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
1541
+ context.emulator.slot,
1542
+ message,
1543
+ pythStateOref,
1544
+ ),
1545
+ context.lucid,
1546
+ context.emulator,
1547
+ );
1548
+
1549
+ const [pkh, skh] = await addrDetails(context.lucid);
1550
+
1551
+ const res = await findCdp(
1552
+ context.lucid,
1553
+ sysParams.validatorHashes.cdpHash,
1554
+ fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
1555
+ pkh.hash,
1556
+ skh,
1557
+ );
1558
+
1559
+ // Assert leverage
1560
+ assertValueInRange(
1561
+ Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
1562
+ {
1563
+ min: 1.999,
1564
+ max: 2.0,
1565
+ },
1566
+ );
1567
+
1568
+ // Assert collateral ratio
1569
+ assertValueInRange(
1570
+ cdpCollateralRatioPercentage(
1571
+ context.emulator.slot,
1572
+ price,
1573
+ res.utxo,
1574
+ res.datum,
1575
+ parseInterestOracleDatum(
1576
+ getInlineDatumOrThrow(orefs.interestOracleUtxo),
1577
+ ),
1578
+ context.lucid.config().network!,
1579
+ ),
1580
+ {
1581
+ min: 197,
1582
+ max: 197.001,
1583
+ },
1584
+ );
1585
+ });
1586
+
1587
+ test<MyContext>('Open 2x leveraged CDP; 4 LRPs; price ~0.9; f_r=.01; f_m=.005', async (context: MyContext) => {
1588
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1589
+
1590
+ const [sysParams, [iusdAssetInfo]] = await init(
1591
+ context.lucid,
1592
+ [
1593
+ {
1594
+ ...iusdInitialAssetCfg(),
1595
+ collateralAssets: [
1596
+ {
1597
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
1598
+ numerator: 904_093n,
1599
+ denominator: 1_000_000n,
1600
+ }),
1601
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
1602
+ },
1603
+ ],
1604
+ debtMintingFeeRatio: { numerator: 1n, denominator: 200n },
1605
+ redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
1606
+ },
1607
+ ],
1608
+ context.emulator.slot,
1609
+ );
1610
+
1611
+ await openBuyRobs(
1612
+ context,
1613
+ sysParams,
1614
+ iusdAssetInfo.iassetTokenNameAscii,
1615
+ adaAssetClass,
1616
+ [26_250_000n, 26_250_000n, 26_250_000n, 26_250_000n],
1617
+ { numerator: 15n, denominator: 10n },
1618
+ );
1619
+
1620
+ const allRobs = await findAllRobs(
1621
+ context.lucid,
1622
+ sysParams,
1623
+ iusdAssetInfo.iassetTokenNameAscii,
1624
+ );
1625
+
1626
+ const orefs = await findAllNecessaryOrefs(
1627
+ context.lucid,
1628
+ sysParams,
1629
+ iusdAssetInfo.iassetTokenNameAscii,
1630
+ adaAssetClass,
1631
+ );
1632
+
1633
+ const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1634
+ context.lucid,
1635
+ orefs.collateralAsset,
1636
+ );
1637
+
1638
+ if (priceOracleUtxo == null) {
1639
+ throw new Error('Expected oracle UTXO');
1640
+ }
1641
+
1642
+ const baseCollateral = 100_000_000n;
1643
+ await runAndAwaitTx(
1644
+ context.lucid,
1645
+ leverageCdpWithRob(
1646
+ 2,
1647
+ baseCollateral,
1648
+ priceOracleUtxo,
1649
+ orefs.iasset.utxo,
1650
+ orefs.collateralAsset.utxo,
1651
+ orefs.cdpCreatorUtxo,
1652
+ orefs.interestOracleUtxo,
1653
+ orefs.treasuryUtxo,
1654
+ sysParams,
1655
+ context.lucid,
1656
+ allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
1657
+ context.emulator.slot,
1658
+ ),
1659
+ );
1660
+
1661
+ const [pkh, skh] = await addrDetails(context.lucid);
1662
+
1663
+ const res = await findCdp(
1664
+ context.lucid,
1665
+ sysParams.validatorHashes.cdpHash,
1666
+ fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
1667
+ pkh.hash,
1668
+ skh,
1669
+ );
1670
+
1671
+ // Assert leverage
1672
+ assertValueInRange(
1673
+ Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
1674
+ {
1675
+ min: 1.999,
1676
+ max: 2.0,
1677
+ },
1678
+ );
1679
+
1680
+ // Assert collateral ratio
1681
+ assertValueInRange(
1682
+ cdpCollateralRatioPercentage(
1683
+ context.emulator.slot,
1684
+ parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
1685
+ res.utxo,
1686
+ res.datum,
1687
+ parseInterestOracleDatum(
1688
+ getInlineDatumOrThrow(orefs.interestOracleUtxo),
1689
+ ),
1690
+ context.lucid.config().network!,
1691
+ ),
1692
+ {
1693
+ min: 197,
1694
+ max: 197.001,
1695
+ },
1696
+ );
1697
+
1698
+ {
1699
+ const lrps = await findAllRobs(
1700
+ context.lucid,
1701
+ sysParams,
1702
+ iusdAssetInfo.iassetTokenNameAscii,
1703
+ );
1704
+ expect(
1705
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1706
+ ).toBeTruthy();
1707
+ }
1708
+ });
1709
+
1710
+ test<MyContext>('Open 2.3x leveraged CDP; 4 LRPs; price ~1.03; f_r=.01; f_m=.013', async (context: MyContext) => {
1711
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1712
+
1713
+ const [sysParams, [iusdAssetInfo]] = await init(
1714
+ context.lucid,
1715
+ [
1716
+ {
1717
+ ...iusdInitialAssetCfg(),
1718
+ collateralAssets: [
1719
+ {
1720
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
1721
+ numerator: 1_037_093n,
1722
+ denominator: 1_000_000n,
1723
+ }),
1724
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
1725
+ },
1726
+ ],
1727
+ debtMintingFeeRatio: { numerator: 13n, denominator: 1_000n },
1728
+ redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
1729
+ },
1730
+ ],
1731
+ context.emulator.slot,
1732
+ );
1733
+
1734
+ await openBuyRobs(
1735
+ context,
1736
+ sysParams,
1737
+ iusdAssetInfo.iassetTokenNameAscii,
1738
+ adaAssetClass,
1739
+ [35_139_729n, 35_000_397n, 35_001_079n, 35_107_049n],
1740
+ { numerator: 15n, denominator: 10n },
1741
+ );
1742
+
1743
+ const allLrps = await findAllRobs(
1744
+ context.lucid,
1745
+ sysParams,
1746
+ iusdAssetInfo.iassetTokenNameAscii,
1747
+ );
1748
+
1749
+ const orefs = await findAllNecessaryOrefs(
1750
+ context.lucid,
1751
+ sysParams,
1752
+ iusdAssetInfo.iassetTokenNameAscii,
1753
+ adaAssetClass,
1754
+ );
1755
+
1756
+ const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1757
+ context.lucid,
1758
+ orefs.collateralAsset,
1759
+ );
1760
+
1761
+ if (priceOracleUtxo == null) {
1762
+ throw new Error('Expected oracle UTXO');
1763
+ }
1764
+
1765
+ const baseCollateral = 100_000_000n;
1766
+ await runAndAwaitTx(
1767
+ context.lucid,
1768
+ leverageCdpWithRob(
1769
+ 2.3,
1770
+ baseCollateral,
1771
+ priceOracleUtxo,
1772
+ orefs.iasset.utxo,
1773
+ orefs.collateralAsset.utxo,
1774
+ orefs.cdpCreatorUtxo,
1775
+ orefs.interestOracleUtxo,
1776
+ orefs.treasuryUtxo,
1777
+ sysParams,
1778
+ context.lucid,
1779
+ allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
1780
+ context.emulator.slot,
880
1781
  ),
881
- ).toEqual<bigint>(18_002_000n);
882
- });
883
- });
1782
+ );
884
1783
 
885
- describe('LRP leverage', () => {
886
- beforeEach<MyContext>(async (context: MyContext) => {
887
- context.users = {
888
- admin: generateEmulatorAccount({
889
- lovelace: BigInt(100_000_000_000_000),
890
- }),
891
- user: generateEmulatorAccount(addAssets(mkLovelacesOf(150_000_000n))),
892
- };
1784
+ const [pkh, skh] = await addrDetails(context.lucid);
893
1785
 
894
- context.emulator = new Emulator([context.users.admin, context.users.user]);
895
- context.lucid = await Lucid(context.emulator, 'Custom');
1786
+ const res = await findCdp(
1787
+ context.lucid,
1788
+ sysParams.validatorHashes.cdpHash,
1789
+ fromSystemParamsAsset(sysParams.cdpParams.cdpAuthToken),
1790
+ pkh.hash,
1791
+ skh,
1792
+ );
1793
+
1794
+ // Assert leverage
1795
+ assertValueInRange(
1796
+ Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
1797
+ {
1798
+ min: 2.29999,
1799
+ max: 2.3,
1800
+ },
1801
+ );
1802
+
1803
+ // Assert collateral ratio
1804
+ assertValueInRange(
1805
+ cdpCollateralRatioPercentage(
1806
+ context.emulator.slot,
1807
+ parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
1808
+ res.utxo,
1809
+ res.datum,
1810
+ parseInterestOracleDatum(
1811
+ getInlineDatumOrThrow(orefs.interestOracleUtxo),
1812
+ ),
1813
+ context.lucid.config().network!,
1814
+ ),
1815
+ {
1816
+ min: 172,
1817
+ max: 172.1,
1818
+ },
1819
+ );
1820
+
1821
+ {
1822
+ const lrps = await findAllRobs(
1823
+ context.lucid,
1824
+ sysParams,
1825
+ iusdAssetInfo.iassetTokenNameAscii,
1826
+ );
1827
+ expect(
1828
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1829
+ ).toBeTruthy();
1830
+ }
896
1831
  });
897
1832
 
898
- test<MyContext>('Open 2x leveraged CDP; 1 LRP; price ~1.1; f_r=.01; f_m=.005', async (context: MyContext) => {
1833
+ test<MyContext>('Open 1.2x leveraged CDP 3 LRPs price ~1.46; f_r=.02; f_m=.007', async (context: MyContext) => {
899
1834
  context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
900
1835
 
901
1836
  const [sysParams, [iusdAssetInfo]] = await init(
@@ -905,12 +1840,15 @@ describe('LRP leverage', () => {
905
1840
  ...iusdInitialAssetCfg(),
906
1841
  collateralAssets: [
907
1842
  {
908
- ...mkBaseCollateralAsset(adaAssetClass, 0n, 1_104_093n),
909
- maintenanceRatioPercentage: 150_000_000n,
1843
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
1844
+ numerator: 1_461_093n,
1845
+ denominator: 1_000_000n,
1846
+ }),
1847
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
910
1848
  },
911
1849
  ],
912
- debtMintingFeePercentage: 500_000n,
913
- redemptionReimbursementPercentage: 1_000_000n,
1850
+ debtMintingFeeRatio: { numerator: 7n, denominator: 1_000n },
1851
+ redemptionReimbursementRatio: { numerator: 2n, denominator: 100n },
914
1852
  },
915
1853
  ],
916
1854
  context.emulator.slot,
@@ -921,13 +1859,11 @@ describe('LRP leverage', () => {
921
1859
  sysParams,
922
1860
  iusdAssetInfo.iassetTokenNameAscii,
923
1861
  adaAssetClass,
924
- [100_000_000n],
925
- {
926
- getOnChainInt: 1_500_000n,
927
- },
1862
+ [75_000_000n, 75_000_000n, 75_000_000n],
1863
+ { numerator: 15n, denominator: 10n },
928
1864
  );
929
1865
 
930
- const allRobs = await findAllRobs(
1866
+ const allLrps = await findAllRobs(
931
1867
  context.lucid,
932
1868
  sysParams,
933
1869
  iusdAssetInfo.iassetTokenNameAscii,
@@ -945,26 +1881,27 @@ describe('LRP leverage', () => {
945
1881
  orefs.collateralAsset,
946
1882
  );
947
1883
 
948
- const baseCollateral = 20_000_000n;
1884
+ if (priceOracleUtxo == null) {
1885
+ throw new Error('Expected oracle UTXO');
1886
+ }
949
1887
 
950
- await benchmarkAndAwaitTx(
951
- 'Leverage - CDP open with 1 LRP',
952
- await leverageCdpWithLrp(
953
- 2,
1888
+ const baseCollateral = 1_000_000_000n;
1889
+ await runAndAwaitTx(
1890
+ context.lucid,
1891
+ leverageCdpWithRob(
1892
+ 1.2,
954
1893
  baseCollateral,
955
1894
  priceOracleUtxo,
956
1895
  orefs.iasset.utxo,
957
1896
  orefs.collateralAsset.utxo,
958
1897
  orefs.cdpCreatorUtxo,
959
1898
  orefs.interestOracleUtxo,
960
- orefs.collectorUtxo,
1899
+ orefs.treasuryUtxo,
961
1900
  sysParams,
962
1901
  context.lucid,
963
- allRobs.map((lrps) => [lrps.utxo, lrps.datum]),
1902
+ allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
964
1903
  context.emulator.slot,
965
1904
  ),
966
- context.lucid,
967
- context.emulator,
968
1905
  );
969
1906
 
970
1907
  const [pkh, skh] = await addrDetails(context.lucid);
@@ -981,8 +1918,8 @@ describe('LRP leverage', () => {
981
1918
  assertValueInRange(
982
1919
  Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
983
1920
  {
984
- min: 2,
985
- max: 2.001,
1921
+ min: 1.19999,
1922
+ max: 1.2,
986
1923
  },
987
1924
  );
988
1925
 
@@ -999,72 +1936,90 @@ describe('LRP leverage', () => {
999
1936
  context.lucid.config().network!,
1000
1937
  ),
1001
1938
  {
1002
- min: 197,
1003
- max: 197.001,
1939
+ min: 583,
1940
+ max: 583.1,
1004
1941
  },
1005
1942
  );
1943
+
1944
+ {
1945
+ const lrps = await findAllRobs(
1946
+ context.lucid,
1947
+ sysParams,
1948
+ iusdAssetInfo.iassetTokenNameAscii,
1949
+ );
1950
+ expect(
1951
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1952
+ ).toBeTruthy();
1953
+ }
1006
1954
  });
1007
1955
 
1008
- test<MyContext>('Open 2x leveraged CDP; 4 LRPs; price ~0.9; f_r=.01; f_m=.005', async (context: MyContext) => {
1956
+ test<MyContext>('Open 2.1x leveraged CDP 3 LRPs price ~0.0589; f_r=.02; f_m=.007', async (context: MyContext) => {
1009
1957
  context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1010
1958
 
1011
- const [sysParams, __] = await init(
1959
+ const [sysParams, [iusdAssetInfo]] = await init(
1012
1960
  context.lucid,
1013
1961
  [
1014
1962
  {
1015
- ...iusdInitialAssetCfg,
1016
- priceOracle: {
1017
- ...iusdInitialAssetCfg.priceOracle,
1018
- startPrice: 904_093n,
1019
- },
1020
- interestOracle: {
1021
- ...iusdInitialAssetCfg.interestOracle,
1022
- initialInterestRate: 0n,
1023
- },
1024
- maintenanceRatioPercentage: 150_000_000n,
1025
- debtMintingFeePercentage: 500_000n,
1026
- redemptionReimbursementPercentage: 1_000_000n,
1963
+ ...iusdInitialAssetCfg(),
1964
+ collateralAssets: [
1965
+ {
1966
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
1967
+ numerator: 58_900n,
1968
+ denominator: 1_000_000n,
1969
+ }),
1970
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
1971
+ },
1972
+ ],
1973
+ debtMintingFeeRatio: { numerator: 7n, denominator: 1_000n },
1974
+ redemptionReimbursementRatio: { numerator: 2n, denominator: 100n },
1027
1975
  },
1028
1976
  ],
1029
1977
  context.emulator.slot,
1030
1978
  );
1031
1979
 
1032
- const iasset = fromText(iusdInitialAssetCfg.name);
1033
-
1034
1980
  await openBuyRobs(
1035
1981
  context,
1036
1982
  sysParams,
1037
- iasset,
1038
- [26_250_000n, 26_250_000n, 26_250_000n, 26_250_000n],
1039
- {
1040
- getOnChainInt: 1_500_000n,
1041
- },
1983
+ iusdAssetInfo.iassetTokenNameAscii,
1984
+ adaAssetClass,
1985
+ [400_000_000n, 400_000_000n, 400_000_000n],
1986
+ { numerator: 15n, denominator: 10n },
1042
1987
  );
1043
1988
 
1044
- const allLrps = await findAllLrps(context.lucid, sysParams, iasset);
1989
+ const allLrps = await findAllRobs(
1990
+ context.lucid,
1991
+ sysParams,
1992
+ iusdAssetInfo.iassetTokenNameAscii,
1993
+ );
1045
1994
 
1046
1995
  const orefs = await findAllNecessaryOrefs(
1047
1996
  context.lucid,
1048
1997
  sysParams,
1049
- toText(iasset),
1998
+ iusdAssetInfo.iassetTokenNameAscii,
1999
+ adaAssetClass,
1050
2000
  );
1051
2001
 
1052
2002
  const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1053
2003
  context.lucid,
1054
- orefs.iasset,
2004
+ orefs.collateralAsset,
1055
2005
  );
1056
2006
 
1057
- const baseCollateral = 100_000_000n;
2007
+ if (priceOracleUtxo == null) {
2008
+ throw new Error('Expected oracle UTXO');
2009
+ }
2010
+
2011
+ const baseCollateral = 1_000_000_000n;
1058
2012
  await runAndAwaitTx(
1059
2013
  context.lucid,
1060
- leverageCdpWithLrp(
1061
- 2,
2014
+ leverageCdpWithRob(
2015
+ 2.1,
1062
2016
  baseCollateral,
1063
2017
  priceOracleUtxo,
1064
2018
  orefs.iasset.utxo,
2019
+ orefs.collateralAsset.utxo,
1065
2020
  orefs.cdpCreatorUtxo,
1066
2021
  orefs.interestOracleUtxo,
1067
- orefs.collectorUtxo,
2022
+ orefs.treasuryUtxo,
1068
2023
  sysParams,
1069
2024
  context.lucid,
1070
2025
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
@@ -1086,8 +2041,8 @@ describe('LRP leverage', () => {
1086
2041
  assertValueInRange(
1087
2042
  Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
1088
2043
  {
1089
- min: 2,
1090
- max: 2.001,
2044
+ min: 2.099999,
2045
+ max: 2.1,
1091
2046
  },
1092
2047
  );
1093
2048
 
@@ -1104,79 +2059,90 @@ describe('LRP leverage', () => {
1104
2059
  context.lucid.config().network!,
1105
2060
  ),
1106
2061
  {
1107
- min: 197,
1108
- max: 197.001,
2062
+ min: 185,
2063
+ max: 185.1,
1109
2064
  },
1110
2065
  );
1111
2066
 
1112
2067
  {
1113
- const lrps = await findAllLrps(context.lucid, sysParams, iasset);
2068
+ const lrps = await findAllRobs(
2069
+ context.lucid,
2070
+ sysParams,
2071
+ iusdAssetInfo.iassetTokenNameAscii,
2072
+ );
1114
2073
  expect(
1115
- lrps.every((lrp) => hadRobRedemption(lrp, sysParams.lrpParams)),
2074
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1116
2075
  ).toBeTruthy();
1117
2076
  }
1118
2077
  });
1119
2078
 
1120
- test<MyContext>('Open 2.3x leveraged CDP; 4 LRPs; price ~1.03; f_r=.01; f_m=.013', async (context: MyContext) => {
2079
+ test<MyContext>('Open 2.5x leveraged CDP 4 LRPs price ~103.973621; f_r=.01; f_m=.03', async (context: MyContext) => {
1121
2080
  context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1122
2081
 
1123
- const [sysParams, __] = await init(
2082
+ const [sysParams, [iusdAssetInfo]] = await init(
1124
2083
  context.lucid,
1125
2084
  [
1126
2085
  {
1127
- ...iusdInitialAssetCfg,
1128
- priceOracle: {
1129
- ...iusdInitialAssetCfg.priceOracle,
1130
- startPrice: 1_037_093n,
1131
- },
1132
- interestOracle: {
1133
- ...iusdInitialAssetCfg.interestOracle,
1134
- initialInterestRate: 0n,
1135
- },
1136
- maintenanceRatioPercentage: 150_000_000n,
1137
- debtMintingFeePercentage: 1_300_000n,
1138
- redemptionReimbursementPercentage: 1_000_000n,
2086
+ ...iusdInitialAssetCfg(),
2087
+ collateralAssets: [
2088
+ {
2089
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
2090
+ numerator: 103_973_621n,
2091
+ denominator: 1_000_000n,
2092
+ }),
2093
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
2094
+ },
2095
+ ],
2096
+ debtMintingFeeRatio: { numerator: 3n, denominator: 100n },
2097
+ redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
1139
2098
  },
1140
2099
  ],
1141
2100
  context.emulator.slot,
1142
2101
  );
1143
2102
 
1144
- const iasset = fromText(iusdInitialAssetCfg.name);
1145
-
1146
2103
  await openBuyRobs(
1147
2104
  context,
1148
2105
  sysParams,
1149
- iasset,
1150
- [35_139_729n, 35_000_397n, 35_001_079n, 35_107_049n],
1151
- {
1152
- getOnChainInt: 1_500_000n,
1153
- },
2106
+ iusdAssetInfo.iassetTokenNameAscii,
2107
+ adaAssetClass,
2108
+ [400_000_000n, 400_000_000n, 400_000_000n, 400_000_000n],
2109
+ { numerator: 150n, denominator: 1n },
1154
2110
  );
1155
2111
 
1156
- const allLrps = await findAllLrps(context.lucid, sysParams, iasset);
2112
+ const allLrps = await findAllRobs(
2113
+ context.lucid,
2114
+ sysParams,
2115
+ iusdAssetInfo.iassetTokenNameAscii,
2116
+ );
1157
2117
 
1158
2118
  const orefs = await findAllNecessaryOrefs(
1159
2119
  context.lucid,
1160
2120
  sysParams,
1161
- toText(iasset),
2121
+ iusdAssetInfo.iassetTokenNameAscii,
2122
+ adaAssetClass,
1162
2123
  );
1163
2124
 
1164
2125
  const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1165
2126
  context.lucid,
1166
- orefs.iasset,
2127
+ orefs.collateralAsset,
1167
2128
  );
1168
2129
 
1169
- const baseCollateral = 100_000_000n;
2130
+ if (priceOracleUtxo == null) {
2131
+ throw new Error('Expected oracle UTXO');
2132
+ }
2133
+
2134
+ const baseCollateral = 1_000_000_000n;
1170
2135
  await runAndAwaitTx(
1171
2136
  context.lucid,
1172
- leverageCdpWithLrp(
1173
- 2.3,
2137
+ leverageCdpWithRob(
2138
+ 2.5,
1174
2139
  baseCollateral,
1175
2140
  priceOracleUtxo,
1176
2141
  orefs.iasset.utxo,
2142
+ orefs.collateralAsset.utxo,
1177
2143
  orefs.cdpCreatorUtxo,
1178
2144
  orefs.interestOracleUtxo,
1179
- orefs.collectorUtxo,
2145
+ orefs.treasuryUtxo,
1180
2146
  sysParams,
1181
2147
  context.lucid,
1182
2148
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
@@ -1198,8 +2164,8 @@ describe('LRP leverage', () => {
1198
2164
  assertValueInRange(
1199
2165
  Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
1200
2166
  {
1201
- min: 2.3,
1202
- max: 2.301,
2167
+ min: 2.4999,
2168
+ max: 2.5,
1203
2169
  },
1204
2170
  );
1205
2171
 
@@ -1216,79 +2182,105 @@ describe('LRP leverage', () => {
1216
2182
  context.lucid.config().network!,
1217
2183
  ),
1218
2184
  {
1219
- min: 172.8,
1220
- max: 172.9,
2185
+ min: 160,
2186
+ max: 160.1,
1221
2187
  },
1222
2188
  );
1223
2189
 
1224
2190
  {
1225
- const lrps = await findAllLrps(context.lucid, sysParams, iasset);
2191
+ const lrps = await findAllRobs(
2192
+ context.lucid,
2193
+ sysParams,
2194
+ iusdAssetInfo.iassetTokenNameAscii,
2195
+ );
1226
2196
  expect(
1227
- lrps.every((lrp) => hadRobRedemption(lrp, sysParams.lrpParams)),
2197
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1228
2198
  ).toBeTruthy();
1229
2199
  }
1230
2200
  });
1231
2201
 
1232
- test<MyContext>('Open 1.2x leveraged CDP 3 LRPs price ~1.46; f_r=.02; f_m=.007', async (context: MyContext) => {
2202
+ test<MyContext>('(non ADA collateral) Open 1.75x leveraged CDP 3 LRPs price ~2.397; f_r=.017; f_m=.0391', async (context: MyContext) => {
1233
2203
  context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1234
2204
 
1235
- const [sysParams, __] = await init(
2205
+ const [sysParams, [iusdAssetInfo]] = await init(
1236
2206
  context.lucid,
1237
2207
  [
1238
2208
  {
1239
- ...iusdInitialAssetCfg,
1240
- priceOracle: {
1241
- ...iusdInitialAssetCfg.priceOracle,
1242
- startPrice: 1_461_093n,
1243
- },
1244
- interestOracle: {
1245
- ...iusdInitialAssetCfg.interestOracle,
1246
- initialInterestRate: 0n,
1247
- },
1248
- maintenanceRatioPercentage: 150_000_000n,
1249
- debtMintingFeePercentage: 700_000n,
1250
- redemptionReimbursementPercentage: 2_000_000n,
2209
+ ...iusdInitialAssetCfg(),
2210
+ collateralAssets: [
2211
+ {
2212
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
2213
+ numerator: 1_000_000n,
2214
+ denominator: 1_000_000n,
2215
+ }),
2216
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
2217
+ },
2218
+ {
2219
+ ...mkBaseCollateralAsset(collateralAssetA, 0n, {
2220
+ numerator: 2_397_000n,
2221
+ denominator: 1_000_000n,
2222
+ }),
2223
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
2224
+ },
2225
+ ],
2226
+ debtMintingFeeRatio: { numerator: 391n, denominator: 10_000n },
2227
+ redemptionReimbursementRatio: { numerator: 17n, denominator: 1000n },
1251
2228
  },
1252
2229
  ],
1253
2230
  context.emulator.slot,
1254
2231
  );
1255
2232
 
1256
- const iasset = fromText(iusdInitialAssetCfg.name);
1257
-
1258
2233
  await openBuyRobs(
1259
2234
  context,
1260
2235
  sysParams,
1261
- iasset,
1262
- [75_000_000n, 75_000_000n, 75_000_000n],
1263
- {
1264
- getOnChainInt: 1_500_000n,
1265
- },
2236
+ iusdAssetInfo.iassetTokenNameAscii,
2237
+ adaAssetClass,
2238
+ [400_000_000n, 400_000_000n],
2239
+ { numerator: 5n, denominator: 1n },
2240
+ );
2241
+ await openBuyRobs(
2242
+ context,
2243
+ sysParams,
2244
+ iusdAssetInfo.iassetTokenNameAscii,
2245
+ collateralAssetA,
2246
+ [450_000_000n, 450_000_000n],
2247
+ { numerator: 5n, denominator: 1n },
1266
2248
  );
1267
2249
 
1268
- const allLrps = await findAllLrps(context.lucid, sysParams, iasset);
2250
+ const allLrps = await findAllRobs(
2251
+ context.lucid,
2252
+ sysParams,
2253
+ iusdAssetInfo.iassetTokenNameAscii,
2254
+ );
1269
2255
 
1270
2256
  const orefs = await findAllNecessaryOrefs(
1271
2257
  context.lucid,
1272
2258
  sysParams,
1273
- toText(iasset),
2259
+ iusdAssetInfo.iassetTokenNameAscii,
2260
+ collateralAssetA,
1274
2261
  );
1275
2262
 
1276
2263
  const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1277
2264
  context.lucid,
1278
- orefs.iasset,
2265
+ orefs.collateralAsset,
1279
2266
  );
1280
2267
 
2268
+ if (priceOracleUtxo == null) {
2269
+ throw new Error('Expected oracle UTXO');
2270
+ }
2271
+
1281
2272
  const baseCollateral = 1_000_000_000n;
1282
2273
  await runAndAwaitTx(
1283
2274
  context.lucid,
1284
- leverageCdpWithLrp(
1285
- 1.2,
2275
+ leverageCdpWithRob(
2276
+ 1.75,
1286
2277
  baseCollateral,
1287
2278
  priceOracleUtxo,
1288
2279
  orefs.iasset.utxo,
2280
+ orefs.collateralAsset.utxo,
1289
2281
  orefs.cdpCreatorUtxo,
1290
2282
  orefs.interestOracleUtxo,
1291
- orefs.collectorUtxo,
2283
+ orefs.treasuryUtxo,
1292
2284
  sysParams,
1293
2285
  context.lucid,
1294
2286
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
@@ -1308,10 +2300,11 @@ describe('LRP leverage', () => {
1308
2300
 
1309
2301
  // Assert leverage
1310
2302
  assertValueInRange(
1311
- Number(lovelacesAmt(res.utxo.assets)) / Number(baseCollateral),
2303
+ Number(assetClassValueOf(res.utxo.assets, collateralAssetA)) /
2304
+ Number(baseCollateral),
1312
2305
  {
1313
- min: 1.2,
1314
- max: 1.2001,
2306
+ min: 1.749999,
2307
+ max: 1.75,
1315
2308
  },
1316
2309
  );
1317
2310
 
@@ -1328,15 +2321,29 @@ describe('LRP leverage', () => {
1328
2321
  context.lucid.config().network!,
1329
2322
  ),
1330
2323
  {
1331
- min: 583.79,
1332
- max: 583.8,
2324
+ min: 220,
2325
+ max: 220.1,
1333
2326
  },
1334
2327
  );
1335
2328
 
1336
2329
  {
1337
- const lrps = await findAllLrps(context.lucid, sysParams, iasset);
2330
+ const lrps = await findAllRobs(
2331
+ context.lucid,
2332
+ sysParams,
2333
+ iusdAssetInfo.iassetTokenNameAscii,
2334
+ );
2335
+ // All with collateral asset A had redemptions
1338
2336
  expect(
1339
- lrps.every((lrp) => hadRobRedemption(lrp, sysParams.lrpParams)),
2337
+ lrps
2338
+ .filter((lrp) =>
2339
+ match(lrp.datum.orderType)
2340
+ .returnType<boolean>()
2341
+ .with({ BuyIAssetOrder: P.select() }, (content) =>
2342
+ isSameAssetClass(content.collateralAsset, collateralAssetA),
2343
+ )
2344
+ .otherwise(() => false),
2345
+ )
2346
+ .every((lrp) => hadRobRedemption(lrp, sysParams)),
1340
2347
  ).toBeTruthy();
1341
2348
  }
1342
2349
  });
@@ -1344,74 +2351,81 @@ describe('LRP leverage', () => {
1344
2351
  test<MyContext>('Open max leverage leveraged CDP; 4 CDPs; price 1; f_r=.01; f_m=.005', async (context: MyContext) => {
1345
2352
  context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1346
2353
 
1347
- const [sysParams, __] = await init(
2354
+ const [sysParams, [iusdAssetInfo]] = await init(
1348
2355
  context.lucid,
1349
2356
  [
1350
2357
  {
1351
- ...iusdInitialAssetCfg,
1352
- priceOracle: {
1353
- ...iusdInitialAssetCfg.priceOracle,
1354
- startPrice: 1_000_000n,
1355
- },
1356
- interestOracle: {
1357
- ...iusdInitialAssetCfg.interestOracle,
1358
- initialInterestRate: 0n,
1359
- },
1360
- maintenanceRatioPercentage: 150_000_000n,
1361
- debtMintingFeePercentage: 500_000n,
1362
- redemptionReimbursementPercentage: 1_000_000n,
2358
+ ...iusdInitialAssetCfg(),
2359
+ collateralAssets: [
2360
+ {
2361
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
2362
+ numerator: 1n,
2363
+ denominator: 1n,
2364
+ }),
2365
+ maintenanceRatio: { numerator: 15n, denominator: 10n },
2366
+ },
2367
+ ],
2368
+ debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
2369
+ redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
1363
2370
  },
1364
2371
  ],
1365
2372
  context.emulator.slot,
1366
2373
  );
1367
2374
 
1368
- const iasset = fromText(iusdInitialAssetCfg.name);
1369
-
1370
2375
  await openBuyRobs(
1371
2376
  context,
1372
2377
  sysParams,
1373
- iasset,
2378
+ iusdAssetInfo.iassetTokenNameAscii,
2379
+ adaAssetClass,
1374
2380
  [500_000_000n, 500_000_000n, 500_000_000n, 500_000_000n],
1375
- {
1376
- getOnChainInt: 1_500_000n,
1377
- },
2381
+ { numerator: 15n, denominator: 10n },
1378
2382
  );
1379
2383
 
1380
- const allLrps = await findAllLrps(context.lucid, sysParams, iasset);
2384
+ const allLrps = await findAllRobs(
2385
+ context.lucid,
2386
+ sysParams,
2387
+ iusdAssetInfo.iassetTokenNameAscii,
2388
+ );
1381
2389
 
1382
2390
  const orefs = await findAllNecessaryOrefs(
1383
2391
  context.lucid,
1384
2392
  sysParams,
1385
- toText(iasset),
2393
+ iusdAssetInfo.iassetTokenNameAscii,
2394
+ adaAssetClass,
1386
2395
  );
1387
2396
 
1388
2397
  const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1389
2398
  context.lucid,
1390
- orefs.iasset,
2399
+ orefs.collateralAsset,
1391
2400
  );
1392
2401
 
2402
+ if (priceOracleUtxo == null) {
2403
+ throw new Error('Expected oracle UTXO');
2404
+ }
2405
+
1393
2406
  const baseCollateral = 1_000_000_000n;
1394
2407
  const maxLeverage = calculateLeverageFromCollateralRatio(
1395
- fromHex(iasset),
1396
- orefs.iasset.datum.maintenanceRatio,
2408
+ fromHex(fromText(iusdAssetInfo.iassetTokenNameAscii)),
2409
+ adaAssetClass,
2410
+ orefs.collateralAsset.datum.maintenanceRatio,
1397
2411
  baseCollateral,
1398
2412
  parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
1399
- orefs.iasset.datum.debtMintingFeePercentage,
1400
- orefs.iasset.datum.redemptionReimbursementPercentage,
1401
- sysParams.lrpParams,
2413
+ orefs.iasset.datum.debtMintingFeeRatio,
2414
+ orefs.iasset.datum.redemptionProcessingFeeRatio,
1402
2415
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
1403
2416
  )!;
1404
2417
 
1405
2418
  await benchmarkAndAwaitTx(
1406
2419
  'Leverage - CDP open with 4 LRP',
1407
- await leverageCdpWithLrp(
2420
+ await leverageCdpWithRob(
1408
2421
  maxLeverage,
1409
2422
  baseCollateral,
1410
2423
  priceOracleUtxo,
1411
2424
  orefs.iasset.utxo,
2425
+ orefs.collateralAsset.utxo,
1412
2426
  orefs.cdpCreatorUtxo,
1413
2427
  orefs.interestOracleUtxo,
1414
- orefs.collectorUtxo,
2428
+ orefs.treasuryUtxo,
1415
2429
  sysParams,
1416
2430
  context.lucid,
1417
2431
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
@@ -1453,15 +2467,22 @@ describe('LRP leverage', () => {
1453
2467
  context.lucid.config().network!,
1454
2468
  ),
1455
2469
  {
1456
- min: Number(ocdFloor(orefs.iasset.datum.maintenanceRatio)),
1457
- max: Number(ocdFloor(orefs.iasset.datum.maintenanceRatio)) + 1,
2470
+ min:
2471
+ rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100,
2472
+ max:
2473
+ rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100 +
2474
+ 1,
1458
2475
  },
1459
2476
  );
1460
2477
 
1461
2478
  {
1462
- const lrps = await findAllLrps(context.lucid, sysParams, iasset);
2479
+ const lrps = await findAllRobs(
2480
+ context.lucid,
2481
+ sysParams,
2482
+ iusdAssetInfo.iassetTokenNameAscii,
2483
+ );
1463
2484
  expect(
1464
- lrps.every((lrp) => hadRobRedemption(lrp, sysParams.lrpParams)),
2485
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1465
2486
  ).toBeTruthy();
1466
2487
  }
1467
2488
  });
@@ -1469,74 +2490,81 @@ describe('LRP leverage', () => {
1469
2490
  test<MyContext>('Open max leverage leveraged CDP; 2 CDPs; price 2.5; f_r=.014; f_m=.006', async (context: MyContext) => {
1470
2491
  context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1471
2492
 
1472
- const [sysParams, __] = await init(
2493
+ const [sysParams, [iusdAssetInfo]] = await init(
1473
2494
  context.lucid,
1474
2495
  [
1475
2496
  {
1476
- ...iusdInitialAssetCfg,
1477
- priceOracle: {
1478
- ...iusdInitialAssetCfg.priceOracle,
1479
- startPrice: 2_500_000n,
1480
- },
1481
- interestOracle: {
1482
- ...iusdInitialAssetCfg.interestOracle,
1483
- initialInterestRate: 0n,
1484
- },
1485
- maintenanceRatioPercentage: 130_000_000n,
1486
- debtMintingFeePercentage: 600_000n,
1487
- redemptionReimbursementPercentage: 1_400_000n,
2497
+ ...iusdInitialAssetCfg(),
2498
+ collateralAssets: [
2499
+ {
2500
+ ...mkBaseCollateralAsset(adaAssetClass, 0n, {
2501
+ numerator: 25n,
2502
+ denominator: 10n,
2503
+ }),
2504
+ maintenanceRatio: { numerator: 13n, denominator: 10n },
2505
+ },
2506
+ ],
2507
+ debtMintingFeeRatio: { numerator: 6n, denominator: 1_000n },
2508
+ redemptionReimbursementRatio: { numerator: 14n, denominator: 1000n },
1488
2509
  },
1489
2510
  ],
1490
2511
  context.emulator.slot,
1491
2512
  );
1492
2513
 
1493
- const iasset = fromText(iusdInitialAssetCfg.name);
1494
-
1495
2514
  await openBuyRobs(
1496
2515
  context,
1497
2516
  sysParams,
1498
- iasset,
2517
+ iusdAssetInfo.iassetTokenNameAscii,
2518
+ adaAssetClass,
1499
2519
  [325_000_000n, 325_000_000n],
1500
- {
1501
- getOnChainInt: 3_000_000n,
1502
- },
2520
+ { numerator: 3n, denominator: 1n },
1503
2521
  );
1504
2522
 
1505
- const allLrps = await findAllLrps(context.lucid, sysParams, iasset);
2523
+ const allLrps = await findAllRobs(
2524
+ context.lucid,
2525
+ sysParams,
2526
+ iusdAssetInfo.iassetTokenNameAscii,
2527
+ );
1506
2528
 
1507
2529
  const orefs = await findAllNecessaryOrefs(
1508
2530
  context.lucid,
1509
2531
  sysParams,
1510
- toText(iasset),
2532
+ iusdAssetInfo.iassetTokenNameAscii,
2533
+ adaAssetClass,
1511
2534
  );
1512
2535
 
1513
2536
  const priceOracleUtxo = await findPriceOracleFromCollateralAsset(
1514
2537
  context.lucid,
1515
- orefs.iasset,
2538
+ orefs.collateralAsset,
1516
2539
  );
1517
2540
 
2541
+ if (priceOracleUtxo == null) {
2542
+ throw new Error('Expected oracle UTXO');
2543
+ }
2544
+
1518
2545
  const baseCollateral = 200_000_000n;
1519
2546
  const maxLeverage = calculateLeverageFromCollateralRatio(
1520
- fromHex(iasset),
1521
- orefs.iasset.datum.maintenanceRatio,
2547
+ fromHex(fromText(iusdAssetInfo.iassetTokenNameAscii)),
2548
+ adaAssetClass,
2549
+ orefs.collateralAsset.datum.maintenanceRatio,
1522
2550
  baseCollateral,
1523
2551
  parsePriceOracleDatum(getInlineDatumOrThrow(priceOracleUtxo)).price,
1524
- orefs.iasset.datum.debtMintingFeePercentage,
1525
- orefs.iasset.datum.redemptionReimbursementPercentage,
1526
- sysParams.lrpParams,
2552
+ orefs.iasset.datum.debtMintingFeeRatio,
2553
+ orefs.iasset.datum.redemptionReimbursementRatio,
1527
2554
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
1528
2555
  )!;
1529
2556
 
1530
2557
  await runAndAwaitTx(
1531
2558
  context.lucid,
1532
- leverageCdpWithLrp(
2559
+ leverageCdpWithRob(
1533
2560
  maxLeverage,
1534
2561
  baseCollateral,
1535
2562
  priceOracleUtxo,
1536
2563
  orefs.iasset.utxo,
2564
+ orefs.collateralAsset.utxo,
1537
2565
  orefs.cdpCreatorUtxo,
1538
2566
  orefs.interestOracleUtxo,
1539
- orefs.collectorUtxo,
2567
+ orefs.treasuryUtxo,
1540
2568
  sysParams,
1541
2569
  context.lucid,
1542
2570
  allLrps.map((lrps) => [lrps.utxo, lrps.datum]),
@@ -1576,17 +2604,23 @@ describe('LRP leverage', () => {
1576
2604
  context.lucid.config().network!,
1577
2605
  ),
1578
2606
  {
1579
- min: Number(ocdFloor(orefs.iasset.datum.maintenanceRatio)),
1580
- max: Number(ocdFloor(orefs.iasset.datum.maintenanceRatio)) + 1,
2607
+ min:
2608
+ rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100,
2609
+ max:
2610
+ rationalToFloat(orefs.collateralAsset.datum.maintenanceRatio) * 100 +
2611
+ 1,
1581
2612
  },
1582
2613
  );
1583
2614
 
1584
2615
  {
1585
- const lrps = await findAllLrps(context.lucid, sysParams, iasset);
2616
+ const lrps = await findAllRobs(
2617
+ context.lucid,
2618
+ sysParams,
2619
+ iusdAssetInfo.iassetTokenNameAscii,
2620
+ );
1586
2621
  expect(
1587
- lrps.every((lrp) => hadRobRedemption(lrp, sysParams.lrpParams)),
2622
+ lrps.every((lrp) => hadRobRedemption(lrp, sysParams)),
1588
2623
  ).toBeTruthy();
1589
2624
  }
1590
2625
  });
1591
2626
  });
1592
- */