@indigo-labs/indigo-sdk 0.2.41 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/.github/workflows/ci.yml +4 -2
  2. package/dist/index.d.mts +3008 -2194
  3. package/dist/index.d.ts +3008 -2194
  4. package/dist/index.js +9827 -6194
  5. package/dist/index.mjs +8591 -4809
  6. package/package.json +14 -3
  7. package/src/contracts/cdp/helpers.ts +68 -72
  8. package/src/contracts/cdp/scripts.ts +50 -13
  9. package/src/contracts/cdp/transactions.ts +831 -545
  10. package/src/contracts/cdp/types-new.ts +256 -0
  11. package/src/contracts/cdp/types.ts +26 -144
  12. package/src/contracts/cdp-creator/scripts.ts +15 -9
  13. package/src/contracts/cdp-creator/types-new.ts +50 -0
  14. package/src/contracts/cdp-creator/types.ts +5 -31
  15. package/src/contracts/collector/scripts.ts +1 -1
  16. package/src/contracts/collector/transactions.ts +23 -13
  17. package/src/contracts/collector/types-new.ts +17 -0
  18. package/src/contracts/execute/scripts.ts +19 -10
  19. package/src/contracts/execute/types-new.ts +44 -0
  20. package/src/contracts/execute/types.ts +5 -38
  21. package/src/contracts/gov/helpers.ts +187 -51
  22. package/src/contracts/gov/scripts.ts +17 -10
  23. package/src/contracts/gov/transactions.ts +599 -271
  24. package/src/contracts/gov/types-new.ts +253 -100
  25. package/src/contracts/gov/types.ts +4 -71
  26. package/src/contracts/iasset/helpers.ts +172 -0
  27. package/src/contracts/iasset/scripts.ts +38 -0
  28. package/src/contracts/iasset/types.ts +154 -0
  29. package/src/contracts/initialize/actions.ts +768 -0
  30. package/src/contracts/initialize/helpers.ts +611 -36
  31. package/src/contracts/initialize/types.ts +102 -28
  32. package/src/contracts/interest-collection/helpers.ts +19 -0
  33. package/src/contracts/interest-collection/scripts.ts +44 -0
  34. package/src/contracts/interest-collection/transactions.ts +436 -0
  35. package/src/contracts/interest-collection/types-new.ts +50 -0
  36. package/src/contracts/interest-collection/types.ts +26 -0
  37. package/src/contracts/interest-oracle/helpers.ts +2 -30
  38. package/src/contracts/interest-oracle/scripts.ts +1 -1
  39. package/src/contracts/interest-oracle/transactions.ts +21 -16
  40. package/src/contracts/interest-oracle/types-new.ts +32 -0
  41. package/src/contracts/interest-oracle/types.ts +1 -40
  42. package/src/contracts/one-shot/transactions.ts +1 -2
  43. package/src/contracts/poll/helpers.ts +5 -23
  44. package/src/contracts/poll/scripts.ts +12 -13
  45. package/src/contracts/poll/types-poll-manager.ts +1 -19
  46. package/src/contracts/poll/types-poll-new.ts +170 -0
  47. package/src/contracts/poll/types-poll-shard.ts +2 -24
  48. package/src/contracts/price-oracle/helpers.ts +1 -4
  49. package/src/contracts/price-oracle/scripts.ts +3 -8
  50. package/src/contracts/price-oracle/transactions.ts +32 -25
  51. package/src/contracts/price-oracle/types-new.ts +50 -0
  52. package/src/contracts/price-oracle/types.ts +2 -36
  53. package/src/contracts/pyth-feed/helpers.ts +58 -0
  54. package/src/contracts/pyth-feed/scripts.ts +15 -0
  55. package/src/contracts/pyth-feed/types.ts +181 -0
  56. package/src/contracts/rob/helpers.ts +405 -0
  57. package/src/contracts/rob/scripts.ts +35 -0
  58. package/src/contracts/rob/transactions.ts +410 -0
  59. package/src/contracts/rob/types-new.ts +128 -0
  60. package/src/contracts/rob/types.ts +16 -0
  61. package/src/contracts/rob-leverage/helpers.ts +424 -0
  62. package/src/contracts/{leverage → rob-leverage}/transactions.ts +68 -48
  63. package/src/contracts/stability-pool/helpers.ts +714 -230
  64. package/src/contracts/stability-pool/scripts.ts +20 -15
  65. package/src/contracts/stability-pool/transactions.ts +625 -495
  66. package/src/contracts/stability-pool/types-new.ts +237 -100
  67. package/src/contracts/stability-pool/types.ts +5 -22
  68. package/src/contracts/stableswap/helpers.ts +22 -0
  69. package/src/contracts/stableswap/scripts.ts +37 -0
  70. package/src/contracts/stableswap/transactions.ts +647 -0
  71. package/src/contracts/stableswap/types-new.ts +131 -0
  72. package/src/contracts/stableswap/types.ts +17 -0
  73. package/src/contracts/staking/helpers.ts +49 -34
  74. package/src/contracts/staking/scripts.ts +1 -1
  75. package/src/contracts/staking/transactions.ts +85 -130
  76. package/src/contracts/staking/types-new.ts +60 -28
  77. package/src/contracts/staking/types.ts +1 -28
  78. package/src/contracts/treasury/helpers.ts +21 -0
  79. package/src/contracts/treasury/scripts.ts +16 -26
  80. package/src/contracts/treasury/transactions.ts +256 -27
  81. package/src/contracts/treasury/types-new.ts +69 -0
  82. package/src/contracts/treasury/types.ts +2 -43
  83. package/src/contracts/version-registry/scripts.ts +2 -2
  84. package/src/contracts/version-registry/types-new.ts +6 -7
  85. package/src/index.ts +37 -20
  86. package/src/scripts/auth-token-policy.ts +3 -2
  87. package/src/scripts/iasset-policy.ts +3 -2
  88. package/src/types/evolution-schema-options.ts +3 -3
  89. package/src/types/generic.ts +17 -89
  90. package/src/types/multisig.ts +48 -0
  91. package/src/types/on-chain-decimal.ts +14 -7
  92. package/src/types/rational.ts +61 -0
  93. package/src/types/system-params.ts +237 -41
  94. package/src/utils/array-utils.ts +70 -1
  95. package/src/utils/bigint-utils.ts +12 -0
  96. package/src/utils/indigo-helpers.ts +8 -10
  97. package/src/utils/lucid-utils.ts +47 -40
  98. package/src/utils/oracle-helpers.ts +62 -0
  99. package/src/utils/pyth/decode.ts +223 -0
  100. package/src/utils/pyth/encode.ts +262 -0
  101. package/src/utils/pyth/index.ts +14 -0
  102. package/src/utils/pyth/types.ts +87 -0
  103. package/src/validators/always-succeed-validator.ts +6 -0
  104. package/src/validators/cdp-creator-validator.ts +2 -2
  105. package/src/validators/cdp-redeem-validator.ts +7 -0
  106. package/src/validators/cdp-validator.ts +2 -2
  107. package/src/validators/collector-validator.ts +2 -2
  108. package/src/validators/execute-validator.ts +2 -2
  109. package/src/validators/governance-validator.ts +2 -2
  110. package/src/validators/iasset-validator.ts +7 -0
  111. package/src/validators/interest-collection-validator.ts +7 -0
  112. package/src/validators/interest-oracle-validator.ts +2 -2
  113. package/src/validators/poll-manager-validator.ts +2 -2
  114. package/src/validators/poll-shard-validator.ts +2 -2
  115. package/src/validators/price-oracle-validator.ts +7 -0
  116. package/src/validators/pyth-feed-validator.ts +7 -0
  117. package/src/validators/rob-validator.ts +7 -0
  118. package/src/validators/stability-pool-validator.ts +2 -2
  119. package/src/validators/stableswap-validator.ts +7 -0
  120. package/src/validators/staking-validator.ts +2 -2
  121. package/src/validators/treasury-validator.ts +2 -2
  122. package/src/validators/version-record-policy.ts +2 -2
  123. package/src/validators/version-registry-validator.ts +2 -2
  124. package/tests/always-succeed/script.ts +7 -0
  125. package/tests/bigint-utils.test.ts +41 -0
  126. package/tests/cdp/actions.ts +611 -0
  127. package/tests/cdp/cdp-helpers.ts +55 -0
  128. package/tests/cdp/cdp-queries.ts +440 -0
  129. package/tests/cdp/cdp.test.ts +6087 -0
  130. package/tests/cdp/transactions-mutated.ts +1729 -0
  131. package/tests/data/system-params.json +177 -34
  132. package/tests/datums.test.ts +209 -210
  133. package/tests/endpoints/initialize.ts +68 -0
  134. package/tests/endpoints/interest-collector.ts +37 -0
  135. package/tests/endpoints/treasury.ts +70 -0
  136. package/tests/gov/actions.ts +406 -0
  137. package/tests/gov/gov.test.ts +4450 -0
  138. package/tests/{queries → gov}/governance-queries.ts +6 -3
  139. package/tests/hash-checks.test.ts +38 -11
  140. package/tests/indigo-test-helpers.ts +100 -0
  141. package/tests/initialize.test.ts +61 -9
  142. package/tests/interest-collection/interest-collection.test.ts +892 -0
  143. package/tests/interest-collection/interest-collector-queries.ts +49 -0
  144. package/tests/interest-collection/transactions-mutated.ts +260 -0
  145. package/tests/interest-oracle.test.ts +43 -35
  146. package/tests/mock/assets-mock.ts +234 -23
  147. package/tests/mock/protocol-params-mock.ts +21 -0
  148. package/tests/price-oracle/actions.ts +163 -0
  149. package/tests/price-oracle/price-oracle-queries.ts +12 -0
  150. package/tests/price-oracle/price-oracle.test.ts +240 -0
  151. package/tests/price-oracle/transactions-mutated.ts +62 -0
  152. package/tests/pyth/endpoints.ts +96 -0
  153. package/tests/pyth/helpers.ts +37 -0
  154. package/tests/pyth/pyth-encoding.test.ts +376 -0
  155. package/tests/pyth/pyth-indigo.test.ts +509 -0
  156. package/tests/pyth/pyth.test.ts +300 -0
  157. package/tests/queries/execute-queries.ts +6 -5
  158. package/tests/queries/iasset-queries.ts +175 -5
  159. package/tests/queries/interest-oracle-queries.ts +4 -2
  160. package/tests/queries/poll-queries.ts +8 -9
  161. package/tests/queries/stability-pool-queries.ts +95 -48
  162. package/tests/queries/staking-queries.ts +4 -2
  163. package/tests/queries/treasury-queries.ts +80 -5
  164. package/tests/rob/actions.ts +58 -0
  165. package/tests/{lrp-leverage.test.ts → rob/rob-leverage.test.ts} +393 -296
  166. package/tests/rob/rob-queries.ts +95 -0
  167. package/tests/rob/rob.test.ts +3762 -0
  168. package/tests/rob/transactions-mutated.ts +853 -0
  169. package/tests/script-size.test.ts +240 -0
  170. package/tests/setup.ts +135 -0
  171. package/tests/stability-pool/actions.ts +210 -0
  172. package/tests/stability-pool.test.ts +5469 -666
  173. package/tests/stableswap/stableswap-actions.ts +84 -0
  174. package/tests/stableswap/stableswap-queries.ts +89 -0
  175. package/tests/stableswap/stableswap.test.ts +3891 -0
  176. package/tests/stableswap/transactions-mutated.ts +348 -0
  177. package/tests/staking.test.ts +82 -99
  178. package/tests/test-helpers.ts +58 -11
  179. package/tests/treasury.test.ts +242 -0
  180. package/tests/utils/asserts.ts +74 -0
  181. package/tests/utils/benchmark-utils.ts +81 -0
  182. package/tests/utils/index.ts +122 -4
  183. package/tsconfig.json +9 -1
  184. package/vitest.config.ts +3 -1
  185. package/src/contracts/collector/types.ts +0 -16
  186. package/src/contracts/initialize/transactions.ts +0 -891
  187. package/src/contracts/leverage/helpers.ts +0 -424
  188. package/src/contracts/lrp/helpers.ts +0 -294
  189. package/src/contracts/lrp/scripts.ts +0 -27
  190. package/src/contracts/lrp/transactions.ts +0 -250
  191. package/src/contracts/lrp/types.ts +0 -131
  192. package/src/contracts/poll/types-poll.ts +0 -88
  193. package/src/contracts/vesting/helpers.ts +0 -218
  194. package/src/utils/value-helpers.ts +0 -37
  195. package/src/validators/lrp-validator.ts +0 -7
  196. package/tests/cdp.test.ts +0 -1528
  197. package/tests/gov.test.ts +0 -2011
  198. package/tests/lrp.test.ts +0 -673
  199. package/tests/queries/cdp-queries.ts +0 -220
  200. package/tests/queries/lrp-queries.ts +0 -76
  201. package/tests/queries/price-oracle-queries.ts +0 -10
