@indigo-labs/indigo-sdk 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/.github/workflows/ci.yml +8 -3
  2. package/dist/index.d.mts +1298 -677
  3. package/dist/index.d.ts +1298 -677
  4. package/dist/index.js +4650 -2217
  5. package/dist/index.mjs +4592 -2192
  6. package/eslint.config.mjs +7 -1
  7. package/package.json +9 -4
  8. package/src/contracts/cdp/helpers.ts +167 -0
  9. package/src/contracts/cdp/scripts.ts +33 -0
  10. package/src/contracts/cdp/transactions.ts +1310 -0
  11. package/src/contracts/cdp/types.ts +161 -0
  12. package/src/contracts/cdp-creator/scripts.ts +39 -0
  13. package/src/{types/indigo/cdp-creator.ts → contracts/cdp-creator/types.ts} +6 -4
  14. package/src/contracts/collector/scripts.ts +32 -0
  15. package/src/contracts/collector/transactions.ts +44 -0
  16. package/src/contracts/execute/scripts.ts +48 -0
  17. package/src/contracts/execute/types.ts +57 -0
  18. package/src/contracts/gov/helpers.ts +157 -0
  19. package/src/contracts/gov/scripts.ts +34 -0
  20. package/src/contracts/gov/transactions.ts +1224 -0
  21. package/src/contracts/gov/types-new.ts +115 -0
  22. package/src/contracts/gov/types.ts +89 -0
  23. package/src/{helpers/interest-oracle.ts → contracts/interest-oracle/helpers.ts} +37 -9
  24. package/src/contracts/interest-oracle/scripts.ts +18 -0
  25. package/src/contracts/interest-oracle/transactions.ts +149 -0
  26. package/src/{types/indigo/interest-oracle.ts → contracts/interest-oracle/types.ts} +1 -1
  27. package/src/contracts/lrp/scripts.ts +27 -0
  28. package/src/contracts/{lrp.ts → lrp/transactions.ts} +23 -23
  29. package/src/{types/indigo/lrp.ts → contracts/lrp/types.ts} +3 -3
  30. package/src/{scripts/one-shot-policy.ts → contracts/one-shot/scripts.ts} +1 -1
  31. package/src/contracts/{one-shot.ts → one-shot/transactions.ts} +3 -3
  32. package/src/contracts/poll/helpers.ts +55 -0
  33. package/src/contracts/poll/scripts.ts +72 -0
  34. package/src/contracts/poll/types-poll-manager.ts +38 -0
  35. package/src/contracts/poll/types-poll-shard.ts +38 -0
  36. package/src/contracts/poll/types-poll.ts +88 -0
  37. package/src/{scripts/price-oracle-validator.ts → contracts/price-oracle/scripts.ts} +1 -4
  38. package/src/contracts/price-oracle/transactions.ts +112 -0
  39. package/src/{types/indigo/price-oracle.ts → contracts/price-oracle/types.ts} +16 -4
  40. package/src/{helpers/stability-pool-helpers.ts → contracts/stability-pool/helpers.ts} +110 -6
  41. package/src/contracts/stability-pool/scripts.ts +46 -0
  42. package/src/contracts/stability-pool/transactions.ts +660 -0
  43. package/src/contracts/stability-pool/types-new.ts +208 -0
  44. package/src/contracts/stability-pool/types.ts +42 -0
  45. package/src/contracts/staking/helpers.ts +116 -0
  46. package/src/contracts/staking/scripts.ts +41 -0
  47. package/src/contracts/staking/transactions.ts +268 -0
  48. package/src/contracts/staking/types-new.ts +81 -0
  49. package/src/contracts/staking/types.ts +41 -0
  50. package/src/contracts/treasury/scripts.ts +37 -0
  51. package/src/contracts/treasury/transactions.ts +44 -0
  52. package/src/contracts/treasury/types.ts +55 -0
  53. package/src/contracts/version-registry/scripts.ts +29 -0
  54. package/src/contracts/version-registry/types-new.ts +19 -0
  55. package/src/{types/indigo/version-record.ts → contracts/version-registry/types.ts} +1 -1
  56. package/src/contracts/vesting/helpers.ts +267 -0
  57. package/src/index.ts +38 -33
  58. package/src/types/evolution-schema-options.ts +16 -0
  59. package/src/types/generic.ts +78 -60
  60. package/src/types/on-chain-decimal.ts +22 -0
  61. package/src/types/system-params.ts +22 -11
  62. package/src/utils/bigint-utils.ts +7 -0
  63. package/src/{helpers → utils}/helper-txs.ts +1 -0
  64. package/src/utils/time-helpers.ts +4 -0
  65. package/src/{helpers/helpers.ts → utils/utils.ts} +0 -10
  66. package/src/{helpers → utils}/value-helpers.ts +10 -0
  67. package/src/{scripts → validators}/cdp-creator-validator.ts +4 -50
  68. package/src/{scripts → validators}/cdp-validator.ts +3 -5
  69. package/src/{scripts → validators}/collector-validator.ts +2 -3
  70. package/src/{scripts/version-registry.ts → validators/execute-validator.ts} +3 -11
  71. package/src/{scripts/gov-validator.ts → validators/governance-validator.ts} +3 -40
  72. package/src/{scripts → validators}/interest-oracle-validator.ts +4 -20
  73. package/src/validators/lrp-validator.ts +7 -0
  74. package/src/validators/poll-manager-validator.ts +7 -0
  75. package/src/{scripts → validators}/poll-shard-validator.ts +3 -43
  76. package/src/{scripts → validators}/stability-pool-validator.ts +4 -57
  77. package/src/{scripts → validators}/staking-validator.ts +2 -3
  78. package/src/{scripts → validators}/treasury-validator.ts +2 -3
  79. package/src/{scripts → validators}/version-record-policy.ts +4 -23
  80. package/src/validators/version-registry-validator.ts +7 -0
  81. package/tests/cdp.test.ts +1565 -0
  82. package/tests/datums.test.ts +125 -108
  83. package/tests/endpoints/initialize.ts +240 -338
  84. package/tests/gov.test.ts +1874 -0
  85. package/tests/hash-checks.test.ts +26 -21
  86. package/tests/indigo-test-helpers.ts +1 -55
  87. package/tests/initialize.test.ts +10 -5
  88. package/tests/interest-calculations.test.ts +18 -18
  89. package/tests/interest-oracle.test.ts +20 -18
  90. package/tests/lrp.test.ts +180 -78
  91. package/tests/mock/assets-mock.ts +59 -0
  92. package/tests/queries/cdp-queries.ts +144 -0
  93. package/tests/queries/collector-queries.ts +26 -0
  94. package/tests/queries/execute-queries.ts +46 -0
  95. package/tests/queries/governance-queries.ts +19 -16
  96. package/tests/queries/iasset-queries.ts +46 -23
  97. package/tests/queries/interest-oracle-queries.ts +3 -6
  98. package/tests/queries/lrp-queries.ts +2 -2
  99. package/tests/queries/poll-queries.ts +97 -0
  100. package/tests/queries/price-oracle-queries.ts +5 -22
  101. package/tests/queries/stability-pool-queries.ts +10 -8
  102. package/tests/queries/staking-queries.ts +28 -19
  103. package/tests/queries/treasury-queries.ts +19 -0
  104. package/tests/stability-pool.test.ts +186 -71
  105. package/tests/staking.test.ts +30 -23
  106. package/tests/test-helpers.ts +11 -2
  107. package/tests/utils/asserts.ts +13 -0
  108. package/tests/utils/index.ts +50 -0
  109. package/tsconfig.json +3 -1
  110. package/vitest.config.ts +1 -1
  111. package/.github/workflows/test.yml +0 -44
  112. package/src/contracts/cdp.ts +0 -748
  113. package/src/contracts/collector.ts +0 -98
  114. package/src/contracts/gov.ts +0 -1
  115. package/src/contracts/interest-oracle.ts +0 -149
  116. package/src/contracts/stability-pool.ts +0 -692
  117. package/src/contracts/staking.ts +0 -348
  118. package/src/contracts/treasury.ts +0 -112
  119. package/src/helpers/asset-helpers.ts +0 -57
  120. package/src/helpers/staking-helpers.ts +0 -94
  121. package/src/helpers/time-helpers.ts +0 -4
  122. package/src/scripts/execute-validator.ts +0 -52
  123. package/src/scripts/lrp-validator.ts +0 -40
  124. package/src/scripts/poll-manager-validator.ts +0 -52
  125. package/src/types/indigo/cdp.ts +0 -88
  126. package/src/types/indigo/execute.ts +0 -21
  127. package/src/types/indigo/gov.ts +0 -51
  128. package/src/types/indigo/poll-manager.ts +0 -21
  129. package/src/types/indigo/poll-shard.ts +0 -16
  130. package/src/types/indigo/stability-pool.ts +0 -233
  131. package/src/types/indigo/staking.ts +0 -99
  132. /package/src/{types/one-shot.ts → contracts/one-shot/types.ts} +0 -0
  133. /package/src/{helpers/price-oracle-helpers.ts → contracts/price-oracle/helpers.ts} +0 -0
  134. /package/src/{helpers → utils}/indigo-helpers.ts +0 -0
  135. /package/src/{helpers → utils}/lucid-utils.ts +0 -0
