@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.
- package/dist/index.d.mts +293 -765
- package/dist/index.d.ts +293 -765
- package/dist/index.js +1033 -476
- package/dist/index.mjs +948 -388
- package/package.json +1 -1
- package/src/contracts/cdp/helpers.ts +12 -0
- package/src/contracts/gov/transactions.ts +4 -14
- package/src/contracts/iasset/helpers.ts +1 -0
- package/src/contracts/price-oracle/types-new.ts +1 -1
- package/src/contracts/rob/helpers.ts +274 -196
- package/src/contracts/rob/transactions.ts +11 -5
- package/src/contracts/rob-leverage/helpers.ts +379 -371
- package/src/contracts/rob-leverage/transactions.ts +176 -104
- package/src/index.ts +0 -2
- package/src/utils/utils.ts +0 -3
- package/tests/cdp/cdp-queries.ts +1 -1
- package/tests/endpoints/initialize.ts +14 -2
- package/tests/interest-collection/interest-collector-queries.ts +2 -1
- package/tests/queries/collector-queries.ts +2 -1
- package/tests/queries/poll-queries.ts +2 -1
- package/tests/queries/treasury-queries.ts +2 -1
- package/tests/rob/rob-leverage.test.ts +1646 -612
- package/tests/rob/rob.test.ts +35 -19
- package/tests/rob/transactions-mutated.ts +6 -4
package/package.json
CHANGED
|
@@ -17,6 +17,18 @@ import {
|
|
|
17
17
|
rationalSub,
|
|
18
18
|
} from '../../types/rational';
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Amount of iasset equal in value to the given number of collateral amount.
|
|
22
|
+
*/
|
|
23
|
+
export function iassetValueOfCollateral(
|
|
24
|
+
collateralAmt: bigint,
|
|
25
|
+
oraclePrice: Rational,
|
|
26
|
+
): bigint {
|
|
27
|
+
return rationalFloor(
|
|
28
|
+
rationalDiv(rationalFromInt(collateralAmt), oraclePrice),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
/**
|
|
21
33
|
* This is mostly for debugging purposes.
|
|
22
34
|
*/
|
|
@@ -252,11 +252,7 @@ export async function createProposal(
|
|
|
252
252
|
),
|
|
253
253
|
)
|
|
254
254
|
.validFrom(Number(currentTime) - ONE_SECOND)
|
|
255
|
-
.validTo(
|
|
256
|
-
Number(currentTime) +
|
|
257
|
-
Number(sysParams.govParams.gBiasTime) -
|
|
258
|
-
ONE_SECOND,
|
|
259
|
-
)
|
|
255
|
+
.validTo(Number(currentTime + sysParams.govParams.gBiasTime) - ONE_SECOND)
|
|
260
256
|
.addSigner(ownAddr),
|
|
261
257
|
newPollId,
|
|
262
258
|
];
|
|
@@ -325,9 +321,7 @@ export async function createShardsChunks(
|
|
|
325
321
|
.newTx()
|
|
326
322
|
.validFrom(Number(currentTime) - ONE_SECOND)
|
|
327
323
|
.validTo(
|
|
328
|
-
Number(currentTime)
|
|
329
|
-
Number(sysParams.pollManagerParams.pBiasTime) -
|
|
330
|
-
ONE_SECOND,
|
|
324
|
+
Number(currentTime + sysParams.pollManagerParams.pBiasTime) - ONE_SECOND,
|
|
331
325
|
)
|
|
332
326
|
.mintAssets(mkAssetsOf(pollNft, shardsCount), Data.void())
|
|
333
327
|
// Ref scripts
|
|
@@ -580,9 +574,7 @@ export async function mergeShards(
|
|
|
580
574
|
.newTx()
|
|
581
575
|
.validFrom(Number(currentTime) - ONE_SECOND)
|
|
582
576
|
.validTo(
|
|
583
|
-
Number(currentTime)
|
|
584
|
-
Number(sysParams.pollManagerParams.pBiasTime) -
|
|
585
|
-
ONE_SECOND,
|
|
577
|
+
Number(currentTime + sysParams.pollManagerParams.pBiasTime) - ONE_SECOND,
|
|
586
578
|
)
|
|
587
579
|
.readFrom([
|
|
588
580
|
pollShardRefScriptUtxo,
|
|
@@ -1561,9 +1553,7 @@ export async function executeProposal(
|
|
|
1561
1553
|
|
|
1562
1554
|
tx.readFrom([upgradeTokenPolicyRefScriptUtxo, executeRefScriptUtxo])
|
|
1563
1555
|
.validFrom(Number(currentTime) - ONE_SECOND)
|
|
1564
|
-
.validTo(
|
|
1565
|
-
Number(currentTime) + Number(sysParams.govParams.gBiasTime) - ONE_SECOND,
|
|
1566
|
-
)
|
|
1556
|
+
.validTo(Number(currentTime + sysParams.govParams.gBiasTime) - ONE_SECOND)
|
|
1567
1557
|
.collectFrom([executeUtxo], Data.void())
|
|
1568
1558
|
.mintAssets(
|
|
1569
1559
|
mkAssetsOf(fromSystemParamsAsset(sysParams.govParams.upgradeToken), -1n),
|
|
@@ -95,6 +95,7 @@ export function attachOracle(
|
|
|
95
95
|
.with(
|
|
96
96
|
{ DeferredValidation: { feedValHash: P.select() } },
|
|
97
97
|
async (feedValHash) => {
|
|
98
|
+
if (priceOracleOref) throw new Error('Cannot pass price oracle oref');
|
|
98
99
|
if (!pythMessage) throw new Error('Missing Pyth message');
|
|
99
100
|
if (!pythStateOref) throw new Error('Missing pyth state out ref');
|
|
100
101
|
|
|
@@ -20,7 +20,7 @@ export const OracleIdxSchema = TSchema.Union(
|
|
|
20
20
|
TSchema.Literal('OracleVoid', { flatInUnion: true }),
|
|
21
21
|
);
|
|
22
22
|
|
|
23
|
-
export type OracleIdx = typeof OracleIdxSchema;
|
|
23
|
+
export type OracleIdx = typeof OracleIdxSchema.Type;
|
|
24
24
|
|
|
25
25
|
const PriceOracleDatumSchema = TSchema.Struct({
|
|
26
26
|
price: RationalSchema,
|
|
@@ -13,14 +13,15 @@ import {
|
|
|
13
13
|
serialiseRobRedeemer,
|
|
14
14
|
} from './types-new';
|
|
15
15
|
import { calculateFeeFromRatio } from '../../utils/indigo-helpers';
|
|
16
|
-
import { zeroNegatives } from '../../utils/bigint-utils';
|
|
16
|
+
import { BigIntOrd, sum, zeroNegatives } from '../../utils/bigint-utils';
|
|
17
17
|
import {
|
|
18
18
|
readonlyArray as RA,
|
|
19
19
|
array as A,
|
|
20
20
|
function as F,
|
|
21
21
|
option as O,
|
|
22
|
+
ord as Ord,
|
|
22
23
|
} from 'fp-ts';
|
|
23
|
-
import { SystemParams } from '../../types/system-params';
|
|
24
|
+
import { CurrencySymbolSP, SystemParams } from '../../types/system-params';
|
|
24
25
|
import { match, P } from 'ts-pattern';
|
|
25
26
|
import { getInlineDatumOrThrow } from '../../utils/lucid-utils';
|
|
26
27
|
import {
|
|
@@ -33,19 +34,22 @@ import {
|
|
|
33
34
|
} from '@3rd-eye-labs/cardano-offchain-common';
|
|
34
35
|
import {
|
|
35
36
|
Rational,
|
|
36
|
-
rationalDiv,
|
|
37
37
|
rationalFloor,
|
|
38
38
|
rationalFromInt,
|
|
39
39
|
rationalMul,
|
|
40
|
+
rationalToFloat,
|
|
40
41
|
} from '../../types/rational';
|
|
42
|
+
import { insertSorted, shuffle } from '../../utils/array-utils';
|
|
43
|
+
import { iassetValueOfCollateral } from '../cdp/helpers';
|
|
44
|
+
import { OracleIdx } from '../price-oracle/types-new';
|
|
41
45
|
|
|
42
46
|
export const MIN_ROB_COLLATERAL_AMT = 3_000_000n;
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
): bigint {
|
|
48
|
+
/**
|
|
49
|
+
* The amount of collateral asset available in the ROB when buy order. In case of ADA, take
|
|
50
|
+
* into account the min UTXO collateral.
|
|
51
|
+
*/
|
|
52
|
+
export function robCollateralAmtToSpend(utxo: UTxO, datum: RobDatum): bigint {
|
|
49
53
|
return match(datum.orderType)
|
|
50
54
|
.returnType<bigint>()
|
|
51
55
|
.with({ BuyIAssetOrder: P.select() }, (content) => {
|
|
@@ -57,55 +61,118 @@ export function robAmountToSpend(
|
|
|
57
61
|
return assetClassValueOf(utxo.assets, content.collateralAsset);
|
|
58
62
|
}
|
|
59
63
|
})
|
|
64
|
+
.otherwise(() => {
|
|
65
|
+
throw new Error('Collateral to spend is relevant only for Buy orders.');
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The amount if iassets available in ROB when sell order.
|
|
71
|
+
*/
|
|
72
|
+
export function robIAssetAmtToSpend(
|
|
73
|
+
utxo: UTxO,
|
|
74
|
+
datum: RobDatum,
|
|
75
|
+
iassetCurrencySymbol: CurrencySymbolSP,
|
|
76
|
+
) {
|
|
77
|
+
return match(datum.orderType)
|
|
78
|
+
.returnType<bigint>()
|
|
60
79
|
.with({ SellIAssetOrder: P.any }, (_) => {
|
|
61
80
|
return assetClassValueOf(utxo.assets, {
|
|
62
|
-
currencySymbol: fromHex(
|
|
63
|
-
sysParams.cdpParams.cdpAssetSymbol.unCurrencySymbol,
|
|
64
|
-
),
|
|
81
|
+
currencySymbol: fromHex(iassetCurrencySymbol.unCurrencySymbol),
|
|
65
82
|
tokenName: datum.iasset,
|
|
66
83
|
});
|
|
67
84
|
})
|
|
85
|
+
.otherwise(() => {
|
|
86
|
+
throw new Error('IAssets to spend is relevant only for Sell orders.');
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Amount to spend from the ROB universal for Buy and sell orders.
|
|
92
|
+
*/
|
|
93
|
+
export function robAmtToSpend(
|
|
94
|
+
utxo: UTxO,
|
|
95
|
+
datum: RobDatum,
|
|
96
|
+
iassetCurrencySymbol: CurrencySymbolSP,
|
|
97
|
+
): bigint {
|
|
98
|
+
return match(datum.orderType)
|
|
99
|
+
.with({ BuyIAssetOrder: P.any }, () => robCollateralAmtToSpend(utxo, datum))
|
|
100
|
+
.with({ SellIAssetOrder: P.any }, () =>
|
|
101
|
+
robIAssetAmtToSpend(utxo, datum, iassetCurrencySymbol),
|
|
102
|
+
)
|
|
68
103
|
.exhaustive();
|
|
69
104
|
}
|
|
70
105
|
|
|
71
|
-
export function
|
|
106
|
+
export function robBuyOrderSummary(
|
|
72
107
|
utxo: UTxO,
|
|
73
108
|
datum: RobDatum,
|
|
74
|
-
|
|
109
|
+
oraclePrice: Rational,
|
|
110
|
+
): {
|
|
111
|
+
/**
|
|
112
|
+
* The amount that can be spent from the ROB.
|
|
113
|
+
*/
|
|
114
|
+
redeemableCollateral: bigint;
|
|
115
|
+
/**
|
|
116
|
+
* The amount paid to the ROB when everything redeemed.
|
|
117
|
+
*/
|
|
118
|
+
payoutIAsset: bigint;
|
|
119
|
+
} {
|
|
120
|
+
const redeemable = robCollateralAmtToSpend(utxo, datum);
|
|
121
|
+
|
|
122
|
+
const payoutAmt = iassetValueOfCollateral(redeemable, oraclePrice);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
redeemableCollateral: redeemable,
|
|
126
|
+
payoutIAsset: payoutAmt,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* In case it's applied to a sell order instead, it will throw an error.
|
|
132
|
+
*/
|
|
133
|
+
export function isBuyOrderFullyRedeemed(
|
|
134
|
+
utxo: UTxO,
|
|
135
|
+
datum: RobDatum,
|
|
136
|
+
oraclePrice: Rational,
|
|
75
137
|
): boolean {
|
|
76
|
-
|
|
77
|
-
|
|
138
|
+
const summary = robBuyOrderSummary(utxo, datum, oraclePrice);
|
|
139
|
+
|
|
140
|
+
return summary.redeemableCollateral <= 0n || summary.payoutIAsset <= 0n;
|
|
78
141
|
}
|
|
79
142
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
//
|
|
108
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Use the limit prices to decide fully redeemed.
|
|
145
|
+
*/
|
|
146
|
+
export function isFullyRedeemed(
|
|
147
|
+
utxo: UTxO,
|
|
148
|
+
datum: RobDatum,
|
|
149
|
+
iassetCurrencySymbol: CurrencySymbolSP,
|
|
150
|
+
): boolean {
|
|
151
|
+
return match(datum.orderType)
|
|
152
|
+
.returnType<boolean>()
|
|
153
|
+
.with({ BuyIAssetOrder: P.select() }, (content) =>
|
|
154
|
+
isBuyOrderFullyRedeemed(utxo, datum, content.maxPrice),
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
.with({ SellIAssetOrder: P.select() }, (content) => {
|
|
158
|
+
const iassetToSpend = robIAssetAmtToSpend(
|
|
159
|
+
utxo,
|
|
160
|
+
datum,
|
|
161
|
+
iassetCurrencySymbol,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const payoutAmts = content.allowedCollateralAssets.map((c) =>
|
|
165
|
+
rationalFloor(rationalMul(rationalFromInt(iassetToSpend), c[1])),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
iassetToSpend <= 0n ||
|
|
170
|
+
// When for every allowed collateral asset the payout would be 0
|
|
171
|
+
payoutAmts.every((amt) => amt <= 0n)
|
|
172
|
+
);
|
|
173
|
+
})
|
|
174
|
+
.exhaustive();
|
|
175
|
+
}
|
|
109
176
|
|
|
110
177
|
/**
|
|
111
178
|
* Right now we allow multi redemptions when the collateral asset, iasset pair is the same.
|
|
@@ -129,13 +196,13 @@ export function buildRedemptionsTx(
|
|
|
129
196
|
txOutputsBeforeCount: bigint,
|
|
130
197
|
collateralAssetRefInputIdx: bigint,
|
|
131
198
|
iassetRefInputIdx: bigint,
|
|
132
|
-
oracleIdx:
|
|
199
|
+
oracleIdx: OracleIdx,
|
|
133
200
|
): TxBuilder {
|
|
134
201
|
return F.pipe(
|
|
135
202
|
redemptions,
|
|
136
203
|
A.reduceWithIndex<[UTxO, bigint], TxBuilder>(
|
|
137
204
|
tx,
|
|
138
|
-
(idx, acc, [robUtxo,
|
|
205
|
+
(idx, acc, [robUtxo, payoutAmt]) => {
|
|
139
206
|
const robDatum = parseRobDatumOrThrow(getInlineDatumOrThrow(robUtxo));
|
|
140
207
|
|
|
141
208
|
if (toHex(robDatum.iasset) !== toHex(iasset)) {
|
|
@@ -151,7 +218,7 @@ export function buildRedemptionsTx(
|
|
|
151
218
|
throw new Error('Only same collateral asset');
|
|
152
219
|
}
|
|
153
220
|
|
|
154
|
-
const payoutIAssetAmt =
|
|
221
|
+
const payoutIAssetAmt = payoutAmt;
|
|
155
222
|
|
|
156
223
|
const reimburstmentIAsset = calculateFeeFromRatio(
|
|
157
224
|
redemptionReimbursementRatio,
|
|
@@ -192,18 +259,16 @@ export function buildRedemptionsTx(
|
|
|
192
259
|
}),
|
|
193
260
|
);
|
|
194
261
|
|
|
195
|
-
const payoutCollateralAmt =
|
|
262
|
+
const payoutCollateralAmt = payoutAmt;
|
|
196
263
|
|
|
197
264
|
const reimbursementCollateral = calculateFeeFromRatio(
|
|
198
265
|
redemptionReimbursementRatio,
|
|
199
266
|
payoutCollateralAmt,
|
|
200
267
|
);
|
|
201
268
|
|
|
202
|
-
const redeemedIAssetAmt =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
price,
|
|
206
|
-
),
|
|
269
|
+
const redeemedIAssetAmt = iassetValueOfCollateral(
|
|
270
|
+
payoutCollateralAmt - reimbursementCollateral,
|
|
271
|
+
price,
|
|
207
272
|
);
|
|
208
273
|
|
|
209
274
|
const resultVal = addAssets(
|
|
@@ -225,7 +290,9 @@ export function buildRedemptionsTx(
|
|
|
225
290
|
.exhaustive();
|
|
226
291
|
|
|
227
292
|
if (lovelacesAmt(robOutputVal) < MIN_ROB_COLLATERAL_AMT) {
|
|
228
|
-
throw new Error(
|
|
293
|
+
throw new Error(
|
|
294
|
+
'Redeeming more than available or selected ROB was incorrectly initialised.',
|
|
295
|
+
);
|
|
229
296
|
}
|
|
230
297
|
|
|
231
298
|
return acc
|
|
@@ -239,10 +306,7 @@ export function buildRedemptionsTx(
|
|
|
239
306
|
iassetRefInputIdx: iassetRefInputIdx,
|
|
240
307
|
continuingOutputIdx: txOutputsBeforeCount + BigInt(idx),
|
|
241
308
|
sellOrderAllowedAssetsIdx: sellOrderAllowedAssetsIdx,
|
|
242
|
-
priceOracleIdx:
|
|
243
|
-
oracleIdx != null
|
|
244
|
-
? { OracleRefInputIdx: oracleIdx }
|
|
245
|
-
: 'OracleVoid',
|
|
309
|
+
priceOracleIdx: oracleIdx,
|
|
246
310
|
},
|
|
247
311
|
}),
|
|
248
312
|
})
|
|
@@ -265,141 +329,155 @@ export function buildRedemptionsTx(
|
|
|
265
329
|
);
|
|
266
330
|
}
|
|
267
331
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
//
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Given all available LRP UTXOs, calculate total available collateral that can be redeemed.
|
|
334
|
+
* Taking into account incorrectly initialised LRPs (without base collateral) and max number of ROBs.
|
|
335
|
+
*/
|
|
336
|
+
export function calculateTotalCollateralForRedemption(
|
|
337
|
+
iasset: Uint8Array<ArrayBufferLike>,
|
|
338
|
+
collateralAsset: AssetClass,
|
|
339
|
+
iassetPrice: Rational,
|
|
340
|
+
allRobs: [UTxO, RobDatum][],
|
|
341
|
+
/**
|
|
342
|
+
* How many LRPs can be redeemed in a single Tx.
|
|
343
|
+
*/
|
|
344
|
+
maxRobsInTx: number,
|
|
345
|
+
): bigint {
|
|
346
|
+
return F.pipe(
|
|
347
|
+
allRobs,
|
|
348
|
+
A.filterMap(([utxo, datum]) => {
|
|
349
|
+
const isCorrectOrder = match(datum.orderType)
|
|
350
|
+
.returnType<boolean>()
|
|
351
|
+
.with(
|
|
352
|
+
{ BuyIAssetOrder: P.select() },
|
|
353
|
+
(content) =>
|
|
354
|
+
isSameAssetClass(content.collateralAsset, collateralAsset) &&
|
|
355
|
+
rationalToFloat(content.maxPrice) >= rationalToFloat(iassetPrice) &&
|
|
356
|
+
!isBuyOrderFullyRedeemed(utxo, datum, iassetPrice),
|
|
357
|
+
)
|
|
358
|
+
.otherwise(() => false);
|
|
359
|
+
|
|
360
|
+
if (toHex(datum.iasset) !== toHex(iasset) || !isCorrectOrder) {
|
|
361
|
+
return O.none;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// We constrained in the logic above that the ROB is a buy order and is not yet fully redeemed.
|
|
365
|
+
const collateralToSpend = robCollateralAmtToSpend(utxo, datum);
|
|
366
|
+
|
|
367
|
+
return O.some(collateralToSpend);
|
|
368
|
+
}),
|
|
369
|
+
// From largest to smallest
|
|
370
|
+
A.sort(Ord.reverse(BigIntOrd)),
|
|
371
|
+
// We can fit only this number of redemptions with CDP open into a single Tx.
|
|
372
|
+
A.takeLeft(maxRobsInTx),
|
|
373
|
+
sum,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Pick random subset from all the ROBs (it does the necessary filtering) satisfying the target collateral to spend.
|
|
379
|
+
* It's relevant for BUY orders only.
|
|
380
|
+
*/
|
|
381
|
+
export function randomRobsSubsetSatisfyingTargetCollateral(
|
|
382
|
+
iasset: Uint8Array<ArrayBufferLike>,
|
|
383
|
+
collateralAsset: AssetClass,
|
|
384
|
+
targetCollateralToSpend: bigint,
|
|
385
|
+
iassetPrice: Rational,
|
|
386
|
+
allLrps: [UTxO, RobDatum][],
|
|
387
|
+
/**
|
|
388
|
+
* How many LRPs can be redeemed in a single Tx.
|
|
389
|
+
*/
|
|
390
|
+
maxLrpsInTx: number,
|
|
391
|
+
randomiseFn: (arr: [UTxO, RobDatum][]) => [UTxO, RobDatum][] = shuffle,
|
|
392
|
+
): [UTxO, RobDatum][] {
|
|
393
|
+
if (
|
|
394
|
+
targetCollateralToSpend <= 0n ||
|
|
395
|
+
iassetValueOfCollateral(targetCollateralToSpend, iassetPrice) <= 0n
|
|
396
|
+
) {
|
|
397
|
+
throw new Error('Must redeem and payout more than 0.');
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const shuffled = randomiseFn(
|
|
401
|
+
F.pipe(
|
|
402
|
+
allLrps,
|
|
403
|
+
A.filter(
|
|
404
|
+
([utxo, datum]) =>
|
|
405
|
+
toHex(datum.iasset) === toHex(iasset) &&
|
|
406
|
+
match(datum.orderType)
|
|
407
|
+
.with(
|
|
408
|
+
{ BuyIAssetOrder: P.select() },
|
|
409
|
+
(content) =>
|
|
410
|
+
isSameAssetClass(collateralAsset, content.collateralAsset) &&
|
|
411
|
+
rationalToFloat(content.maxPrice) >=
|
|
412
|
+
rationalToFloat(iassetPrice),
|
|
413
|
+
)
|
|
414
|
+
// Only buy order types
|
|
415
|
+
.otherwise(() => false) &&
|
|
416
|
+
!isBuyOrderFullyRedeemed(utxo, datum, iassetPrice),
|
|
417
|
+
),
|
|
418
|
+
),
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
// Sorted from highest to lowest by lovelaces to spend
|
|
422
|
+
let result: [UTxO, RobDatum][] = [];
|
|
423
|
+
let runningSum = 0n;
|
|
424
|
+
|
|
425
|
+
for (let i = 0; i < shuffled.length; i++) {
|
|
426
|
+
const element = shuffled[i];
|
|
427
|
+
|
|
428
|
+
const lovelacesToSpend = robCollateralAmtToSpend(element[0], element[1]);
|
|
429
|
+
|
|
430
|
+
const remainingToRedeem = targetCollateralToSpend - runningSum;
|
|
431
|
+
const remainingToPayout = iassetValueOfCollateral(
|
|
432
|
+
remainingToRedeem,
|
|
433
|
+
iassetPrice,
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// When we can't add a new redemption because otherwise there would be no payout.
|
|
437
|
+
// Try to replace the smallest collected with a following larger one when available.
|
|
438
|
+
if (result.length > 0 && remainingToPayout <= 0n) {
|
|
439
|
+
const last = result[result.length - 1];
|
|
440
|
+
|
|
441
|
+
const lastSummary = robBuyOrderSummary(last[0], last[1], iassetPrice);
|
|
442
|
+
|
|
443
|
+
// Pop the smallest collected when the current is larger.
|
|
444
|
+
if (lastSummary.redeemableCollateral < lovelacesToSpend) {
|
|
445
|
+
result.pop()!;
|
|
446
|
+
runningSum -= lastSummary.redeemableCollateral;
|
|
447
|
+
} else {
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
result = insertSorted(
|
|
453
|
+
result,
|
|
454
|
+
element,
|
|
455
|
+
Ord.contramap<bigint, [UTxO, RobDatum]>(
|
|
456
|
+
([utxo, dat]) => robCollateralAmtToSpend(utxo, dat),
|
|
457
|
+
// From highest to lowest
|
|
458
|
+
)(Ord.reverse(BigIntOrd)),
|
|
459
|
+
);
|
|
460
|
+
runningSum += lovelacesToSpend;
|
|
461
|
+
|
|
462
|
+
// When more items than max allowed, pop the one with smallest value
|
|
463
|
+
if (result.length > maxLrpsInTx) {
|
|
464
|
+
const popped = result.pop()!;
|
|
465
|
+
runningSum -= robCollateralAmtToSpend(popped[0], popped[1]);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (runningSum >= targetCollateralToSpend) {
|
|
469
|
+
return result;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const remainingToSpend = targetCollateralToSpend - runningSum;
|
|
474
|
+
|
|
475
|
+
if (
|
|
476
|
+
remainingToSpend > 0n &&
|
|
477
|
+
iassetValueOfCollateral(remainingToSpend, iassetPrice) > 0n
|
|
478
|
+
) {
|
|
479
|
+
throw new Error("Couldn't achieve target lovelaces");
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return result;
|
|
483
|
+
}
|