@@ -1,311 +1,795 @@
1
- import { UTxO } from '@lucid-evolution/lucid';
2
- import { getInlineDatumOrThrow } from '../../utils/lucid-utils';
3
1
  import {
4
- EpochToScaleToSum,
2
+ addAssets,
3
+ Address,
4
+ Assets,
5
+ credentialToAddress,
6
+ getInputIndices,
7
+ LucidEvolution,
8
+ OutRef,
9
+ toHex,
10
+ UTxO,
11
+ validatorToScriptHash,
12
+ } from '@lucid-evolution/lucid';
13
+ import {
14
+ AccountContent,
15
+ AssetSnapshot,
16
+ AssetState,
17
+ E2S2SIndex,
18
+ E2S2SIndicesPerAsset,
19
+ EpochToScaleKey,
20
+ EpochToScaleToSumEntry,
5
21
  fromSPInteger,
6
22
  mkSPInteger,
7
- parseSnapshotEpochToScaleToSumDatum,
8
- parseStabilityPoolDatum,
23
+ parseSnapshotEpochToScaleToSumDatumOrThrow,
24
+ ProcessRequestAccountContent,
25
+ SnapshotEpochToScaleToSumContent,
9
26
  spAdd,
10
27
  spDiv,
11
28
  SPInteger,
12
29
  spMul,
13
30
  spSub,
14
31
  StabilityPoolContent,
15
- StabilityPoolSnapshot,
32
+ StateSnapshot,
33
+ SumSnapshot,
16
34
  } from './types-new';