@@ -0,0 +1,1874 @@
1
+ import {
2
+ addAssets,
3
+ Data,
4
+ Emulator,
5
+ EmulatorAccount,
6
+ fromHex,
7
+ fromText,
8
+ generateEmulatorAccount,
9
+ Lucid,
10
+ unixTimeToSlot,
11
+ UTxO,
12
+ } from '@lucid-evolution/lucid';
13
+ import { describe, beforeEach, test, expect, assert } from 'vitest';
14
+ import {
15
+ assetClassValueOf,
16
+ mkAssetsOf,
17
+ mkLovelacesOf,
18
+ } from '../src/utils/value-helpers';
19
+ import { init } from './endpoints/initialize';
20
+ import { findGov } from './queries/governance-queries';
21
+ import {
22
+ addrDetails,
23
+ addressFromBech32,
24
+ createProposal,
25
+ createScriptAddress,
26
+ createShardsChunks,
27
+ endProposal,
28
+ executeProposal,
29
+ fromSystemParamsAsset,
30
+ matchSingle,
31
+ mergeShards,
32
+ ONE_DAY,
33
+ openStakingPosition,
34
+ startInterestOracle,
35
+ SystemParams,
36
+ vote,
37
+ VoteOption,
38
+ } from '../src';
39
+ import {
40
+ LucidContext,
41
+ runAndAwaitTx,
42
+ runAndAwaitTxBuilder,
43
+ } from './test-helpers';
44
+ import { startPriceOracleTx } from '../src/contracts/price-oracle/transactions';
45
+ import { findAllIAssets, findIAsset } from './queries/iasset-queries';
46
+ import {
47
+ findAllPollShards,
48
+ findPollManager,
49
+ findRandomPollShard,
50
+ } from './queries/poll-queries';
51
+ import { findStakingPosition } from './queries/staking-queries';
52
+ import {
53
+ readonlyArray as RA,
54
+ task as T,
55
+ array as A,
56
+ function as F,
57
+ number as N,
58
+ } from 'fp-ts';
59
+ import { findExecute } from './queries/execute-queries';
60
+ import {
61
+ getNewUtxosAtAddressAfterAction,
62
+ getValueChangeAtAddressAfterAction,
63
+ } from './utils';
64
+ import { serialiseUpgradePaths } from '../src/contracts/gov/types-new';
65
+ import { iusdInitialAssetCfg } from './mock/assets-mock';
66
+
67
+ type MyContext = LucidContext<{
68
+ admin: EmulatorAccount;
69
+ user: EmulatorAccount;
70
+ withdrawalAccount: EmulatorAccount;
71
+ }>;
72
+
73
+ async function createUtxoAtTreasury(
74
+ indyAmt: bigint,
75
+ sysParams: SystemParams,
76
+ context: MyContext,
77
+ ): Promise<UTxO> {
78
+ const treasuryAddr = createScriptAddress(
79
+ context.lucid.config().network!,
80
+ sysParams.validatorHashes.treasuryHash,
81
+ );
82
+
83
+ const tx = context.lucid
84
+ .newTx()
85
+ .pay.ToContract(
86
+ treasuryAddr,
87
+ { kind: 'inline', value: Data.void() },
88
+ mkAssetsOf(fromSystemParamsAsset(sysParams.govParams.indyAsset), indyAmt),
89
+ );
90
+
91
+ const [_, utxos] = await getNewUtxosAtAddressAfterAction(
92
+ context.lucid,
93
+ treasuryAddr,
94
+ () => runAndAwaitTxBuilder(context.lucid, tx),
95
+ );
96
+
97
+ return matchSingle(utxos, () => new Error('Expected a single treasury UTXO'));
98
+ }
99
+
100
+ async function runVote(
101
+ pollId: bigint,
102
+ option: VoteOption,
103
+ sysParams: SystemParams,
104
+ context: MyContext,
105
+ ): Promise<void> {
106
+ const [pkh, _] = await addrDetails(context.lucid);
107
+
108
+ const stakingPosOref = await findStakingPosition(
109
+ context.lucid,
110
+ sysParams.validatorHashes.stakingHash,
111
+ fromSystemParamsAsset(sysParams.stakingParams.stakingToken),
112
+ pkh.hash,
113
+ );
114
+
115
+ const pollShard = await findRandomPollShard(
116
+ context.lucid,
117
+ sysParams.validatorHashes.pollShardHash,
118
+ fromSystemParamsAsset(sysParams.pollShardParams.pollToken),
119
+ pollId,
120
+ );
121
+
122
+ await runAndAwaitTx(
123
+ context.lucid,
124
+ vote(
125
+ option,
126
+ pollShard.utxo,
127
+ stakingPosOref.utxo,
128
+ sysParams,
129
+ context.lucid,
130
+ context.emulator.slot,
131
+ ),
132
+ );
133
+ }
134
+
135
+ async function runCreateAllShards(
136
+ pollId: bigint,
137
+ sysParams: SystemParams,
138
+ context: MyContext,
139
+ ): Promise<void> {
140
+ const govUtxo = await findGov(
141
+ context.lucid,
142
+ sysParams.validatorHashes.govHash,
143
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
144
+ );
145
+
146
+ for (
147
+ let i = 0;
148
+ i < Math.ceil(Number(govUtxo.datum.protocolParams.totalShards) / 2);
149
+ i++
150
+ ) {
151
+ const pollUtxo = await findPollManager(
152
+ context.lucid,
153
+ sysParams.validatorHashes.pollManagerHash,
154
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
155
+ pollId,
156
+ );
157
+
158
+ await runAndAwaitTx(
159
+ context.lucid,
160
+ createShardsChunks(
161
+ 2n,
162
+ pollUtxo.utxo,
163
+ sysParams,
164
+ context.emulator.slot,
165
+ context.lucid,
166
+ ),
167
+ );
168
+ }
169
+ }
170
+
171
+ async function runMergeAllShards(
172
+ pollId: bigint,
173
+ sysParams: SystemParams,
174
+ context: MyContext,
175
+ ): Promise<void> {
176
+ const govUtxo = await findGov(
177
+ context.lucid,
178
+ sysParams.validatorHashes.govHash,
179
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
180
+ );
181
+
182
+ for (
183
+ let i = 0;
184
+ i < Math.ceil(Number(govUtxo.datum.protocolParams.totalShards) / 2);
185
+ i++
186
+ ) {
187
+ const pollUtxo = await findPollManager(
188
+ context.lucid,
189
+ sysParams.validatorHashes.pollManagerHash,
190
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
191
+ pollId,
192
+ );
193
+
194
+ const allPollShards = await findAllPollShards(
195
+ context.lucid,
196
+ sysParams.validatorHashes.pollShardHash,
197
+ fromSystemParamsAsset(sysParams.pollShardParams.pollToken),
198
+ pollUtxo.datum.pollId,
199
+ );
200
+
201
+ await runAndAwaitTx(
202
+ context.lucid,
203
+ mergeShards(
204
+ pollUtxo.utxo,
205
+ A.takeLeft(2)(allPollShards).map((u) => u.utxo),
206
+ sysParams,
207
+ context.lucid,
208
+ context.emulator.slot,
209
+ ),
210
+ );
211
+ }
212
+ }
213
+
214
+ async function runEndProposal(
215
+ pollId: bigint,
216
+ sysParams: SystemParams,
217
+ context: MyContext,
218
+ ): Promise<void> {
219
+ const pollUtxo = await findPollManager(
220
+ context.lucid,
221
+ sysParams.validatorHashes.pollManagerHash,
222
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
223
+ pollId,
224
+ );
225
+
226
+ const govUtxo = await findGov(
227
+ context.lucid,
228
+ sysParams.validatorHashes.govHash,
229
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
230
+ );
231
+
232
+ await runAndAwaitTx(
233
+ context.lucid,
234
+ endProposal(
235
+ pollUtxo.utxo,
236
+ govUtxo.utxo,
237
+ sysParams,
238
+ context.lucid,
239
+ context.emulator.slot,
240
+ ),
241
+ );
242
+ }
243
+
244
+ async function waitForVotingEnd(
245
+ pollId: bigint,
246
+ sysParams: SystemParams,
247
+ context: MyContext,
248
+ ): Promise<void> {
249
+ const pollUtxo = await findPollManager(
250
+ context.lucid,
251
+ sysParams.validatorHashes.pollManagerHash,
252
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
253
+ pollId,
254
+ );
255
+
256
+ const targetSlot = unixTimeToSlot(
257
+ context.lucid.config().network!,
258
+ Number(pollUtxo.datum.votingEndTime),
259
+ );
260
+ expect(targetSlot).toBeGreaterThan(context.emulator.slot);
261
+
262
+ context.emulator.awaitSlot(targetSlot - context.emulator.slot + 1);
263
+ }
264
+
265
+ describe('Gov', () => {
266
+ beforeEach<MyContext>(async (context: MyContext) => {
267
+ context.users = {
268
+ admin: generateEmulatorAccount({
269
+ lovelace: BigInt(100_000_000_000_000),
270
+ }),
271
+ user: generateEmulatorAccount(addAssets(mkLovelacesOf(100_000_000n))),
272
+ withdrawalAccount: generateEmulatorAccount({}),
273
+ };
274
+
275
+ context.emulator = new Emulator([context.users.admin, context.users.user]);
276
+ context.lucid = await Lucid(context.emulator, 'Custom');
277
+ });
278
+
279
+ test<MyContext>('Create text proposal', async (context: MyContext) => {
280
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
281
+
282
+ const [sysParams, __] = await init(context.lucid, [iusdInitialAssetCfg]);
283
+
284
+ const govUtxo = await findGov(
285
+ context.lucid,
286
+ sysParams.validatorHashes.govHash,
287
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
288
+ );
289
+
290
+ const [tx, _] = await createProposal(
291
+ { TextProposal: { bytes: fromText('smth') } },
292
+ null,
293
+ sysParams,
294
+ context.lucid,
295
+ context.emulator.slot,
296
+ govUtxo.utxo,
297
+ [],
298
+ );
299
+
300
+ await runAndAwaitTxBuilder(context.lucid, tx);
301
+ });
302
+
303
+ test<MyContext>('Create text proposal with shards', async (context: MyContext) => {
304
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
305
+
306
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
307
+
308
+ const govUtxo = await findGov(
309
+ context.lucid,
310
+ sysParams.validatorHashes.govHash,
311
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
312
+ );
313
+
314
+ const [tx, pollId] = await createProposal(
315
+ { TextProposal: { bytes: fromText('smth') } },
316
+ null,
317
+ sysParams,
318
+ context.lucid,
319
+ context.emulator.slot,
320
+ govUtxo.utxo,
321
+ [],
322
+ );
323
+
324
+ await runAndAwaitTxBuilder(context.lucid, tx);
325
+
326
+ await runCreateAllShards(pollId, sysParams, context);
327
+
328
+ const pollUtxo = await findPollManager(
329
+ context.lucid,
330
+ sysParams.validatorHashes.pollManagerHash,
331
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
332
+ pollId,
333
+ );
334
+
335
+ expect(
336
+ pollUtxo.datum.createdShardsCount === pollUtxo.datum.totalShardsCount,
337
+ 'Expected total shards count being created',
338
+ ).toBeTruthy();
339
+ });
340
+
341
+ test<MyContext>('Merge proposal shards', async (context: MyContext) => {
342
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
343
+
344
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
345
+
346
+ const govUtxo = await findGov(
347
+ context.lucid,
348
+ sysParams.validatorHashes.govHash,
349
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
350
+ );
351
+
352
+ const [tx, pollId] = await createProposal(
353
+ { TextProposal: { bytes: fromText('smth') } },
354
+ null,
355
+ sysParams,
356
+ context.lucid,
357
+ context.emulator.slot,
358
+ govUtxo.utxo,
359
+ [],
360
+ );
361
+
362
+ await runAndAwaitTxBuilder(context.lucid, tx);
363
+
364
+ {
365
+ const pollUtxo = await findPollManager(
366
+ context.lucid,
367
+ sysParams.validatorHashes.pollManagerHash,
368
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
369
+ pollId,
370
+ );
371
+
372
+ await runAndAwaitTx(
373
+ context.lucid,
374
+ createShardsChunks(
375
+ 2n,
376
+ pollUtxo.utxo,
377
+ sysParams,
378
+ context.emulator.slot,
379
+ context.lucid,
380
+ ),
381
+ );
382
+
383
+ const targetSlot = unixTimeToSlot(
384
+ context.lucid.config().network!,
385
+ Number(pollUtxo.datum.votingEndTime),
386
+ );
387
+ expect(targetSlot).toBeGreaterThan(context.emulator.slot);
388
+
389
+ context.emulator.awaitSlot(targetSlot - context.emulator.slot + 1);
390
+ }
391
+
392
+ {
393
+ const pollUtxo = await findPollManager(
394
+ context.lucid,
395
+ sysParams.validatorHashes.pollManagerHash,
396
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
397
+ pollId,
398
+ );
399
+
400
+ const allPollShards = await findAllPollShards(
401
+ context.lucid,
402
+ sysParams.validatorHashes.pollShardHash,
403
+ fromSystemParamsAsset(sysParams.pollShardParams.pollToken),
404
+ pollUtxo.datum.pollId,
405
+ );
406
+
407
+ assert(allPollShards.length === 2);
408
+
409
+ await runAndAwaitTx(
410
+ context.lucid,
411
+ mergeShards(
412
+ pollUtxo.utxo,
413
+ allPollShards.map((u) => u.utxo),
414
+ sysParams,
415
+ context.lucid,
416
+ context.emulator.slot,
417
+ ),
418
+ );
419
+ }
420
+
421
+ const pollUtxo = await findPollManager(
422
+ context.lucid,
423
+ sysParams.validatorHashes.pollManagerHash,
424
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
425
+ pollId,
426
+ );
427
+
428
+ assert(pollUtxo.datum.talliedShardsCount === 2n);
429
+ });
430
+
431
+ test<MyContext>('Create asset proposal', async (context: MyContext) => {
432
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
433
+
434
+ const [sysParams, ___] = await init(context.lucid, [iusdInitialAssetCfg]);
435
+
436
+ const [pkh, _] = await addrDetails(context.lucid);
437
+
438
+ const [startInterestTx, interestOracleNft] = await startInterestOracle(
439
+ 0n,
440
+ 0n,
441
+ 0n,
442
+ {
443
+ biasTime: 120_000n,
444
+ owner: pkh.hash,
445
+ },
446
+ context.lucid,
447
+ );
448
+ await runAndAwaitTxBuilder(context.lucid, startInterestTx);
449
+
450
+ const [priceOracleTx, priceOranceNft] = await startPriceOracleTx(
451
+ context.lucid,
452
+ 'IBTC_ORACLE',
453
+ { getOnChainInt: 1_000_000n },
454
+ { biasTime: 120_000n, expiration: 1_800_000n, owner: pkh.hash },
455
+ );
456
+ await runAndAwaitTxBuilder(context.lucid, priceOracleTx);
457
+
458
+ const govUtxo = await findGov(
459
+ context.lucid,
460
+ sysParams.validatorHashes.govHash,
461
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
462
+ );
463
+
464
+ const allIassetOrefs = (
465
+ await findAllIAssets(
466
+ context.lucid,
467
+ sysParams.validatorHashes.cdpHash,
468
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
469
+ )
470
+ ).map((iasset) => iasset.utxo);
471
+
472
+ const [tx, __] = await createProposal(
473
+ {
474
+ ProposeAsset: {
475
+ asset: fromText('iBTC'),
476
+ priceOracleNft: priceOranceNft,
477
+ interestOracleNft: interestOracleNft,
478
+ redemptionRatioPercentage: { getOnChainInt: 200_000_000n },
479
+ maintenanceRatioPercentage: { getOnChainInt: 150_000_000n },
480
+ liquidationRatioPercentage: { getOnChainInt: 120_000_000n },
481
+ debtMintingFeePercentage: { getOnChainInt: 500_000n },
482
+ liquidationProcessingFeePercentage: { getOnChainInt: 2_000_000n },
483
+ stabilityPoolWithdrawalFeePercentage: { getOnChainInt: 500_000n },
484
+ redemptionReimbursementPercentage: { getOnChainInt: 1_000_000n },
485
+ redemptionProcessingFeePercentage: { getOnChainInt: 1_000_000n },
486
+ interestCollectorPortionPercentage: { getOnChainInt: 40_000_000n },
487
+ },
488
+ },
489
+ null,
490
+ sysParams,
491
+ context.lucid,
492
+ context.emulator.slot,
493
+ govUtxo.utxo,
494
+ allIassetOrefs,
495
+ );
496
+
497
+ await runAndAwaitTxBuilder(context.lucid, tx);
498
+ });
499
+
500
+ test<MyContext>('Vote on proposal', async (context: MyContext) => {
501
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
502
+
503
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
504
+
505
+ const govUtxo = await findGov(
506
+ context.lucid,
507
+ sysParams.validatorHashes.govHash,
508
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
509
+ );
510
+
511
+ const [tx, pollId] = await createProposal(
512
+ { TextProposal: { bytes: fromText('smth') } },
513
+ null,
514
+ sysParams,
515
+ context.lucid,
516
+ context.emulator.slot,
517
+ govUtxo.utxo,
518
+ [],
519
+ );
520
+
521
+ await runAndAwaitTxBuilder(context.lucid, tx);
522
+
523
+ await runCreateAllShards(pollId, sysParams, context);
524
+
525
+ await runAndAwaitTx(
526
+ context.lucid,
527
+ openStakingPosition(1_000_000n, sysParams, context.lucid),
528
+ );
529
+
530
+ await runVote(pollId, 'Yes', sysParams, context);
531
+ });
532
+
533
+ test<MyContext>('Vote on 2 proposals sequentially (lower pollID first)', async (context: MyContext) => {
534
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
535
+
536
+ const [sysParams, __] = await init(context.lucid, [iusdInitialAssetCfg]);
537
+
538
+ await runAndAwaitTx(
539
+ context.lucid,
540
+ openStakingPosition(1_000_000n, sysParams, context.lucid),
541
+ );
542
+ const [pkh, _] = await addrDetails(context.lucid);
543
+
544
+ // Create proposals
545
+ const createProposalsTask = F.pipe(
546
+ [fromText('proposal 1'), fromText('proposal 2')].map(
547
+ (txtContent): T.Task<bigint> => {
548
+ return async () => {
549
+ const govUtxo = await findGov(
550
+ context.lucid,
551
+ sysParams.validatorHashes.govHash,
552
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
553
+ );
554
+
555
+ const [tx, pollId] = await createProposal(
556
+ { TextProposal: { bytes: txtContent } },
557
+ null,
558
+ sysParams,
559
+ context.lucid,
560
+ context.emulator.slot,
561
+ govUtxo.utxo,
562
+ [],
563
+ );
564
+
565
+ await runAndAwaitTxBuilder(context.lucid, tx);
566
+
567
+ await runCreateAllShards(pollId, sysParams, context);
568
+
569
+ return pollId;
570
+ };
571
+ },
572
+ ),
573
+ T.sequenceSeqArray,
574
+ );
575
+
576
+ const pollIds = await createProposalsTask();
577
+
578
+ // vote on each proposal
579
+ const voteEachProposalTask = F.pipe(
580
+ pollIds.map(
581
+ (pollId): T.Task<void> =>
582
+ async () => {
583
+ await runVote(
584
+ pollId,
585
+ Number(pollId) % 2 == 0 ? 'Yes' : 'No',
586
+ sysParams,
587
+ context,
588
+ );
589
+ },
590
+ ),
591
+ T.sequenceSeqArray,
592
+ );
593
+
594
+ await voteEachProposalTask();
595
+
596
+ const stakingPosUtxo = await findStakingPosition(
597
+ context.lucid,
598
+ sysParams.validatorHashes.stakingHash,
599
+ fromSystemParamsAsset(sysParams.stakingParams.stakingToken),
600
+ pkh.hash,
601
+ );
602
+
603
+ expect([...stakingPosUtxo.datum.lockedAmount.keys()]).toEqual([1n, 2n]);
604
+ });
605
+
606
+ test<MyContext>('Vote on 2 proposals in reverse (higher pollID first), both yes and no votes', async (context: MyContext) => {
607
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
608
+
609
+ const [sysParams, __] = await init(context.lucid, [iusdInitialAssetCfg]);
610
+
611
+ await runAndAwaitTx(
612
+ context.lucid,
613
+ openStakingPosition(1_000_000n, sysParams, context.lucid),
614
+ );
615
+ const [pkh, _] = await addrDetails(context.lucid);
616
+
617
+ // Create proposals
618
+ const createProposalsTask = F.pipe(
619
+ [fromText('proposal 1'), fromText('proposal 2')].map(
620
+ (txtContent): T.Task<bigint> => {
621
+ return async () => {
622
+ const govUtxo = await findGov(
623
+ context.lucid,
624
+ sysParams.validatorHashes.govHash,
625
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
626
+ );
627
+
628
+ const [tx, pollId] = await createProposal(
629
+ { TextProposal: { bytes: txtContent } },
630
+ null,
631
+ sysParams,
632
+ context.lucid,
633
+ context.emulator.slot,
634
+ govUtxo.utxo,
635
+ [],
636
+ );
637
+
638
+ await runAndAwaitTxBuilder(context.lucid, tx);
639
+
640
+ await runCreateAllShards(pollId, sysParams, context);
641
+
642
+ return pollId;
643
+ };
644
+ },
645
+ ),
646
+ T.sequenceSeqArray,
647
+ );
648
+
649
+ const pollIdsDescending = F.pipe(
650
+ await createProposalsTask(),
651
+ RA.toArray, // Sort it from high to low
652
+ A.map(Number),
653
+ A.sort(N.Ord),
654
+ A.map(BigInt),
655
+ A.reverse,
656
+ );
657
+
658
+ // vote on each proposal
659
+ const voteEachProposalTask = F.pipe(
660
+ pollIdsDescending.map(
661
+ (pollId): T.Task<void> =>
662
+ async () => {
663
+ await runVote(
664
+ pollId,
665
+ Number(pollId) % 2 == 0 ? 'Yes' : 'No',
666
+ sysParams,
667
+ context,
668
+ );
669
+ },
670
+ ),
671
+ T.sequenceSeqArray,
672
+ );
673
+
674
+ await voteEachProposalTask();
675
+
676
+ const stakingPosUtxo = await findStakingPosition(
677
+ context.lucid,
678
+ sysParams.validatorHashes.stakingHash,
679
+ fromSystemParamsAsset(sysParams.stakingParams.stakingToken),
680
+ pkh.hash,
681
+ );
682
+
683
+ expect([...stakingPosUtxo.datum.lockedAmount.keys()]).toEqual([2n, 1n]);
684
+ });
685
+
686
+ test<MyContext>('End passed proposal', async (context: MyContext) => {
687
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
688
+
689
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
690
+
691
+ const [tx, pollId] = await createProposal(
692
+ { TextProposal: { bytes: fromText('smth') } },
693
+ null,
694
+ sysParams,
695
+ context.lucid,
696
+ context.emulator.slot,
697
+ (
698
+ await findGov(
699
+ context.lucid,
700
+ sysParams.validatorHashes.govHash,
701
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
702
+ )
703
+ ).utxo,
704
+ [],
705
+ );
706
+
707
+ await runAndAwaitTxBuilder(context.lucid, tx);
708
+
709
+ await runCreateAllShards(pollId, sysParams, context);
710
+
711
+ await runAndAwaitTx(
712
+ context.lucid,
713
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
714
+ );
715
+
716
+ await runVote(pollId, 'Yes', sysParams, context);
717
+
718
+ await waitForVotingEnd(pollId, sysParams, context);
719
+
720
+ await runMergeAllShards(pollId, sysParams, context);
721
+
722
+ await runEndProposal(pollId, sysParams, context);
723
+
724
+ await expect(
725
+ findExecute(
726
+ context.lucid,
727
+ sysParams.validatorHashes.executeHash,
728
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
729
+ pollId,
730
+ ),
731
+ ).resolves.toBeDefined();
732
+ });
733
+
734
+ test<MyContext>('End failed proposal', async (context: MyContext) => {
735
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
736
+
737
+ const [sysParams, __] = await init(context.lucid, [iusdInitialAssetCfg]);
738
+
739
+ const [tx, pollId] = await createProposal(
740
+ { TextProposal: { bytes: fromText('smth') } },
741
+ null,
742
+ sysParams,
743
+ context.lucid,
744
+ context.emulator.slot,
745
+ (
746
+ await findGov(
747
+ context.lucid,
748
+ sysParams.validatorHashes.govHash,
749
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
750
+ )
751
+ ).utxo,
752
+ [],
753
+ );
754
+
755
+ await runAndAwaitTxBuilder(context.lucid, tx);
756
+
757
+ await runCreateAllShards(pollId, sysParams, context);
758
+
759
+ await runAndAwaitTx(
760
+ context.lucid,
761
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
762
+ );
763
+
764
+ await runVote(pollId, 'No', sysParams, context);
765
+
766
+ {
767
+ const pollUtxo = await findPollManager(
768
+ context.lucid,
769
+ sysParams.validatorHashes.pollManagerHash,
770
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
771
+ pollId,
772
+ );
773
+
774
+ const targetSlot = unixTimeToSlot(
775
+ context.lucid.config().network!,
776
+ Number(pollUtxo.datum.votingEndTime),
777
+ );
778
+ expect(targetSlot).toBeGreaterThan(context.emulator.slot);
779
+
780
+ context.emulator.awaitSlot(targetSlot - context.emulator.slot + 1);
781
+ }
782
+
783
+ await runMergeAllShards(pollId, sysParams, context);
784
+
785
+ const pollUtxo = await findPollManager(
786
+ context.lucid,
787
+ sysParams.validatorHashes.pollManagerHash,
788
+ fromSystemParamsAsset(sysParams.pollManagerParams.pollToken),
789
+ pollId,
790
+ );
791
+
792
+ const govUtxo = await findGov(
793
+ context.lucid,
794
+ sysParams.validatorHashes.govHash,
795
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
796
+ );
797
+
798
+ const [_, newUtxos] = await getNewUtxosAtAddressAfterAction(
799
+ context.lucid,
800
+ createScriptAddress(
801
+ context.lucid.config().network!,
802
+ sysParams.validatorHashes.treasuryHash,
803
+ ),
804
+ () =>
805
+ runAndAwaitTx(
806
+ context.lucid,
807
+ endProposal(
808
+ pollUtxo.utxo,
809
+ govUtxo.utxo,
810
+ sysParams,
811
+ context.lucid,
812
+ context.emulator.slot,
813
+ ),
814
+ ),
815
+ );
816
+
817
+ const treasuryOutput = matchSingle(
818
+ newUtxos,
819
+ () => new Error('Expected single treasury output'),
820
+ );
821
+
822
+ assert(
823
+ assetClassValueOf(
824
+ treasuryOutput.assets,
825
+ fromSystemParamsAsset(sysParams.govParams.indyAsset),
826
+ ) === govUtxo.datum.protocolParams.proposalDeposit,
827
+ 'Treasury should get proposal deposit back on failed proposal end',
828
+ );
829
+
830
+ await expect(
831
+ findExecute(
832
+ context.lucid,
833
+ sysParams.validatorHashes.executeHash,
834
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
835
+ pollId,
836
+ ),
837
+ ).rejects.toThrow();
838
+ });
839
+
840
+ test<MyContext>('Execute text proposal', async (context: MyContext) => {
841
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
842
+
843
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
844
+
845
+ const [tx, pollId] = await createProposal(
846
+ { TextProposal: { bytes: fromText('smth') } },
847
+ null,
848
+ sysParams,
849
+ context.lucid,
850
+ context.emulator.slot,
851
+ (
852
+ await findGov(
853
+ context.lucid,
854
+ sysParams.validatorHashes.govHash,
855
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
856
+ )
857
+ ).utxo,
858
+ [],
859
+ );
860
+
861
+ await runAndAwaitTxBuilder(context.lucid, tx);
862
+
863
+ await runCreateAllShards(pollId, sysParams, context);
864
+
865
+ await runAndAwaitTx(
866
+ context.lucid,
867
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
868
+ );
869
+
870
+ await runVote(pollId, 'Yes', sysParams, context);
871
+
872
+ await waitForVotingEnd(pollId, sysParams, context);
873
+
874
+ await runMergeAllShards(pollId, sysParams, context);
875
+
876
+ await runEndProposal(pollId, sysParams, context);
877
+
878
+ const govUtxo = await findGov(
879
+ context.lucid,
880
+ sysParams.validatorHashes.govHash,
881
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
882
+ );
883
+ const executeUtxo = await findExecute(
884
+ context.lucid,
885
+ sysParams.validatorHashes.executeHash,
886
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
887
+ pollId,
888
+ );
889
+
890
+ await runAndAwaitTx(
891
+ context.lucid,
892
+ executeProposal(
893
+ executeUtxo.utxo,
894
+ govUtxo.utxo,
895
+ null,
896
+ null,
897
+ null,
898
+ sysParams,
899
+ context.lucid,
900
+ context.emulator.slot,
901
+ ),
902
+ );
903
+ });
904
+
905
+ test<MyContext>('Execute text proposal with treasury withdrawal', async (context: MyContext) => {
906
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
907
+
908
+ const [sysParams, __] = await init(context.lucid, [iusdInitialAssetCfg]);
909
+
910
+ const withdrawalIndyAmt = 1_000n;
911
+ const treasuryWithdrawalUtxo = await createUtxoAtTreasury(
912
+ withdrawalIndyAmt,
913
+ sysParams,
914
+ context,
915
+ );
916
+
917
+ const [tx, pollId] = await createProposal(
918
+ { TextProposal: { bytes: fromText('smth') } },
919
+ {
920
+ destination: addressFromBech32(context.users.withdrawalAccount.address),
921
+ value: [
922
+ [
923
+ sysParams.govParams.indyAsset[0].unCurrencySymbol,
924
+ fromText(sysParams.govParams.indyAsset[1].unTokenName),
925
+ withdrawalIndyAmt,
926
+ ],
927
+ ],
928
+ },
929
+ sysParams,
930
+ context.lucid,
931
+ context.emulator.slot,
932
+ (
933
+ await findGov(
934
+ context.lucid,
935
+ sysParams.validatorHashes.govHash,
936
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
937
+ )
938
+ ).utxo,
939
+ [],
940
+ );
941
+
942
+ await runAndAwaitTxBuilder(context.lucid, tx);
943
+
944
+ await runCreateAllShards(pollId, sysParams, context);
945
+
946
+ await runAndAwaitTx(
947
+ context.lucid,
948
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
949
+ );
950
+
951
+ await runVote(pollId, 'Yes', sysParams, context);
952
+
953
+ await waitForVotingEnd(pollId, sysParams, context);
954
+
955
+ await runMergeAllShards(pollId, sysParams, context);
956
+
957
+ await runEndProposal(pollId, sysParams, context);
958
+
959
+ const govUtxo = await findGov(
960
+ context.lucid,
961
+ sysParams.validatorHashes.govHash,
962
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
963
+ );
964
+ const executeUtxo = await findExecute(
965
+ context.lucid,
966
+ sysParams.validatorHashes.executeHash,
967
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
968
+ pollId,
969
+ );
970
+
971
+ const [_, newVal] = await getValueChangeAtAddressAfterAction(
972
+ context.lucid,
973
+ context.users.withdrawalAccount.address,
974
+ async () =>
975
+ await runAndAwaitTx(
976
+ context.lucid,
977
+ executeProposal(
978
+ executeUtxo.utxo,
979
+ govUtxo.utxo,
980
+ treasuryWithdrawalUtxo,
981
+ null,
982
+ null,
983
+ sysParams,
984
+ context.lucid,
985
+ context.emulator.slot,
986
+ ),
987
+ ),
988
+ );
989
+
990
+ expect(
991
+ assetClassValueOf(
992
+ newVal,
993
+ fromSystemParamsAsset(sysParams.govParams.indyAsset),
994
+ ) === withdrawalIndyAmt,
995
+ 'Unexpected withdrawn indy amt',
996
+ ).toBeTruthy();
997
+ });
998
+
999
+ test<MyContext>('Execute create asset proposal', async (context: MyContext) => {
1000
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1001
+
1002
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1003
+
1004
+ const [pkh, __] = await addrDetails(context.lucid);
1005
+
1006
+ const [startInterestTx, interestOracleNft] = await startInterestOracle(
1007
+ 0n,
1008
+ 0n,
1009
+ 0n,
1010
+ {
1011
+ biasTime: 120_000n,
1012
+ owner: pkh.hash,
1013
+ },
1014
+ context.lucid,
1015
+ );
1016
+ await runAndAwaitTxBuilder(context.lucid, startInterestTx);
1017
+
1018
+ const [priceOracleTx, priceOranceNft] = await startPriceOracleTx(
1019
+ context.lucid,
1020
+ 'IBTC_ORACLE',
1021
+ { getOnChainInt: 1_000_000n },
1022
+ { biasTime: 120_000n, expiration: 1_800_000n, owner: pkh.hash },
1023
+ );
1024
+ await runAndAwaitTxBuilder(context.lucid, priceOracleTx);
1025
+
1026
+ const govUtxo = await findGov(
1027
+ context.lucid,
1028
+ sysParams.validatorHashes.govHash,
1029
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1030
+ );
1031
+
1032
+ const [tx, pollId] = await createProposal(
1033
+ {
1034
+ ProposeAsset: {
1035
+ asset: fromText('iBTC'),
1036
+ priceOracleNft: priceOranceNft,
1037
+ interestOracleNft: interestOracleNft,
1038
+ redemptionRatioPercentage: { getOnChainInt: 200_000_000n },
1039
+ maintenanceRatioPercentage: { getOnChainInt: 150_000_000n },
1040
+ liquidationRatioPercentage: { getOnChainInt: 120_000_000n },
1041
+ debtMintingFeePercentage: { getOnChainInt: 500_000n },
1042
+ liquidationProcessingFeePercentage: { getOnChainInt: 2_000_000n },
1043
+ stabilityPoolWithdrawalFeePercentage: { getOnChainInt: 500_000n },
1044
+ redemptionReimbursementPercentage: { getOnChainInt: 1_000_000n },
1045
+ redemptionProcessingFeePercentage: { getOnChainInt: 1_000_000n },
1046
+ interestCollectorPortionPercentage: { getOnChainInt: 40_000_000n },
1047
+ },
1048
+ },
1049
+ null,
1050
+ sysParams,
1051
+ context.lucid,
1052
+ context.emulator.slot,
1053
+ govUtxo.utxo,
1054
+ (
1055
+ await findAllIAssets(
1056
+ context.lucid,
1057
+ sysParams.validatorHashes.cdpHash,
1058
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
1059
+ )
1060
+ ).map((iasset) => iasset.utxo),
1061
+ );
1062
+
1063
+ await runAndAwaitTxBuilder(context.lucid, tx);
1064
+
1065
+ await runCreateAllShards(pollId, sysParams, context);
1066
+
1067
+ await runAndAwaitTx(
1068
+ context.lucid,
1069
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1070
+ );
1071
+
1072
+ await runVote(pollId, 'Yes', sysParams, context);
1073
+
1074
+ await waitForVotingEnd(pollId, sysParams, context);
1075
+
1076
+ await runMergeAllShards(pollId, sysParams, context);
1077
+
1078
+ await runEndProposal(pollId, sysParams, context);
1079
+
1080
+ const executeUtxo = await findExecute(
1081
+ context.lucid,
1082
+ sysParams.validatorHashes.executeHash,
1083
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1084
+ pollId,
1085
+ );
1086
+
1087
+ await runAndAwaitTx(
1088
+ context.lucid,
1089
+ executeProposal(
1090
+ executeUtxo.utxo,
1091
+ (
1092
+ await findGov(
1093
+ context.lucid,
1094
+ sysParams.validatorHashes.govHash,
1095
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1096
+ )
1097
+ ).utxo,
1098
+ null,
1099
+ (
1100
+ await findAllIAssets(
1101
+ context.lucid,
1102
+ sysParams.validatorHashes.cdpHash,
1103
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
1104
+ )
1105
+ ).map((iasset) => iasset.utxo),
1106
+ null,
1107
+ sysParams,
1108
+ context.lucid,
1109
+ context.emulator.slot,
1110
+ ),
1111
+ );
1112
+ });
1113
+
1114
+ test<MyContext>('Execute create asset proposal with treasury withdrawal', async (context: MyContext) => {
1115
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1116
+
1117
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1118
+
1119
+ const [pkh, ___] = await addrDetails(context.lucid);
1120
+
1121
+ const withdrawalIndyAmt = 1_000n;
1122
+ const treasuryWithdrawalUtxo = await createUtxoAtTreasury(
1123
+ withdrawalIndyAmt,
1124
+ sysParams,
1125
+ context,
1126
+ );
1127
+
1128
+ const [startInterestTx, interestOracleNft] = await startInterestOracle(
1129
+ 0n,
1130
+ 0n,
1131
+ 0n,
1132
+ {
1133
+ biasTime: 120_000n,
1134
+ owner: pkh.hash,
1135
+ },
1136
+ context.lucid,
1137
+ );
1138
+ await runAndAwaitTxBuilder(context.lucid, startInterestTx);
1139
+
1140
+ const [priceOracleTx, priceOranceNft] = await startPriceOracleTx(
1141
+ context.lucid,
1142
+ 'IBTC_ORACLE',
1143
+ { getOnChainInt: 1_000_000n },
1144
+ { biasTime: 120_000n, expiration: 1_800_000n, owner: pkh.hash },
1145
+ );
1146
+ await runAndAwaitTxBuilder(context.lucid, priceOracleTx);
1147
+
1148
+ const govUtxo = await findGov(
1149
+ context.lucid,
1150
+ sysParams.validatorHashes.govHash,
1151
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1152
+ );
1153
+
1154
+ const [tx, pollId] = await createProposal(
1155
+ {
1156
+ ProposeAsset: {
1157
+ asset: fromText('iBTC'),
1158
+ priceOracleNft: priceOranceNft,
1159
+ interestOracleNft: interestOracleNft,
1160
+ redemptionRatioPercentage: { getOnChainInt: 200_000_000n },
1161
+ maintenanceRatioPercentage: { getOnChainInt: 150_000_000n },
1162
+ liquidationRatioPercentage: { getOnChainInt: 120_000_000n },
1163
+ debtMintingFeePercentage: { getOnChainInt: 500_000n },
1164
+ liquidationProcessingFeePercentage: { getOnChainInt: 2_000_000n },
1165
+ stabilityPoolWithdrawalFeePercentage: { getOnChainInt: 500_000n },
1166
+ redemptionReimbursementPercentage: { getOnChainInt: 1_000_000n },
1167
+ redemptionProcessingFeePercentage: { getOnChainInt: 1_000_000n },
1168
+ interestCollectorPortionPercentage: { getOnChainInt: 40_000_000n },
1169
+ },
1170
+ },
1171
+ {
1172
+ destination: addressFromBech32(context.users.withdrawalAccount.address),
1173
+ value: [
1174
+ [
1175
+ sysParams.govParams.indyAsset[0].unCurrencySymbol,
1176
+ fromText(sysParams.govParams.indyAsset[1].unTokenName),
1177
+ withdrawalIndyAmt,
1178
+ ],
1179
+ ],
1180
+ },
1181
+ sysParams,
1182
+ context.lucid,
1183
+ context.emulator.slot,
1184
+ govUtxo.utxo,
1185
+ (
1186
+ await findAllIAssets(
1187
+ context.lucid,
1188
+ sysParams.validatorHashes.cdpHash,
1189
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
1190
+ )
1191
+ ).map((iasset) => iasset.utxo),
1192
+ );
1193
+
1194
+ await runAndAwaitTxBuilder(context.lucid, tx);
1195
+
1196
+ await runCreateAllShards(pollId, sysParams, context);
1197
+
1198
+ await runAndAwaitTx(
1199
+ context.lucid,
1200
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1201
+ );
1202
+
1203
+ await runVote(pollId, 'Yes', sysParams, context);
1204
+
1205
+ await waitForVotingEnd(pollId, sysParams, context);
1206
+
1207
+ await runMergeAllShards(pollId, sysParams, context);
1208
+
1209
+ await runEndProposal(pollId, sysParams, context);
1210
+
1211
+ const executeUtxo = await findExecute(
1212
+ context.lucid,
1213
+ sysParams.validatorHashes.executeHash,
1214
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1215
+ pollId,
1216
+ );
1217
+
1218
+ const [__, newVal] = await getValueChangeAtAddressAfterAction(
1219
+ context.lucid,
1220
+ context.users.withdrawalAccount.address,
1221
+ async () =>
1222
+ runAndAwaitTx(
1223
+ context.lucid,
1224
+ executeProposal(
1225
+ executeUtxo.utxo,
1226
+ (
1227
+ await findGov(
1228
+ context.lucid,
1229
+ sysParams.validatorHashes.govHash,
1230
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1231
+ )
1232
+ ).utxo,
1233
+ treasuryWithdrawalUtxo,
1234
+ (
1235
+ await findAllIAssets(
1236
+ context.lucid,
1237
+ sysParams.validatorHashes.cdpHash,
1238
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
1239
+ )
1240
+ ).map((iasset) => iasset.utxo),
1241
+ null,
1242
+ sysParams,
1243
+ context.lucid,
1244
+ context.emulator.slot,
1245
+ ),
1246
+ ),
1247
+ );
1248
+
1249
+ expect(
1250
+ assetClassValueOf(
1251
+ newVal,
1252
+ fromSystemParamsAsset(sysParams.govParams.indyAsset),
1253
+ ) === withdrawalIndyAmt,
1254
+ 'Unexpected withdrawn indy amt',
1255
+ ).toBeTruthy();
1256
+ });
1257
+
1258
+ test<MyContext>('Execute modify asset proposal', async (context: MyContext) => {
1259
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1260
+
1261
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1262
+
1263
+ const govUtxo = await findGov(
1264
+ context.lucid,
1265
+ sysParams.validatorHashes.govHash,
1266
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1267
+ );
1268
+
1269
+ const iassetToModify = await findIAsset(
1270
+ context.lucid,
1271
+ sysParams.validatorHashes.cdpHash,
1272
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
1273
+ 'iUSD',
1274
+ );
1275
+
1276
+ const [tx, pollId] = await createProposal(
1277
+ {
1278
+ ModifyAsset: {
1279
+ asset: fromText('iUSD'),
1280
+ newAssetPriceInfo: iassetToModify.datum.price,
1281
+ newInterestOracleNft: iassetToModify.datum.interestOracleNft,
1282
+ newRedemptionRatioPercentage: iassetToModify.datum.redemptionRatio,
1283
+ newMaintenanceRatioPercentage: iassetToModify.datum.maintenanceRatio,
1284
+ newLiquidationRatioPercentage: iassetToModify.datum.liquidationRatio,
1285
+ newDebtMintingFeePercentage:
1286
+ iassetToModify.datum.debtMintingFeePercentage,
1287
+ newLiquidationProcessingFeePercentage:
1288
+ iassetToModify.datum.liquidationProcessingFeePercentage,
1289
+ newStabilityPoolWithdrawalFeePercentage:
1290
+ iassetToModify.datum.stabilityPoolWithdrawalFeePercentage,
1291
+ newRedemptionReimbursementPercentage:
1292
+ iassetToModify.datum.redemptionReimbursementPercentage,
1293
+ newRedemptionProcessingFeePercentage:
1294
+ iassetToModify.datum.redemptionProcessingFeePercentage,
1295
+ newInterestCollectorPortionPercentage:
1296
+ iassetToModify.datum.interestCollectorPortionPercentage,
1297
+ },
1298
+ },
1299
+ null,
1300
+ sysParams,
1301
+ context.lucid,
1302
+ context.emulator.slot,
1303
+ govUtxo.utxo,
1304
+ [],
1305
+ );
1306
+
1307
+ await runAndAwaitTxBuilder(context.lucid, tx);
1308
+
1309
+ await runCreateAllShards(pollId, sysParams, context);
1310
+
1311
+ await runAndAwaitTx(
1312
+ context.lucid,
1313
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1314
+ );
1315
+
1316
+ await runVote(pollId, 'Yes', sysParams, context);
1317
+
1318
+ await waitForVotingEnd(pollId, sysParams, context);
1319
+
1320
+ await runMergeAllShards(pollId, sysParams, context);
1321
+
1322
+ await runEndProposal(pollId, sysParams, context);
1323
+
1324
+ const executeUtxo = await findExecute(
1325
+ context.lucid,
1326
+ sysParams.validatorHashes.executeHash,
1327
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1328
+ pollId,
1329
+ );
1330
+
1331
+ await runAndAwaitTx(
1332
+ context.lucid,
1333
+ executeProposal(
1334
+ executeUtxo.utxo,
1335
+ (
1336
+ await findGov(
1337
+ context.lucid,
1338
+ sysParams.validatorHashes.govHash,
1339
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1340
+ )
1341
+ ).utxo,
1342
+ null,
1343
+ null,
1344
+ iassetToModify.utxo,
1345
+ sysParams,
1346
+ context.lucid,
1347
+ context.emulator.slot,
1348
+ ),
1349
+ );
1350
+ });
1351
+
1352
+ test<MyContext>('Execute modify asset proposal with treasury withdrawal', async (context: MyContext) => {
1353
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1354
+
1355
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1356
+
1357
+ const withdrawalIndyAmt = 1_000n;
1358
+ const treasuryWithdrawalUtxo = await createUtxoAtTreasury(
1359
+ withdrawalIndyAmt,
1360
+ sysParams,
1361
+ context,
1362
+ );
1363
+
1364
+ const govUtxo = await findGov(
1365
+ context.lucid,
1366
+ sysParams.validatorHashes.govHash,
1367
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1368
+ );
1369
+
1370
+ const iassetToModify = await findIAsset(
1371
+ context.lucid,
1372
+ sysParams.validatorHashes.cdpHash,
1373
+ fromSystemParamsAsset(sysParams.cdpParams.iAssetAuthToken),
1374
+ 'iUSD',
1375
+ );
1376
+
1377
+ const [tx, pollId] = await createProposal(
1378
+ {
1379
+ ModifyAsset: {
1380
+ asset: fromText('iUSD'),
1381
+ newAssetPriceInfo: iassetToModify.datum.price,
1382
+ newInterestOracleNft: iassetToModify.datum.interestOracleNft,
1383
+ newRedemptionRatioPercentage: iassetToModify.datum.redemptionRatio,
1384
+ newMaintenanceRatioPercentage: iassetToModify.datum.maintenanceRatio,
1385
+ newLiquidationRatioPercentage: iassetToModify.datum.liquidationRatio,
1386
+ newDebtMintingFeePercentage:
1387
+ iassetToModify.datum.debtMintingFeePercentage,
1388
+ newLiquidationProcessingFeePercentage:
1389
+ iassetToModify.datum.liquidationProcessingFeePercentage,
1390
+ newStabilityPoolWithdrawalFeePercentage:
1391
+ iassetToModify.datum.stabilityPoolWithdrawalFeePercentage,
1392
+ newRedemptionReimbursementPercentage:
1393
+ iassetToModify.datum.redemptionReimbursementPercentage,
1394
+ newRedemptionProcessingFeePercentage:
1395
+ iassetToModify.datum.redemptionProcessingFeePercentage,
1396
+ newInterestCollectorPortionPercentage:
1397
+ iassetToModify.datum.interestCollectorPortionPercentage,
1398
+ },
1399
+ },
1400
+ {
1401
+ destination: addressFromBech32(context.users.withdrawalAccount.address),
1402
+ value: [
1403
+ [
1404
+ sysParams.govParams.indyAsset[0].unCurrencySymbol,
1405
+ fromText(sysParams.govParams.indyAsset[1].unTokenName),
1406
+ withdrawalIndyAmt,
1407
+ ],
1408
+ ],
1409
+ },
1410
+ sysParams,
1411
+ context.lucid,
1412
+ context.emulator.slot,
1413
+ govUtxo.utxo,
1414
+ [],
1415
+ );
1416
+
1417
+ await runAndAwaitTxBuilder(context.lucid, tx);
1418
+
1419
+ await runCreateAllShards(pollId, sysParams, context);
1420
+
1421
+ await runAndAwaitTx(
1422
+ context.lucid,
1423
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1424
+ );
1425
+
1426
+ await runVote(pollId, 'Yes', sysParams, context);
1427
+
1428
+ await waitForVotingEnd(pollId, sysParams, context);
1429
+
1430
+ await runMergeAllShards(pollId, sysParams, context);
1431
+
1432
+ await runEndProposal(pollId, sysParams, context);
1433
+
1434
+ const executeUtxo = await findExecute(
1435
+ context.lucid,
1436
+ sysParams.validatorHashes.executeHash,
1437
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1438
+ pollId,
1439
+ );
1440
+
1441
+ const [__, newVal] = await getValueChangeAtAddressAfterAction(
1442
+ context.lucid,
1443
+ context.users.withdrawalAccount.address,
1444
+ async () =>
1445
+ runAndAwaitTx(
1446
+ context.lucid,
1447
+ executeProposal(
1448
+ executeUtxo.utxo,
1449
+ (
1450
+ await findGov(
1451
+ context.lucid,
1452
+ sysParams.validatorHashes.govHash,
1453
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1454
+ )
1455
+ ).utxo,
1456
+ treasuryWithdrawalUtxo,
1457
+ null,
1458
+ iassetToModify.utxo,
1459
+ sysParams,
1460
+ context.lucid,
1461
+ context.emulator.slot,
1462
+ ),
1463
+ ),
1464
+ );
1465
+
1466
+ expect(
1467
+ assetClassValueOf(
1468
+ newVal,
1469
+ fromSystemParamsAsset(sysParams.govParams.indyAsset),
1470
+ ) === withdrawalIndyAmt,
1471
+ 'Unexpected withdrawn indy amt',
1472
+ ).toBeTruthy();
1473
+ });
1474
+
1475
+ test<MyContext>('Execute modify protocol params proposal', async (context: MyContext) => {
1476
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1477
+
1478
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1479
+
1480
+ const govUtxo = await findGov(
1481
+ context.lucid,
1482
+ sysParams.validatorHashes.govHash,
1483
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1484
+ );
1485
+
1486
+ const [tx, pollId] = await createProposal(
1487
+ {
1488
+ ModifyProtocolParams: {
1489
+ newParams: {
1490
+ proposalDeposit: govUtxo.datum.protocolParams.proposalDeposit * 2n,
1491
+ votingPeriod: ONE_DAY * 2n,
1492
+ effectiveDelay: govUtxo.datum.protocolParams.effectiveDelay,
1493
+ expirationPeriod: ONE_DAY * 2n,
1494
+ collateralFeePercentage:
1495
+ govUtxo.datum.protocolParams.collateralFeePercentage,
1496
+ proposingPeriod: ONE_DAY,
1497
+ /// Total numer of shards used for voting.
1498
+ totalShards: govUtxo.datum.protocolParams.totalShards,
1499
+ /// The minimum number of votes (yes + no votes) for a proposal to be possible to pass.
1500
+ minimumQuorum: govUtxo.datum.protocolParams.minimumQuorum,
1501
+ /// Maximum amount of lovelaces that can be spent at once from the treasury.
1502
+ maxTreasuryLovelaceSpend:
1503
+ govUtxo.datum.protocolParams.maxTreasuryLovelaceSpend,
1504
+ /// Maximum amount of INDY that can be spent at once from the treasury.
1505
+ maxTreasuryIndySpend:
1506
+ govUtxo.datum.protocolParams.maxTreasuryIndySpend,
1507
+ },
1508
+ },
1509
+ },
1510
+ null,
1511
+ sysParams,
1512
+ context.lucid,
1513
+ context.emulator.slot,
1514
+ govUtxo.utxo,
1515
+ [],
1516
+ );
1517
+
1518
+ await runAndAwaitTxBuilder(context.lucid, tx);
1519
+
1520
+ await runCreateAllShards(pollId, sysParams, context);
1521
+
1522
+ await runAndAwaitTx(
1523
+ context.lucid,
1524
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1525
+ );
1526
+
1527
+ await runVote(pollId, 'Yes', sysParams, context);
1528
+
1529
+ await waitForVotingEnd(pollId, sysParams, context);
1530
+
1531
+ await runMergeAllShards(pollId, sysParams, context);
1532
+
1533
+ await runEndProposal(pollId, sysParams, context);
1534
+
1535
+ const executeUtxo = await findExecute(
1536
+ context.lucid,
1537
+ sysParams.validatorHashes.executeHash,
1538
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1539
+ pollId,
1540
+ );
1541
+
1542
+ await runAndAwaitTx(
1543
+ context.lucid,
1544
+ executeProposal(
1545
+ executeUtxo.utxo,
1546
+ (
1547
+ await findGov(
1548
+ context.lucid,
1549
+ sysParams.validatorHashes.govHash,
1550
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1551
+ )
1552
+ ).utxo,
1553
+ null,
1554
+ null,
1555
+ null,
1556
+ sysParams,
1557
+ context.lucid,
1558
+ context.emulator.slot,
1559
+ ),
1560
+ );
1561
+ });
1562
+
1563
+ test<MyContext>('Execute modify protocol params proposal with treasury withdrawal', async (context: MyContext) => {
1564
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1565
+
1566
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1567
+
1568
+ const withdrawalIndyAmt = 1_000n;
1569
+ const treasuryWithdrawalUtxo = await createUtxoAtTreasury(
1570
+ withdrawalIndyAmt,
1571
+ sysParams,
1572
+ context,
1573
+ );
1574
+
1575
+ const govUtxo = await findGov(
1576
+ context.lucid,
1577
+ sysParams.validatorHashes.govHash,
1578
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1579
+ );
1580
+
1581
+ const [tx, pollId] = await createProposal(
1582
+ {
1583
+ ModifyProtocolParams: {
1584
+ newParams: {
1585
+ proposalDeposit: govUtxo.datum.protocolParams.proposalDeposit * 2n,
1586
+ votingPeriod: ONE_DAY * 2n,
1587
+ effectiveDelay: govUtxo.datum.protocolParams.effectiveDelay,
1588
+ expirationPeriod: ONE_DAY * 2n,
1589
+ collateralFeePercentage:
1590
+ govUtxo.datum.protocolParams.collateralFeePercentage,
1591
+ proposingPeriod: ONE_DAY,
1592
+ /// Total numer of shards used for voting.
1593
+ totalShards: govUtxo.datum.protocolParams.totalShards,
1594
+ /// The minimum number of votes (yes + no votes) for a proposal to be possible to pass.
1595
+ minimumQuorum: govUtxo.datum.protocolParams.minimumQuorum,
1596
+ /// Maximum amount of lovelaces that can be spent at once from the treasury.
1597
+ maxTreasuryLovelaceSpend:
1598
+ govUtxo.datum.protocolParams.maxTreasuryLovelaceSpend,
1599
+ /// Maximum amount of INDY that can be spent at once from the treasury.
1600
+ maxTreasuryIndySpend:
1601
+ govUtxo.datum.protocolParams.maxTreasuryIndySpend,
1602
+ },
1603
+ },
1604
+ },
1605
+ {
1606
+ destination: addressFromBech32(context.users.withdrawalAccount.address),
1607
+ value: [
1608
+ [
1609
+ sysParams.govParams.indyAsset[0].unCurrencySymbol,
1610
+ fromText(sysParams.govParams.indyAsset[1].unTokenName),
1611
+ withdrawalIndyAmt,
1612
+ ],
1613
+ ],
1614
+ },
1615
+ sysParams,
1616
+ context.lucid,
1617
+ context.emulator.slot,
1618
+ govUtxo.utxo,
1619
+ [],
1620
+ );
1621
+
1622
+ await runAndAwaitTxBuilder(context.lucid, tx);
1623
+
1624
+ await runCreateAllShards(pollId, sysParams, context);
1625
+
1626
+ await runAndAwaitTx(
1627
+ context.lucid,
1628
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1629
+ );
1630
+
1631
+ await runVote(pollId, 'Yes', sysParams, context);
1632
+
1633
+ await waitForVotingEnd(pollId, sysParams, context);
1634
+
1635
+ await runMergeAllShards(pollId, sysParams, context);
1636
+
1637
+ await runEndProposal(pollId, sysParams, context);
1638
+
1639
+ const executeUtxo = await findExecute(
1640
+ context.lucid,
1641
+ sysParams.validatorHashes.executeHash,
1642
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1643
+ pollId,
1644
+ );
1645
+
1646
+ const [__, newVal] = await getValueChangeAtAddressAfterAction(
1647
+ context.lucid,
1648
+ context.users.withdrawalAccount.address,
1649
+ async () =>
1650
+ runAndAwaitTx(
1651
+ context.lucid,
1652
+ executeProposal(
1653
+ executeUtxo.utxo,
1654
+ (
1655
+ await findGov(
1656
+ context.lucid,
1657
+ sysParams.validatorHashes.govHash,
1658
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1659
+ )
1660
+ ).utxo,
1661
+ treasuryWithdrawalUtxo,
1662
+ null,
1663
+ null,
1664
+ sysParams,
1665
+ context.lucid,
1666
+ context.emulator.slot,
1667
+ ),
1668
+ ),
1669
+ );
1670
+
1671
+ expect(
1672
+ assetClassValueOf(
1673
+ newVal,
1674
+ fromSystemParamsAsset(sysParams.govParams.indyAsset),
1675
+ ) === withdrawalIndyAmt,
1676
+ 'Unexpected withdrawn indy amt',
1677
+ ).toBeTruthy();
1678
+ });
1679
+
1680
+ test<MyContext>('Execute upgrade protocol proposal', async (context: MyContext) => {
1681
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1682
+
1683
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1684
+
1685
+ const govUtxo = await findGov(
1686
+ context.lucid,
1687
+ sysParams.validatorHashes.govHash,
1688
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1689
+ );
1690
+
1691
+ const [tx, pollId] = await createProposal(
1692
+ {
1693
+ UpgradeProtocol: {
1694
+ content: serialiseUpgradePaths({
1695
+ upgradeId: govUtxo.datum.currentVersion + 1n,
1696
+ upgradePaths: new Map([
1697
+ [
1698
+ fromHex(sysParams.validatorHashes.cdpHash),
1699
+ // NOTICE: this is just a placeholder, in real scenario it needs upgrade minting policy hash
1700
+ {
1701
+ upgradeSymbol: fromHex(
1702
+ sysParams.validatorHashes.cdpCreatorHash,
1703
+ ),
1704
+ },
1705
+ ],
1706
+ ]),
1707
+ }),
1708
+ },
1709
+ },
1710
+ null,
1711
+ sysParams,
1712
+ context.lucid,
1713
+ context.emulator.slot,
1714
+ govUtxo.utxo,
1715
+ [],
1716
+ );
1717
+
1718
+ await runAndAwaitTxBuilder(context.lucid, tx);
1719
+
1720
+ await runCreateAllShards(pollId, sysParams, context);
1721
+
1722
+ await runAndAwaitTx(
1723
+ context.lucid,
1724
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1725
+ );
1726
+
1727
+ await runVote(pollId, 'Yes', sysParams, context);
1728
+
1729
+ await waitForVotingEnd(pollId, sysParams, context);
1730
+
1731
+ await runMergeAllShards(pollId, sysParams, context);
1732
+
1733
+ await runEndProposal(pollId, sysParams, context);
1734
+
1735
+ const executeUtxo = await findExecute(
1736
+ context.lucid,
1737
+ sysParams.validatorHashes.executeHash,
1738
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1739
+ pollId,
1740
+ );
1741
+
1742
+ await runAndAwaitTx(
1743
+ context.lucid,
1744
+ executeProposal(
1745
+ executeUtxo.utxo,
1746
+ (
1747
+ await findGov(
1748
+ context.lucid,
1749
+ sysParams.validatorHashes.govHash,
1750
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1751
+ )
1752
+ ).utxo,
1753
+ null,
1754
+ null,
1755
+ null,
1756
+ sysParams,
1757
+ context.lucid,
1758
+ context.emulator.slot,
1759
+ ),
1760
+ );
1761
+ });
1762
+
1763
+ test<MyContext>('Execute upgrade protocol proposal with withdrawal', async (context: MyContext) => {
1764
+ context.lucid.selectWallet.fromSeed(context.users.admin.seedPhrase);
1765
+
1766
+ const [sysParams, _] = await init(context.lucid, [iusdInitialAssetCfg]);
1767
+
1768
+ const withdrawalIndyAmt = 1_000n;
1769
+ const treasuryWithdrawalUtxo = await createUtxoAtTreasury(
1770
+ withdrawalIndyAmt,
1771
+ sysParams,
1772
+ context,
1773
+ );
1774
+
1775
+ const govUtxo = await findGov(
1776
+ context.lucid,
1777
+ sysParams.validatorHashes.govHash,
1778
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1779
+ );
1780
+
1781
+ const [tx, pollId] = await createProposal(
1782
+ {
1783
+ UpgradeProtocol: {
1784
+ content: serialiseUpgradePaths({
1785
+ upgradeId: govUtxo.datum.currentVersion + 1n,
1786
+ upgradePaths: new Map([
1787
+ [
1788
+ fromHex(sysParams.validatorHashes.cdpHash),
1789
+ // NOTICE: this is just a placeholder, in real scenario it needs upgrade minting policy hash
1790
+ {
1791
+ upgradeSymbol: fromHex(
1792
+ sysParams.validatorHashes.cdpCreatorHash,
1793
+ ),
1794
+ },
1795
+ ],
1796
+ ]),
1797
+ }),
1798
+ },
1799
+ },
1800
+ {
1801
+ destination: addressFromBech32(context.users.withdrawalAccount.address),
1802
+ value: [
1803
+ [
1804
+ sysParams.govParams.indyAsset[0].unCurrencySymbol,
1805
+ fromText(sysParams.govParams.indyAsset[1].unTokenName),
1806
+ withdrawalIndyAmt,
1807
+ ],
1808
+ ],
1809
+ },
1810
+ sysParams,
1811
+ context.lucid,
1812
+ context.emulator.slot,
1813
+ govUtxo.utxo,
1814
+ [],
1815
+ );
1816
+
1817
+ await runAndAwaitTxBuilder(context.lucid, tx);
1818
+
1819
+ await runCreateAllShards(pollId, sysParams, context);
1820
+
1821
+ await runAndAwaitTx(
1822
+ context.lucid,
1823
+ openStakingPosition(100_000_000_000n, sysParams, context.lucid),
1824
+ );
1825
+
1826
+ await runVote(pollId, 'Yes', sysParams, context);
1827
+
1828
+ await waitForVotingEnd(pollId, sysParams, context);
1829
+
1830
+ await runMergeAllShards(pollId, sysParams, context);
1831
+
1832
+ await runEndProposal(pollId, sysParams, context);
1833
+
1834
+ const executeUtxo = await findExecute(
1835
+ context.lucid,
1836
+ sysParams.validatorHashes.executeHash,
1837
+ fromSystemParamsAsset(sysParams.executeParams.upgradeToken),
1838
+ pollId,
1839
+ );
1840
+
1841
+ const [__, newVal] = await getValueChangeAtAddressAfterAction(
1842
+ context.lucid,
1843
+ context.users.withdrawalAccount.address,
1844
+ async () =>
1845
+ runAndAwaitTx(
1846
+ context.lucid,
1847
+ executeProposal(
1848
+ executeUtxo.utxo,
1849
+ (
1850
+ await findGov(
1851
+ context.lucid,
1852
+ sysParams.validatorHashes.govHash,
1853
+ fromSystemParamsAsset(sysParams.govParams.govNFT),
1854
+ )
1855
+ ).utxo,
1856
+ treasuryWithdrawalUtxo,
1857
+ null,
1858
+ null,
1859
+ sysParams,
1860
+ context.lucid,
1861
+ context.emulator.slot,
1862
+ ),
1863
+ ),
1864
+ );
1865
+
1866
+ expect(
1867
+ assetClassValueOf(
1868
+ newVal,
1869
+ fromSystemParamsAsset(sysParams.govParams.indyAsset),
1870
+ ) === withdrawalIndyAmt,
1871
+ 'Unexpected withdrawn indy amt',
1872
+ ).toBeTruthy();
1873
+ });
1874
+ });