17
- import { match } from 'ts-pattern';
35
+ import {
36
+ readonlyArray as RA,
37
+ array as A,
38
+ function as F,
39
+ option as O,
40
+ } from 'fp-ts';
41
+ import {
42
+ AssetClass,
43
+ getInlineDatumOrThrow,
44
+ isSameAssetClass,
45
+ isSameOutRef,
46
+ mkAssetsOf,
47
+ } from '@3rd-eye-labs/cardano-offchain-common';
48
+ import { repsertWithReadonlyArr } from '../../utils/array-utils';
49
+ import { match, P } from 'ts-pattern';
50
+ import {
51
+ fromSysParamsCredential,
52
+ SystemParams,
53
+ } from '../../types/system-params';
54
+ import { mkStabilityPoolValidatorFromSP } from './scripts';
18
55
 
56
+ export const BASE_MAX_TX_FEE = 1_000_000n;
57
+
58
+ export const MAX_E2S2S_ENTRIES_COUNT = 5;
19
59
  const newScaleMultiplier = 1_000_000_000n;
20
60
 
21
- export const initSpSnapshot: StabilityPoolSnapshot = {
22
- productVal: { value: 1_000_000_000_000_000_000n },
61
+ export const initSumVal: SPInteger = { value: 0n };
62
+
63
+ export const initSpState: StateSnapshot = {
64
+ productVal: mkSPInteger(1n),
23
65
  depositVal: { value: 0n },
24
- sumVal: { value: 0n },
25
66
  epoch: 0n,
26
67
  scale: 0n,
27
68
  };
28
69
 
29
- export const initEpochToScaleToSumMap = (): EpochToScaleToSum =>
30
- new Map([[{ epoch: 0n, scale: 0n }, { value: 0n }]]);
31
-
32
- export function getSumFromEpochToScaleToSum(
33
- e2s2s: EpochToScaleToSum,
34
- epoch: bigint,
35
- scale: bigint,
36
- ): SPInteger | undefined {
37
- for (const [key, value] of e2s2s.entries()) {
38
- if (key.epoch === epoch && key.scale === scale) {
39
- return value;
40
- }
41
- }
42
- return undefined;
70
+ export function mkStabilityPoolAddr(
71
+ lucid: LucidEvolution,
72
+ sysParams: SystemParams,
73
+ ): Address {
74
+ return credentialToAddress(
75
+ lucid.config().network!,
76
+ {
77
+ hash: validatorToScriptHash(
78
+ mkStabilityPoolValidatorFromSP(sysParams.stabilityPoolParams),
79
+ ),
80
+ type: 'Script',
81
+ },
82
+ sysParams.stabilityPoolParams.stakeCredential != null
83
+ ? fromSysParamsCredential(sysParams.stabilityPoolParams.stakeCredential)
84
+ : undefined,
85
+ );
43
86
  }
44
87
 
45
- /**
46
- * It's necessary to use this to update map entries because typescript can't compare keys as objects.
47
- */
48
- export function setSumInEpochToScaleToSum(
49
- e2s2s: EpochToScaleToSum,
50
- epoch: bigint,
51
- scale: bigint,
52
- sum: SPInteger,
53
- ): EpochToScaleToSum {
54
- const map = new Map<{ epoch: bigint; scale: bigint }, SPInteger>();
55
- for (const [key, value] of e2s2s.entries()) {
56
- if (!(key.epoch === epoch && key.scale === scale)) {
57
- map.set(key, value);
58
- }
59
- }
88
+ export function isSameEpochToScaleKey(
89
+ a: EpochToScaleKey,
90
+ b: EpochToScaleKey,
91
+ ): boolean {
92
+ return a.epoch === b.epoch && a.scale === b.scale;
93
+ }
60
94
 
61
- map.set({ epoch, scale }, sum);
62
- return map;
95
+ type StabilityPoolListIdx = {
96
+ spListIdx: number;
97
+ sumSnapshot: readonly [EpochToScaleKey, SumSnapshot];
98
+ };
99
+ type SnapshotIdx = {
100
+ snapshotUtxo: UTxO;
101
+ snapshotDatum: SnapshotEpochToScaleToSumContent;
102
+ snapshotListIdx: number;
103
+ sumSnapshot: readonly [EpochToScaleKey, SumSnapshot];
104
+ };
105
+ export type FindE2S2SIdxResult = StabilityPoolListIdx | SnapshotIdx;
106
+
107
+ function findE2s2sIdx(
108
+ expectedKey: EpochToScaleKey,
109
+ collateralAsset: AssetClass,
110
+ spAssetState: AssetSnapshot,
111
+ e2s2sUtxos: [UTxO, SnapshotEpochToScaleToSumContent][],
112
+ ): O.Option<FindE2S2SIdxResult> {
113
+ return F.pipe(
114
+ // Try to find the s1 in sp e2s2s.
115
+ F.pipe(
116
+ spAssetState.epoch2scale2sum,
117
+ RA.findIndex(([key, _]) => isSameEpochToScaleKey(key, expectedKey)),
118
+ ),
119
+ O.match<number, O.Option<FindE2S2SIdxResult>>(
120
+ // When such e2sKey non existent in sp e2s2s, find it in e2s2s snapshots
121
+ () =>
122
+ F.pipe(
123
+ e2s2sUtxos,
124
+ A.findFirst(
125
+ ([_, datum]) =>
126
+ isSameAssetClass(datum.collateralAsset, collateralAsset) &&
127
+ F.pipe(
128
+ datum.snapshot,
129
+ RA.exists(([key, _]) =>
130
+ isSameEpochToScaleKey(expectedKey, key),
131
+ ),
132
+ ),
133
+ ),
134
+ O.map((res) => {
135
+ const listIdx = F.pipe(
136
+ res[1].snapshot,
137
+ RA.findIndex(([key, _]) =>
138
+ isSameEpochToScaleKey(key, expectedKey),
139
+ ),
140
+ O.getOrElse<number>(() => {
141
+ throw new Error(
142
+ 'It was supposed to be there. Some logic error.',
143
+ );
144
+ }),
145
+ );
146
+
147
+ return {
148
+ snapshotUtxo: res[0],
149
+ snapshotDatum: res[1],
150
+ snapshotListIdx: listIdx,
151
+ sumSnapshot: res[1].snapshot[listIdx],
152
+ } satisfies SnapshotIdx;
153
+ }),
154
+ ),
155
+ (poolIdx) =>
156
+ O.some({
157
+ spListIdx: poolIdx,
158
+ sumSnapshot: spAssetState.epoch2scale2sum[poolIdx],
159
+ } satisfies StabilityPoolListIdx),
160
+ ),
161
+ );
63
162
  }
64
163
 
65
- export function getAccountReward(
66
- account: StabilityPoolSnapshot,
67
- e2s2s: EpochToScaleToSum,
68
- ): SPInteger {
69
- const s1 = getSumFromEpochToScaleToSum(e2s2s, account.epoch, account.scale);
70
- if (!s1) throw new Error('No scale found for epoch and scale');
71
- const s2 =
72
- getSumFromEpochToScaleToSum(e2s2s, account.epoch, account.scale + 1n) ?? s1;
73
- const a1 = spSub(s1, account.sumVal);
74
- const a2 = spDiv(spSub(s2, s1), mkSPInteger(newScaleMultiplier));
164
+ export async function findRelevantE2s2sIdxs(
165
+ lucid: LucidEvolution,
166
+ stabilityPool: StabilityPoolContent,
167
+ accountState: StateSnapshot,
168
+ allSnapshotsOutRefs: OutRef[],
169
+ ): Promise<[FindE2S2SIdxResult, O.Option<FindE2S2SIdxResult>][]> {
170
+ const e2s2sUtxos = (await lucid.utxosByOutRef(allSnapshotsOutRefs))
171
+ .map((utxo) => {
172
+ return [
173
+ utxo,
174
+ parseSnapshotEpochToScaleToSumDatumOrThrow(getInlineDatumOrThrow(utxo)),
175
+ ] satisfies [UTxO, SnapshotEpochToScaleToSumContent];
176
+ })
177
+ .filter(([_, d]) => toHex(d.iasset) === toHex(stabilityPool.iasset));
178
+
179
+ const s1E2sKey: EpochToScaleKey = {
180
+ epoch: accountState.epoch,
181
+ scale: accountState.scale,
182
+ };
183
+ const s2E2sKey: EpochToScaleKey = {
184
+ epoch: accountState.epoch,
185
+ scale: accountState.scale + 1n,
186
+ };
187
+
188
+ return stabilityPool.assetStates.map(([collateralAsset, spAssetState]) => {
189
+ const s1Res = F.pipe(
190
+ findE2s2sIdx(s1E2sKey, collateralAsset, spAssetState, e2s2sUtxos),
191
+ // When it's non-existent, we need to find proof it doesn't exist.
192
+ O.getOrElse(() =>
193
+ F.pipe(
194
+ e2s2sUtxos,
195
+ A.findFirst(
196
+ ([_, datum]) =>
197
+ isSameAssetClass(datum.collateralAsset, collateralAsset) &&
198
+ F.pipe(
199
+ datum.snapshot,
200
+ RA.exists(([_, val]) => val.isFirstSnapshot),
201
+ ),
202
+ ),
203
+ O.match<[UTxO, SnapshotEpochToScaleToSumContent], FindE2S2SIdxResult>(
204
+ // Try to find the proof in pool's list.
205
+ () =>
206
+ F.pipe(
207
+ spAssetState.epoch2scale2sum,
208
+ RA.findIndex(([_, dat]) => dat.isFirstSnapshot),
209
+ O.match(
210
+ () => {
211
+ throw new Error(
212
+ "Couldn't find relevant proof for s1 non-existence.",
213
+ );
214
+ },
215
+ (poolIdx) =>
216
+ ({
217
+ spListIdx: poolIdx,
218
+ sumSnapshot: spAssetState.epoch2scale2sum[poolIdx],
219
+ }) satisfies StabilityPoolListIdx,
220
+ ),
221
+ ),
222
+ (snapshotProof) => {
223
+ const listIdx = F.pipe(
224
+ snapshotProof[1].snapshot,
225
+ RA.findIndex(([_, val]) => val.isFirstSnapshot),
226
+ O.getOrElse<number>(() => {
227
+ throw new Error(
228
+ 'It was supposed to be there. Some logic error.',
229
+ );
230
+ }),
231
+ );
232
+
233
+ return {
234
+ snapshotUtxo: snapshotProof[0],
235
+ snapshotDatum: snapshotProof[1],
236
+ snapshotListIdx: listIdx,
237
+ sumSnapshot: snapshotProof[1].snapshot[listIdx],
238
+ } satisfies SnapshotIdx;
239
+ },
240
+ ),
241
+ ),
242
+ ),
243
+ );
244
+
245
+ const s2Res = findE2s2sIdx(
246
+ s2E2sKey,
247
+ collateralAsset,
248
+ spAssetState,
249
+ e2s2sUtxos,
250
+ );
75
251
 
76
- return spDiv(spMul(spAdd(a1, a2), account.depositVal), account.productVal);
252
+ // Is actual s1 index?
253
+ if (isSameEpochToScaleKey(s1Res.sumSnapshot[0], s1E2sKey)) {
254
+ return [
255
+ s1Res,
256
+ F.pipe(
257
+ s2Res,
258
+ O.match(
259
+ () => {
260
+ // When s1 is not just a proof and is last in epoch, it proves s2 non-existant.
261
+ if (s1Res.sumSnapshot[1].isLastInEpoch) {
262
+ return O.none;
263
+ } else {
264
+ throw new Error('Expected s2 to be existent.');
265
+ }
266
+ },
267
+ (res) => O.some(res),
268
+ ),
269
+ ),
270
+ ];
271
+ } else {
272
+ // When s1 is just a proof.
273
+ return [
274
+ s1Res,
275
+ F.pipe(
276
+ s2Res,
277
+ O.match(
278
+ () => {
279
+ // When the non-existance proof works for s2 as well
280
+ if (
281
+ s1Res.sumSnapshot[1].isFirstSnapshot &&
282
+ (s1Res.sumSnapshot[0].epoch > s2E2sKey.epoch ||
283
+ (s1Res.sumSnapshot[0].epoch === s2E2sKey.epoch &&
284
+ s1Res.sumSnapshot[0].scale > s2E2sKey.scale))
285
+ ) {
286
+ return O.none;
287
+ }
288
+
289
+ throw new Error("S1 proof doesn't work for s2.");
290
+ },
291
+ // s2 exists.
292
+ (res) => O.some(res),
293
+ ),
294
+ ),
295
+ ];
296
+ }
297
+ });
77
298
  }
78
299
 
79
- export function getAccountFund(
80
- pool: StabilityPoolSnapshot,
81
- account: StabilityPoolSnapshot,
82
- ): SPInteger {
83
- let fund = spDiv(
84
- spMul(account.depositVal, pool.productVal),
85
- account.productVal,
300
+ export function createProcessRequestAccountRedeemer(
301
+ e2s2sIdxs: [FindE2S2SIdxResult, O.Option<FindE2S2SIdxResult>][],
302
+ otherRefInputs: UTxO[],
303
+ currentTime: bigint,
304
+ ): {
305
+ e2s2sRefInputs: UTxO[];
306
+ mkProcessRequestAccountRedeemerContent: (
307
+ poolInputIdx: bigint,
308
+ accountInputIdx: bigint,
309
+ ) => ProcessRequestAccountContent;
310
+ } {
311
+ const allE2s2sSnapshotRefInputs = F.pipe(
312
+ e2s2sIdxs,
313
+ A.flatMap(([s1, s2]) => {
314
+ const s1RefInput = match(s1)
315
+ .with({ snapshotUtxo: P.select() }, (utxo) => O.some(utxo))
316
+ .otherwise(() => O.none);
317
+
318
+ const s2RefInput = F.pipe(
319
+ s2,
320
+ O.match(
321
+ () => [],
322
+ (s) =>
323
+ match(s)
324
+ .with({ snapshotUtxo: P.select() }, (utxo) =>
325
+ F.pipe(
326
+ s1RefInput,
327
+ O.match(
328
+ () => [utxo],
329
+ // when s1 ref input is same as s2 ref input, don't reference it again
330
+ (s1In) => (isSameOutRef(s1In, utxo) ? [] : [utxo]),
331
+ ),
332
+ ),
333
+ )
334
+ .otherwise(() => []),
335
+ ),
336
+ );
337
+
338
+ return [...F.pipe([s1RefInput], A.compact), ...s2RefInput];
339
+ }),
86
340
  );
87
- if (pool.epoch > account.epoch) fund = mkSPInteger(0n);
88
- if (pool.scale - account.scale > 1n) fund = mkSPInteger(0n);
89
- if (pool.scale > account.scale)
90
- fund = spDiv(spMul(account.depositVal, pool.productVal), {
91
- value: account.productVal.value * newScaleMultiplier,
92
- });
93
341
 
94
- if (fund.value < spDiv(account.depositVal, mkSPInteger(1000000000n)).value)
95
- return mkSPInteger(0n);
96
- return fund;
97
- }
342
+ const e2s2sInputsIndices = getInputIndices(allE2s2sSnapshotRefInputs, [
343
+ ...otherRefInputs,
344
+ ...allE2s2sSnapshotRefInputs,
345
+ ]);
346
+
347
+ const createE2s2sIdx = (s: FindE2S2SIdxResult): E2S2SIndex =>
348
+ match(s)
349
+ .returnType<E2S2SIndex>()
350
+ .with({ spListIdx: P.select() }, (spListIdx) => ({
351
+ StabilityPoolListIdx: BigInt(spListIdx),
352
+ }))
353
+ .with({ snapshotUtxo: P.any }, (obj) => {
354
+ const idxForIdx = F.pipe(
355
+ allE2s2sSnapshotRefInputs,
356
+ A.findIndex((input) => isSameOutRef(input, obj.snapshotUtxo)),
357
+ O.getOrElse<number>(() => {
358
+ throw new Error('Expected to find the index.');
359
+ }),
360
+ );
98
361
 
99
- export function adjust(
100
- pool: StabilityPoolSnapshot,
101
- account: StabilityPoolSnapshot,
102
- e2s2s: EpochToScaleToSum,
103
- ): [StabilityPoolSnapshot, bigint] {
104
- const newAccountSnapshot: StabilityPoolSnapshot = {
105
- ...pool,
106
- depositVal: getAccountFund(pool, account),
362
+ return {
363
+ RefInputIdx: {
364
+ refInputIdx: e2s2sInputsIndices[idxForIdx],
365
+ snapshotListIdx: BigInt(obj.snapshotListIdx),
366
+ },
367
+ };
368
+ })
369
+ .exhaustive();
370
+
371
+ return {
372
+ e2s2sRefInputs: allE2s2sSnapshotRefInputs,
373
+ mkProcessRequestAccountRedeemerContent: (
374
+ poolInputIdx: bigint,
375
+ accountInputIdx: bigint,
376
+ ) => ({
377
+ poolInputIdx: poolInputIdx,
378
+ accountInputIdx: accountInputIdx,
379
+ e2s2sIdxs: F.pipe(
380
+ e2s2sIdxs,
381
+ A.reduce<
382
+ [FindE2S2SIdxResult, O.Option<FindE2S2SIdxResult>],
383
+ E2S2SIndicesPerAsset
384
+ >([], (acc, [s1, s2]) => {
385
+ return [
386
+ ...acc,
387
+ [
388
+ createE2s2sIdx(s1),
389
+ F.pipe(
390
+ s2,
391
+ O.match(
392
+ () => null,
393
+ (s) => createE2s2sIdx(s),
394
+ ),
395
+ ),
396
+ ],
397
+ ];
398
+ }),
399
+ ),
400
+ currentTime: currentTime,
401
+ }),
107
402
  };
403
+ }
108
404
 
109
- const accountReward = fromSPInteger(getAccountReward(account, e2s2s));
405
+ function calculateReward(
406
+ s1: SPInteger,
407
+ s2: SPInteger,
408
+ accountSumVal: SPInteger,
409
+ accountState: StateSnapshot,
410
+ ): bigint {
411
+ const a1 = spSub(s1, accountSumVal);
412
+ const a2 = spDiv(spSub(s2, s1), mkSPInteger(newScaleMultiplier));
110
413
 
111
- return [newAccountSnapshot, accountReward];
414
+ return F.pipe(
415
+ spDiv(
416
+ spMul(spAdd(a1, a2), accountState.depositVal),
417
+ accountState.productVal,
418
+ ),
419
+ fromSPInteger,
420
+ );
112
421
  }
113
422
 
114
- type SnapshotESSSearchResult = { utxo: UTxO; e2s2s: EpochToScaleToSum };
115
-
116
- function findEpochToScaleToSum(
117
- snapshotEpochToScaleToSumTokenRef1: UTxO,
118
- snapshotEpochToScaleToSumTokenRef2: UTxO | undefined,
119
- ): [SnapshotESSSearchResult, SnapshotESSSearchResult | undefined] {
120
- let ess1: EpochToScaleToSum;
121
- try {
122
- ess1 = parseSnapshotEpochToScaleToSumDatum(
123
- getInlineDatumOrThrow(snapshotEpochToScaleToSumTokenRef1),
124
- ).snapshot;
125
- } catch (_) {
126
- ess1 = parseStabilityPoolDatum(
127
- getInlineDatumOrThrow(snapshotEpochToScaleToSumTokenRef1),
128
- ).epochToScaleToSum;
129
- }
423
+ function getE2s2sEntry(
424
+ idx: FindE2S2SIdxResult,
425
+ assetState: AssetSnapshot,
426
+ ): EpochToScaleToSumEntry {
427
+ return match(idx)
428
+ .returnType<EpochToScaleToSumEntry>()
429
+ .with(
430
+ { spListIdx: P.select() },
431
+ (spIdx) => assetState.epoch2scale2sum[spIdx],
432
+ )
433
+ .with(
434
+ { snapshotUtxo: P.any },
435
+ (obj) => obj.snapshotDatum.snapshot[obj.snapshotListIdx],
436
+ )
437
+ .exhaustive();
438
+ }
130
439
 
131
- const ess1Ref: SnapshotESSSearchResult = {
132
- utxo: snapshotEpochToScaleToSumTokenRef1,
133
- e2s2s: ess1,
440
+ function rewardsPerAsset(
441
+ poolAssetStates: readonly AssetState[],
442
+ e2s2sIdxs: [FindE2S2SIdxResult, O.Option<FindE2S2SIdxResult>][],
443
+ accountAssetSums: readonly (readonly [AssetClass, SPInteger])[],
444
+ accountState: StateSnapshot,
445
+ ): [AssetClass, bigint][] {
446
+ const expectedS1Key: EpochToScaleKey = {
447
+ epoch: accountState.epoch,
448
+ scale: accountState.scale,
134
449
  };
135
450
 
136
- if (snapshotEpochToScaleToSumTokenRef2) {
137
- const ess2 = parseSnapshotEpochToScaleToSumDatum(
138
- getInlineDatumOrThrow(snapshotEpochToScaleToSumTokenRef2),
139
- );
140
- const ess2Ref: SnapshotESSSearchResult = {
141
- utxo: snapshotEpochToScaleToSumTokenRef2,
142
- e2s2s: ess2.snapshot,
143
- };
144
- return [ess1Ref, ess2Ref];
145
- }
146
-
147
- return [ess1Ref, undefined];
148
- }
451
+ return F.pipe(
452
+ RA.zip(e2s2sIdxs)(poolAssetStates),
453
+ RA.reduce<
454
+ readonly [AssetState, [FindE2S2SIdxResult, O.Option<FindE2S2SIdxResult>]],
455
+ [AssetClass, bigint][]
456
+ >([], (acc, [[poolAsset, poolAssetState], [s1Idx, s2Idx]]) => {
457
+ const s1Res = getE2s2sEntry(s1Idx, poolAssetState);
458
+
459
+ const s1 = isSameEpochToScaleKey(s1Res[0], expectedS1Key)
460
+ ? s1Res[1].sumVal
461
+ : initSumVal;
462
+
463
+ const s2 = F.pipe(
464
+ s2Idx,
465
+ O.match(
466
+ () => s1,
467
+ (s2Res) => s2Res.sumSnapshot[1].sumVal,
468
+ ),
469
+ );
470
+
471
+ const reward = calculateReward(
472
+ s1,
473
+ s2,
474
+ F.pipe(
475
+ accountAssetSums,
476
+ RA.findFirst(([accountAsset, _]) =>
477
+ isSameAssetClass(accountAsset, poolAsset),
478
+ ),
479
+ O.match(
480
+ // When account doesn't have this asset in snapshots
481
+ () => initSumVal,
482
+ (accountSumVal) => accountSumVal[1],
483
+ ),
484
+ ),
485
+ accountState,
486
+ );
149
487
 
150
- export function adjustmentHelper(
151
- spESTSTokenRef1: UTxO,
152
- spESTSTokenRef2: UTxO | undefined,
153
- pool: StabilityPoolSnapshot,
154
- e2s2s: EpochToScaleToSum,
155
- account: StabilityPoolSnapshot,
156
- ): [StabilityPoolSnapshot, bigint, UTxO[]] {
157
- let accumulatedEpochToScaleToSum: EpochToScaleToSum;
158
- let refInputs: UTxO[];
159
-
160
- const value = getSumFromEpochToScaleToSum(
161
- e2s2s,
162
- account.epoch,
163
- account.scale,
488
+ return [[poolAsset, reward], ...acc];
489
+ }),
164
490
  );
491
+ }
165
492
 
166
- if (value) {
167
- accumulatedEpochToScaleToSum = e2s2s;
168
- refInputs = [];
493
+ export function getUpdatedAccountDeposit(
494
+ poolState: StateSnapshot,
495
+ accountState: StateSnapshot,
496
+ ): SPInteger {
497
+ if (poolState.epoch > accountState.epoch) {
498
+ return mkSPInteger(0n);
499
+ } else if (poolState.scale - accountState.scale > 1) {
500
+ return mkSPInteger(0n);
501
+ } else if (poolState.scale > accountState.scale) {
502
+ return spDiv(spMul(accountState.depositVal, poolState.productVal), {
503
+ value: accountState.productVal.value * newScaleMultiplier,
504
+ });
169
505
  } else {
170
- const [ess1, ess2] = findEpochToScaleToSum(
171
- spESTSTokenRef1,
172
- spESTSTokenRef2,
506
+ return spDiv(
507
+ spMul(accountState.depositVal, poolState.productVal),
508
+ accountState.productVal,
173
509
  );
174
- if (ess2) {
175
- accumulatedEpochToScaleToSum = new Map<
176
- { epoch: bigint; scale: bigint },
177
- SPInteger
178
- >([
179
- ...Array.from(ess1.e2s2s.entries()),
180
- ...Array.from(ess2.e2s2s.entries()),
181
- ...Array.from(e2s2s.entries()),
182
- ]);
183
- refInputs = [ess1.utxo, ess2.utxo];
184
- } else {
185
- accumulatedEpochToScaleToSum = new Map<
186
- { epoch: bigint; scale: bigint },
187
- SPInteger
188
- >([...Array.from(ess1.e2s2s.entries()), ...Array.from(e2s2s.entries())]);
189
- refInputs = [ess1.utxo];
190
- }
191
510
  }
192
-
193
- const [newAccountSnapshot, accountReward] = adjust(
194
- pool,
195
- account,
196
- accumulatedEpochToScaleToSum,
197
- );
198
-
199
- return [newAccountSnapshot, accountReward, refInputs];
200
511
  }
201
512
 
202
- export function updatePoolSnapshotWithdrawalFee(
203
- withdrawalFeeAmount: SPInteger,
204
- newPoolDepositExcludingFee: SPInteger,
205
- pool: StabilityPoolSnapshot,
206
- ): [SPInteger, SPInteger] {
207
- const newPoolDepositVal = spAdd(
208
- newPoolDepositExcludingFee,
209
- withdrawalFeeAmount,
513
+ export function updateAccount(
514
+ pool: StabilityPoolContent,
515
+ account: AccountContent,
516
+ e2s2sIdxs: [FindE2S2SIdxResult, O.Option<FindE2S2SIdxResult>][],
517
+ ): {
518
+ updatedAccountContent: AccountContent;
519
+ reward: Assets;
520
+ } {
521
+ const accountState = account.state;
522
+ const fund = getUpdatedAccountDeposit(pool.state, accountState);
523
+
524
+ const rewards = rewardsPerAsset(
525
+ pool.assetStates,
526
+ e2s2sIdxs,
527
+ account.assetSums,
528
+ accountState,
210
529
  );
211
- const newPoolProduct =
212
- withdrawalFeeAmount.value === 0n
213
- ? pool.productVal
214
- : spMul(
215
- pool.productVal,
216
- spAdd(
217
- mkSPInteger(1n),
218
- spDiv(withdrawalFeeAmount, newPoolDepositExcludingFee),
219
- ),
220
- );
221
530
 
222
- return [newPoolDepositVal, newPoolProduct];
223
- }
531
+ const newDepositVal =
532
+ fund.value <
533
+ spDiv(accountState.depositVal, mkSPInteger(1_000_000_000n)).value
534
+ ? mkSPInteger(0n)
535
+ : fund;
224
536
 
537
+ return {
538
+ updatedAccountContent: {
539
+ ...account,
540
+ state: { ...pool.state, depositVal: newDepositVal },
541
+ assetSums: F.pipe(
542
+ pool.assetStates,
543
+ RA.map(([key, assetState]) => [key, assetState.currentSumVal]),
544
+ ),
545
+ },
546
+ reward: F.pipe(
547
+ rewards,
548
+ A.reduce<[AssetClass, bigint], Assets>({}, (acc, [asset, amt]) =>
549
+ addAssets(acc, mkAssetsOf(asset, amt)),
550
+ ),
551
+ ),
552
+ };
553
+ }
225
554
  export function liquidationHelper(
226
- spContent: StabilityPoolContent,
555
+ poolContent: StabilityPoolContent,
556
+ collateralAsset: AssetClass,
227
557
  iassetBurnAmt: bigint,
228
558
  /**
229
559
  * The collateral absorbed
230
560
  */
231
561
  reward: bigint,
232
- ): { newSpContent: StabilityPoolContent } {
562
+ ): StabilityPoolContent {
233
563
  const lossPerUnitStaked = spDiv(
234
564
  mkSPInteger(iassetBurnAmt),
235
- spContent.poolSnapshot.depositVal,
565
+ poolContent.state.depositVal,
236
566
  );
567
+
237
568
  const productFactor = spSub(mkSPInteger(1n), lossPerUnitStaked);
238
569
 
239
570
  const isScaleIncrease =
240
- spMul(spContent.poolSnapshot.productVal, productFactor).value <
571
+ spMul(poolContent.state.productVal, productFactor).value <
241
572
  newScaleMultiplier;
242
573
 
243
- const newSumSnapshot = spAdd(
244
- spContent.poolSnapshot.sumVal,
245
- spDiv(
246
- spMul(mkSPInteger(reward), spContent.poolSnapshot.productVal),
247
- spContent.poolSnapshot.depositVal,
248
- ),
249
- );
250
- const newProductSnapshot = spMul(
574
+ const newSnapshotP = spMul(
251
575
  {
252
576
  value:
253
- spContent.poolSnapshot.productVal.value *
577
+ poolContent.state.productVal.value *
254
578
  (isScaleIncrease ? newScaleMultiplier : 1n),
255
579
  },
256
580
  productFactor,
257
581
  );
258
582
 
259
- const isEpochIncrease = newProductSnapshot.value <= 0n;
583
+ const currentS = F.pipe(
584
+ poolContent.assetStates,
585
+ RA.findFirstMap(([ac, assetSnap]) =>
586
+ isSameAssetClass(ac, collateralAsset)
587
+ ? O.some(assetSnap.currentSumVal)
588
+ : O.none,
589
+ ),
590
+ O.getOrElse(() => initSumVal),
591
+ );
592
+
593
+ const newSnapshotS = spAdd(
594
+ currentS,
595
+ spDiv(
596
+ spMul(mkSPInteger(reward), poolContent.state.productVal),
597
+ poolContent.state.depositVal,
598
+ ),
599
+ );
600
+
601
+ const isEpochIncrease = newSnapshotP.value <= 0;
260
602
 
261
- const newSnapshot: StabilityPoolSnapshot = isEpochIncrease
262
- ? { ...initSpSnapshot, epoch: spContent.poolSnapshot.epoch + 1n }
603
+ const newState: StateSnapshot = isEpochIncrease
604
+ ? { ...initSpState, epoch: poolContent.state.epoch + 1n }
263
605
  : {
264
- productVal: newProductSnapshot,
606
+ productVal: newSnapshotP,
265
607
  depositVal: spSub(
266
- spContent.poolSnapshot.depositVal,
608
+ poolContent.state.depositVal,
267
609
  mkSPInteger(iassetBurnAmt),
268
610
  ),
269
- sumVal: newSumSnapshot,
270
- epoch: spContent.poolSnapshot.epoch,
271
- scale: spContent.poolSnapshot.scale + (isScaleIncrease ? 1n : 0n),
611
+ epoch: poolContent.state.epoch,
612
+ scale: poolContent.state.scale + (isScaleIncrease ? 1n : 0n),
272
613
  };
273
614
 
274
- const newMap = setSumInEpochToScaleToSum(
275
- spContent.epochToScaleToSum,
276
- spContent.poolSnapshot.epoch,
277
- spContent.poolSnapshot.scale,
278
- newSumSnapshot,
279
- );
615
+ const currentE2S2SKey: EpochToScaleKey = {
616
+ epoch: poolContent.state.epoch,
617
+ scale: poolContent.state.scale,
618
+ };
280
619
 
281
- const newEpochToScaleToSum = match(true)
282
- .when(
283
- () => isEpochIncrease,
284
- () =>
285
- setSumInEpochToScaleToSum(
286
- newMap,
287
- spContent.poolSnapshot.epoch + 1n,
288
- spContent.poolSnapshot.scale,
289
- { value: 0n },
290
- ),
291
- )
292
- .when(
293
- () => isScaleIncrease,
294
- () =>
295
- setSumInEpochToScaleToSum(
296
- newMap,
297
- spContent.poolSnapshot.epoch,
298
- spContent.poolSnapshot.scale + 1n,
299
- newSumSnapshot,
300
- ),
301
- )
302
- .otherwise(() => newMap);
620
+ const newCollateralAssetSumVal = isEpochIncrease ? initSumVal : newSnapshotS;
621
+
622
+ const newAssetStates = (() => {
623
+ const updatedAssetStates = F.pipe(
624
+ poolContent.assetStates,
625
+ repsertWithReadonlyArr<AssetClass, AssetSnapshot>(
626
+ (key) => isSameAssetClass(key, collateralAsset),
627
+ (assetSnap) => ({
628
+ currentSumVal: newCollateralAssetSumVal,
629
+ epoch2scale2sum: F.pipe(
630
+ assetSnap.epoch2scale2sum,
631
+ RA.modifyAt(0, ([key, val]) => [
632
+ key,
633
+ { ...val, sumVal: newSnapshotS } satisfies SumSnapshot,
634
+ ]),
635
+ O.getOrElse<readonly EpochToScaleToSumEntry[]>(() => {
636
+ throw new Error('There has to be first entry');
637
+ }),
638
+ ),
639
+ }),
640
+ () => ({
641
+ currentSumVal: newCollateralAssetSumVal,
642
+ epoch2scale2sum: [
643
+ [
644
+ currentE2S2SKey,
645
+ {
646
+ sumVal: newSnapshotS,
647
+ isFirstSnapshot: true,
648
+ isLastInEpoch: true,
649
+ },
650
+ ],
651
+ ],
652
+ }),
653
+ () => collateralAsset,
654
+ ),
655
+ );
656
+
657
+ if (isEpochIncrease) {
658
+ return F.pipe(
659
+ updatedAssetStates,
660
+ RA.map<AssetState, AssetState>(([key, assetSnap]) => [
661
+ key,
662
+ {
663
+ currentSumVal: initSumVal,
664
+ epoch2scale2sum: [
665
+ [
666
+ {
667
+ epoch: poolContent.state.epoch + 1n,
668
+ scale: 0n,
669
+ },
670
+ {
671
+ sumVal: initSumVal,
672
+ isLastInEpoch: true,
673
+ isFirstSnapshot: false,
674
+ },
675
+ ],
676
+ ...assetSnap.epoch2scale2sum,
677
+ ],
678
+ },
679
+ ]),
680
+ );
681
+ } else if (isScaleIncrease) {
682
+ return F.pipe(
683
+ updatedAssetStates,
684
+ RA.map<AssetState, AssetState>(([key, assetSnap]) => [
685
+ key,
686
+ {
687
+ ...assetSnap,
688
+ epoch2scale2sum: match(assetSnap.epoch2scale2sum)
689
+ .returnType<readonly EpochToScaleToSumEntry[]>()
690
+ .with([[P.any, P.any], ...P.array()], ([[key, val], ...rest]) => {
691
+ const newScaleEntry: EpochToScaleToSumEntry = [
692
+ {
693
+ epoch: poolContent.state.epoch,
694
+ scale: poolContent.state.scale + 1n,
695
+ },
696
+ {
697
+ sumVal: val.sumVal,
698
+ isLastInEpoch: true,
699
+ isFirstSnapshot: false,
700
+ },
701
+ ];
702
+
703
+ return [
704
+ newScaleEntry,
705
+ [key, { ...val, isLastInEpoch: false } satisfies SumSnapshot],
706
+ ...rest,
707
+ ];
708
+ })
709
+ .otherwise(() => {
710
+ throw new Error('There has to be at least 1 entry');
711
+ }),
712
+ },
713
+ ]),
714
+ );
715
+ } else {
716
+ return updatedAssetStates;
717
+ }
718
+ })();
303
719
 
304
720
  return {
305
- newSpContent: {
306
- asset: spContent.asset,
307
- epochToScaleToSum: newEpochToScaleToSum,
308
- poolSnapshot: newSnapshot,
309
- },
721
+ ...poolContent,
722
+ assetStates: newAssetStates,
723
+ state: newState,
310
724
  };
311
725
  }
726
+
727
+ export function updatePoolStateWhenWithdrawalFee(
728
+ withdrawalFeeAmt: bigint,
729
+ updatedPoolState: StateSnapshot,
730
+ ): StateSnapshot {
731
+ if (withdrawalFeeAmt === 0n) {
732
+ return updatedPoolState;
733
+ } else {
734
+ const withdrawalFeeSpInt = mkSPInteger(withdrawalFeeAmt);
735
+ const newDepositVal = spAdd(
736
+ updatedPoolState.depositVal,
737
+ withdrawalFeeSpInt,
738
+ );
739
+
740
+ const productFactor = spAdd(
741
+ mkSPInteger(1n),
742
+ spDiv(withdrawalFeeSpInt, updatedPoolState.depositVal),
743
+ );
744
+
745
+ const newProductVal = spMul(updatedPoolState.productVal, productFactor);
746
+
747
+ return {
748
+ ...updatedPoolState,
749
+ productVal: newProductVal,
750
+ depositVal: newDepositVal,
751
+ };
752
+ }
753
+ }
754
+
755
+ export function partitionEpochToScaleToSums(
756
+ spContent: StabilityPoolContent,
757
+ ): readonly [
758
+ readonly SnapshotEpochToScaleToSumContent[],
759
+ readonly AssetState[],
760
+ ] {
761
+ const res = F.pipe(
762
+ spContent.assetStates,
763
+ RA.map<AssetState, [SnapshotEpochToScaleToSumContent[], AssetState]>(
764
+ ([collateralAsset, assetState]) => {
765
+ if (assetState.epoch2scale2sum.length >= MAX_E2S2S_ENTRIES_COUNT) {
766
+ const { right: remaining, left: snapshotMapItems } = F.pipe(
767
+ assetState.epoch2scale2sum,
768
+ RA.partition(
769
+ ([e2sKey, _]) =>
770
+ e2sKey.epoch === spContent.state.epoch &&
771
+ e2sKey.scale === spContent.state.scale,
772
+ ),
773
+ );
774
+
775
+ return [
776
+ [
777
+ {
778
+ iasset: spContent.iasset,
779
+ collateralAsset: collateralAsset,
780
+ snapshot: snapshotMapItems,
781
+ },
782
+ ],
783
+ [collateralAsset, { ...assetState, epoch2scale2sum: remaining }],
784
+ ];
785
+ } else {
786
+ return [[], [collateralAsset, assetState]];
787
+ }
788
+ },
789
+ ),
790
+ );
791
+
792
+ const [newSnapshots, newAssetStates] = RA.unzip(res);
793
+
794
+ return [RA.flatten(newSnapshots), newAssetStates];
795
+ }