@kehtus/proportion-sdk 0.1.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 (56) hide show
  1. package/SDK_IMPROVEMENT_PLAN.md +197 -0
  2. package/arch.md +448 -0
  3. package/dist/__tests__/account.test.d.ts +1 -0
  4. package/dist/__tests__/account.test.js +36 -0
  5. package/dist/__tests__/indexer.test.d.ts +1 -0
  6. package/dist/__tests__/indexer.test.js +230 -0
  7. package/dist/__tests__/lifecycle.test.d.ts +1 -0
  8. package/dist/__tests__/lifecycle.test.js +186 -0
  9. package/dist/__tests__/reads.test.d.ts +1 -0
  10. package/dist/__tests__/reads.test.js +185 -0
  11. package/dist/__tests__/utils.test.d.ts +1 -0
  12. package/dist/__tests__/utils.test.js +185 -0
  13. package/dist/abi/factory.d.ts +308 -0
  14. package/dist/abi/factory.js +399 -0
  15. package/dist/abi/fundedAccount.d.ts +1074 -0
  16. package/dist/abi/fundedAccount.js +1373 -0
  17. package/dist/abi/mainVault.d.ts +1448 -0
  18. package/dist/abi/mainVault.js +1865 -0
  19. package/dist/account.d.ts +55 -0
  20. package/dist/account.js +290 -0
  21. package/dist/constants.d.ts +48 -0
  22. package/dist/constants.js +42 -0
  23. package/dist/factory.d.ts +23 -0
  24. package/dist/factory.js +139 -0
  25. package/dist/index.d.ts +11 -0
  26. package/dist/index.js +14 -0
  27. package/dist/indexer.d.ts +57 -0
  28. package/dist/indexer.js +540 -0
  29. package/dist/sdk.d.ts +22 -0
  30. package/dist/sdk.js +89 -0
  31. package/dist/types.d.ts +384 -0
  32. package/dist/types.js +11 -0
  33. package/dist/utils.d.ts +21 -0
  34. package/dist/utils.js +89 -0
  35. package/dist/vault.d.ts +60 -0
  36. package/dist/vault.js +429 -0
  37. package/package.json +32 -0
  38. package/src/__tests__/account.test.ts +40 -0
  39. package/src/__tests__/indexer.test.ts +255 -0
  40. package/src/__tests__/lifecycle.test.ts +231 -0
  41. package/src/__tests__/reads.test.ts +209 -0
  42. package/src/__tests__/utils.test.ts +245 -0
  43. package/src/abi/factory.ts +399 -0
  44. package/src/abi/fundedAccount.ts +1373 -0
  45. package/src/abi/mainVault.ts +1865 -0
  46. package/src/account.ts +339 -0
  47. package/src/constants.ts +50 -0
  48. package/src/factory.ts +157 -0
  49. package/src/index.ts +16 -0
  50. package/src/indexer.ts +636 -0
  51. package/src/sdk.ts +93 -0
  52. package/src/types.ts +426 -0
  53. package/src/utils.ts +143 -0
  54. package/src/vault.ts +479 -0
  55. package/tsconfig.json +19 -0
  56. package/vitest.config.ts +8 -0
@@ -0,0 +1,185 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { activationFee, subscriptionFee, splitProfit, trailingFloor, dailyFloor, isTrailingBreached, isDailyBreached, fromUsdc, toUsdc, from8Dec, to8Dec, toUsdc6, formatUsd, stateToLabel, labelToState, } from "../utils.js";
3
+ import { AccountState } from "../types.js";
4
+ // ── Helpers ──────────────────────────────────────────
5
+ const usdc = (n) => BigInt(n) * 1000000n; // 6-dec USDC
6
+ const dec8 = (n) => BigInt(n) * 100000000n; // 8-dec internal
7
+ // ── Decimal conversions ──────────────────────────────
8
+ describe("decimal conversions", () => {
9
+ it("toUsdc converts number to 6-dec bigint", () => {
10
+ expect(toUsdc(100)).toBe(100000000n);
11
+ expect(toUsdc(0.5)).toBe(500000n);
12
+ expect(toUsdc(5000)).toBe(5000000000n);
13
+ });
14
+ it("fromUsdc converts 6-dec bigint to number", () => {
15
+ expect(fromUsdc(100000000n)).toBe(100);
16
+ expect(fromUsdc(500000n)).toBe(0.5);
17
+ });
18
+ it("to8Dec scales 6-dec to 8-dec (*100)", () => {
19
+ expect(to8Dec(usdc(1000))).toBe(dec8(1000));
20
+ });
21
+ it("toUsdc6 scales 8-dec down to 6-dec (/100)", () => {
22
+ expect(toUsdc6(dec8(1000))).toBe(usdc(1000));
23
+ });
24
+ it("from8Dec converts 8-dec bigint to number", () => {
25
+ expect(from8Dec(dec8(5000))).toBe(5000);
26
+ expect(from8Dec(50000000n)).toBe(0.5);
27
+ });
28
+ it("roundtrip: toUsdc -> to8Dec -> toUsdc6", () => {
29
+ const original = toUsdc(1234);
30
+ const up = to8Dec(original);
31
+ const down = toUsdc6(up);
32
+ expect(down).toBe(original);
33
+ });
34
+ });
35
+ // ── Activation fee ───────────────────────────────────
36
+ describe("activationFee", () => {
37
+ const FM = 120; // current contract feeMultiplier
38
+ it("$5000 account, 500bps, no daily DD → $300", () => {
39
+ // 5000 * 500 * 120 / 1_000_000 = 300
40
+ expect(activationFee(usdc(5000), 500, false, FM)).toBe(usdc(300));
41
+ });
42
+ it("$5000 account, 500bps, daily DD → $240", () => {
43
+ // 300 * 8000 / 10000 = 240 (20% discount)
44
+ expect(activationFee(usdc(5000), 500, true, FM)).toBe(usdc(240));
45
+ });
46
+ it("$10000 account, 1000bps, no daily DD → $1200", () => {
47
+ // 10000 * 1000 * 120 / 1_000_000 = 1200
48
+ expect(activationFee(usdc(10000), 1000, false, FM)).toBe(usdc(1200));
49
+ });
50
+ it("$10000 account, 1000bps, daily DD → $960", () => {
51
+ // 1200 * 8000 / 10000 = 960 (20% discount)
52
+ expect(activationFee(usdc(10000), 1000, true, FM)).toBe(usdc(960));
53
+ });
54
+ it("min params: $100 account, 500bps → $6", () => {
55
+ // 100 * 500 * 120 / 1_000_000 = 6
56
+ const fee = activationFee(usdc(100), 500, false, FM);
57
+ expect(fee).toBe(usdc(6));
58
+ });
59
+ });
60
+ // ── Subscription fee ─────────────────────────────────
61
+ describe("subscriptionFee", () => {
62
+ const SFM = 150; // current contract subscriptionFeeMultiplier
63
+ it("$5000 account → $75/month", () => {
64
+ // 5000 * 150 / 10000 = 75
65
+ expect(subscriptionFee(usdc(5000), SFM)).toBe(usdc(75));
66
+ });
67
+ it("legacy signature is still accepted for compatibility", () => {
68
+ expect(subscriptionFee(usdc(5000), 500, true, SFM)).toBe(usdc(75));
69
+ });
70
+ it("$10000 account → $150/month", () => {
71
+ expect(subscriptionFee(usdc(10000), SFM)).toBe(usdc(150));
72
+ });
73
+ });
74
+ // ── Profit split ─────────────────────────────────────
75
+ describe("splitProfit", () => {
76
+ it("$1000 profit → 80% owner, 7% protocol, 13% vault", () => {
77
+ const { owner, vault, protocol } = splitProfit(usdc(1000));
78
+ expect(owner).toBe(usdc(800));
79
+ expect(protocol).toBe(usdc(70));
80
+ expect(vault).toBe(usdc(130));
81
+ });
82
+ it("sum of parts equals total", () => {
83
+ const profit = usdc(7777);
84
+ const { owner, vault, protocol } = splitProfit(profit);
85
+ expect(owner + vault + protocol).toBe(profit);
86
+ });
87
+ it("zero profit", () => {
88
+ const { owner, vault, protocol } = splitProfit(0n);
89
+ expect(owner).toBe(0n);
90
+ expect(vault).toBe(0n);
91
+ expect(protocol).toBe(0n);
92
+ });
93
+ it("small profit — rounding goes to vault (remainder)", () => {
94
+ // $1 = 1_000_000
95
+ // owner = 1_000_000 * 8000 / 10000 = 800_000
96
+ // protocol = 1_000_000 * 700 / 10000 = 70_000
97
+ // vault = 1_000_000 - 800_000 - 70_000 = 130_000
98
+ const { owner, vault, protocol } = splitProfit(usdc(1));
99
+ expect(owner).toBe(800000n);
100
+ expect(protocol).toBe(70000n);
101
+ expect(vault).toBe(130000n);
102
+ });
103
+ });
104
+ // ── Drawdown floors ──────────────────────────────────
105
+ describe("trailingFloor", () => {
106
+ it("500bps = 5% drawdown from HWM", () => {
107
+ const hwm = dec8(10000); // $10,000 in 8-dec
108
+ expect(trailingFloor(hwm, 500)).toBe(dec8(9500));
109
+ });
110
+ it("1000bps = 10% drawdown", () => {
111
+ const hwm = dec8(5000);
112
+ expect(trailingFloor(hwm, 1000)).toBe(dec8(4500));
113
+ });
114
+ });
115
+ describe("dailyFloor", () => {
116
+ it("500bps → daily = 250bps = 2.5% from day start", () => {
117
+ const dsv = dec8(10000);
118
+ // dailyBps = 500 / 2 = 250
119
+ // floor = 10000 - 10000 * 250 / 10000 = 10000 - 250 = 9750
120
+ expect(dailyFloor(dsv, 500)).toBe(dec8(9750));
121
+ });
122
+ it("1000bps → daily = 500bps = 5%", () => {
123
+ const dsv = dec8(10000);
124
+ expect(dailyFloor(dsv, 1000)).toBe(dec8(9500));
125
+ });
126
+ });
127
+ describe("breach checks", () => {
128
+ const hwm = dec8(10000);
129
+ const dsv = dec8(10000);
130
+ it("isTrailingBreached — below floor → true", () => {
131
+ expect(isTrailingBreached(dec8(9499), hwm, 500)).toBe(true);
132
+ });
133
+ it("isTrailingBreached — at floor → false", () => {
134
+ expect(isTrailingBreached(dec8(9500), hwm, 500)).toBe(false);
135
+ });
136
+ it("isTrailingBreached — above floor → false", () => {
137
+ expect(isTrailingBreached(dec8(9501), hwm, 500)).toBe(false);
138
+ });
139
+ it("isDailyBreached — below floor → true", () => {
140
+ expect(isDailyBreached(dec8(9749), dsv, 500)).toBe(true);
141
+ });
142
+ it("isDailyBreached — at floor → false", () => {
143
+ expect(isDailyBreached(dec8(9750), dsv, 500)).toBe(false);
144
+ });
145
+ });
146
+ // ── Enum mapping ─────────────────────────────────────
147
+ describe("state enum mapping", () => {
148
+ it("stateToLabel maps all states", () => {
149
+ expect(stateToLabel(AccountState.Uninitialized)).toBe("UNINITIALIZED");
150
+ expect(stateToLabel(AccountState.Active)).toBe("ACTIVE");
151
+ expect(stateToLabel(AccountState.Closing)).toBe("CLOSING");
152
+ expect(stateToLabel(AccountState.Closed)).toBe("CLOSED");
153
+ expect(stateToLabel(AccountState.WithdrawalPending)).toBe("WITHDRAWAL_PENDING");
154
+ expect(stateToLabel(AccountState.Returning)).toBe("RETURNING");
155
+ expect(stateToLabel(AccountState.Initialized)).toBe("INITIALIZED");
156
+ });
157
+ it("labelToState reverses correctly", () => {
158
+ expect(labelToState("UNINITIALIZED")).toBe(AccountState.Uninitialized);
159
+ expect(labelToState("ACTIVE")).toBe(AccountState.Active);
160
+ expect(labelToState("CLOSED")).toBe(AccountState.Closed);
161
+ expect(labelToState("CLOSING")).toBe(AccountState.Closing);
162
+ });
163
+ it("roundtrip: state → label → state", () => {
164
+ for (const state of [
165
+ AccountState.Initialized,
166
+ AccountState.Active,
167
+ AccountState.WithdrawalPending,
168
+ AccountState.Closing,
169
+ AccountState.Returning,
170
+ AccountState.Closed,
171
+ ]) {
172
+ expect(labelToState(stateToLabel(state))).toBe(state);
173
+ }
174
+ });
175
+ });
176
+ // ── formatUsd ────────────────────────────────────────
177
+ describe("formatUsd", () => {
178
+ it("formats 6-dec bigint as USD", () => {
179
+ expect(formatUsd(usdc(1000))).toBe("$1,000.00");
180
+ expect(formatUsd(usdc(0))).toBe("$0.00");
181
+ });
182
+ it("formats with custom decimals", () => {
183
+ expect(formatUsd(dec8(5000), 8)).toBe("$5,000.00");
184
+ });
185
+ });
@@ -0,0 +1,308 @@
1
+ export declare const factoryAbi: readonly [{
2
+ readonly inputs: readonly [{
3
+ readonly internalType: "address";
4
+ readonly name: "_usdc";
5
+ readonly type: "address";
6
+ }, {
7
+ readonly internalType: "address";
8
+ readonly name: "_implementation";
9
+ readonly type: "address";
10
+ }];
11
+ readonly stateMutability: "nonpayable";
12
+ readonly type: "constructor";
13
+ }, {
14
+ readonly inputs: readonly [];
15
+ readonly name: "FailedDeployment";
16
+ readonly type: "error";
17
+ }, {
18
+ readonly inputs: readonly [{
19
+ readonly internalType: "uint256";
20
+ readonly name: "balance";
21
+ readonly type: "uint256";
22
+ }, {
23
+ readonly internalType: "uint256";
24
+ readonly name: "needed";
25
+ readonly type: "uint256";
26
+ }];
27
+ readonly name: "InsufficientBalance";
28
+ readonly type: "error";
29
+ }, {
30
+ readonly anonymous: false;
31
+ readonly inputs: readonly [{
32
+ readonly indexed: true;
33
+ readonly internalType: "address";
34
+ readonly name: "vault";
35
+ readonly type: "address";
36
+ }, {
37
+ readonly indexed: true;
38
+ readonly internalType: "address";
39
+ readonly name: "owner";
40
+ readonly type: "address";
41
+ }];
42
+ readonly name: "FactoryConfigInitialized";
43
+ readonly type: "event";
44
+ }, {
45
+ readonly anonymous: false;
46
+ readonly inputs: readonly [{
47
+ readonly indexed: true;
48
+ readonly internalType: "address";
49
+ readonly name: "account";
50
+ readonly type: "address";
51
+ }, {
52
+ readonly indexed: true;
53
+ readonly internalType: "address";
54
+ readonly name: "owner";
55
+ readonly type: "address";
56
+ }, {
57
+ readonly indexed: true;
58
+ readonly internalType: "address";
59
+ readonly name: "builder";
60
+ readonly type: "address";
61
+ }, {
62
+ readonly indexed: false;
63
+ readonly internalType: "uint96";
64
+ readonly name: "accountSize";
65
+ readonly type: "uint96";
66
+ }, {
67
+ readonly indexed: false;
68
+ readonly internalType: "uint16";
69
+ readonly name: "drawdownBps";
70
+ readonly type: "uint16";
71
+ }, {
72
+ readonly indexed: false;
73
+ readonly internalType: "bool";
74
+ readonly name: "dailyDrawdownEnabled";
75
+ readonly type: "bool";
76
+ }];
77
+ readonly name: "FundedAccountCreated";
78
+ readonly type: "event";
79
+ }, {
80
+ readonly anonymous: false;
81
+ readonly inputs: readonly [{
82
+ readonly indexed: true;
83
+ readonly internalType: "address";
84
+ readonly name: "previousOwner";
85
+ readonly type: "address";
86
+ }, {
87
+ readonly indexed: true;
88
+ readonly internalType: "address";
89
+ readonly name: "newOwner";
90
+ readonly type: "address";
91
+ }];
92
+ readonly name: "OwnershipTransferStarted";
93
+ readonly type: "event";
94
+ }, {
95
+ readonly anonymous: false;
96
+ readonly inputs: readonly [{
97
+ readonly indexed: true;
98
+ readonly internalType: "address";
99
+ readonly name: "oldOwner";
100
+ readonly type: "address";
101
+ }, {
102
+ readonly indexed: true;
103
+ readonly internalType: "address";
104
+ readonly name: "newOwner";
105
+ readonly type: "address";
106
+ }];
107
+ readonly name: "OwnershipTransferred";
108
+ readonly type: "event";
109
+ }, {
110
+ readonly anonymous: false;
111
+ readonly inputs: readonly [{
112
+ readonly indexed: true;
113
+ readonly internalType: "address";
114
+ readonly name: "oldVault";
115
+ readonly type: "address";
116
+ }, {
117
+ readonly indexed: true;
118
+ readonly internalType: "address";
119
+ readonly name: "newVault";
120
+ readonly type: "address";
121
+ }];
122
+ readonly name: "VaultUpdated";
123
+ readonly type: "event";
124
+ }, {
125
+ readonly inputs: readonly [];
126
+ readonly name: "acceptOwnership";
127
+ readonly outputs: readonly [];
128
+ readonly stateMutability: "nonpayable";
129
+ readonly type: "function";
130
+ }, {
131
+ readonly inputs: readonly [{
132
+ readonly internalType: "uint256";
133
+ readonly name: "";
134
+ readonly type: "uint256";
135
+ }];
136
+ readonly name: "allAccounts";
137
+ readonly outputs: readonly [{
138
+ readonly internalType: "address";
139
+ readonly name: "";
140
+ readonly type: "address";
141
+ }];
142
+ readonly stateMutability: "view";
143
+ readonly type: "function";
144
+ }, {
145
+ readonly inputs: readonly [{
146
+ readonly internalType: "address";
147
+ readonly name: "";
148
+ readonly type: "address";
149
+ }];
150
+ readonly name: "allAccountsIndex";
151
+ readonly outputs: readonly [{
152
+ readonly internalType: "uint256";
153
+ readonly name: "";
154
+ readonly type: "uint256";
155
+ }];
156
+ readonly stateMutability: "view";
157
+ readonly type: "function";
158
+ }, {
159
+ readonly inputs: readonly [{
160
+ readonly internalType: "address";
161
+ readonly name: "accountOwner";
162
+ readonly type: "address";
163
+ }, {
164
+ readonly internalType: "address";
165
+ readonly name: "builder";
166
+ readonly type: "address";
167
+ }, {
168
+ readonly internalType: "uint96";
169
+ readonly name: "accountSize";
170
+ readonly type: "uint96";
171
+ }, {
172
+ readonly internalType: "uint16";
173
+ readonly name: "drawdownBps";
174
+ readonly type: "uint16";
175
+ }, {
176
+ readonly internalType: "bool";
177
+ readonly name: "dailyDrawdownEnabled";
178
+ readonly type: "bool";
179
+ }, {
180
+ readonly internalType: "uint16";
181
+ readonly name: "subscriptionFeeMultiplier";
182
+ readonly type: "uint16";
183
+ }];
184
+ readonly name: "createFundedAccount";
185
+ readonly outputs: readonly [{
186
+ readonly internalType: "address";
187
+ readonly name: "";
188
+ readonly type: "address";
189
+ }];
190
+ readonly stateMutability: "nonpayable";
191
+ readonly type: "function";
192
+ }, {
193
+ readonly inputs: readonly [];
194
+ readonly name: "implementation";
195
+ readonly outputs: readonly [{
196
+ readonly internalType: "address";
197
+ readonly name: "";
198
+ readonly type: "address";
199
+ }];
200
+ readonly stateMutability: "view";
201
+ readonly type: "function";
202
+ }, {
203
+ readonly inputs: readonly [];
204
+ readonly name: "owner";
205
+ readonly outputs: readonly [{
206
+ readonly internalType: "address";
207
+ readonly name: "";
208
+ readonly type: "address";
209
+ }];
210
+ readonly stateMutability: "view";
211
+ readonly type: "function";
212
+ }, {
213
+ readonly inputs: readonly [];
214
+ readonly name: "pendingOwner";
215
+ readonly outputs: readonly [{
216
+ readonly internalType: "address";
217
+ readonly name: "";
218
+ readonly type: "address";
219
+ }];
220
+ readonly stateMutability: "view";
221
+ readonly type: "function";
222
+ }, {
223
+ readonly inputs: readonly [{
224
+ readonly internalType: "address";
225
+ readonly name: "accountOwner";
226
+ readonly type: "address";
227
+ }, {
228
+ readonly internalType: "address";
229
+ readonly name: "builder";
230
+ readonly type: "address";
231
+ }];
232
+ readonly name: "predictFundedAccountAddress";
233
+ readonly outputs: readonly [{
234
+ readonly internalType: "address";
235
+ readonly name: "";
236
+ readonly type: "address";
237
+ }];
238
+ readonly stateMutability: "view";
239
+ readonly type: "function";
240
+ }, {
241
+ readonly inputs: readonly [{
242
+ readonly internalType: "address";
243
+ readonly name: "account";
244
+ readonly type: "address";
245
+ }];
246
+ readonly name: "removeAccount";
247
+ readonly outputs: readonly [];
248
+ readonly stateMutability: "nonpayable";
249
+ readonly type: "function";
250
+ }, {
251
+ readonly inputs: readonly [{
252
+ readonly internalType: "address";
253
+ readonly name: "_vault";
254
+ readonly type: "address";
255
+ }];
256
+ readonly name: "setVault";
257
+ readonly outputs: readonly [];
258
+ readonly stateMutability: "nonpayable";
259
+ readonly type: "function";
260
+ }, {
261
+ readonly inputs: readonly [{
262
+ readonly internalType: "address";
263
+ readonly name: "";
264
+ readonly type: "address";
265
+ }, {
266
+ readonly internalType: "address";
267
+ readonly name: "";
268
+ readonly type: "address";
269
+ }];
270
+ readonly name: "traderBuilderNonce";
271
+ readonly outputs: readonly [{
272
+ readonly internalType: "uint256";
273
+ readonly name: "";
274
+ readonly type: "uint256";
275
+ }];
276
+ readonly stateMutability: "view";
277
+ readonly type: "function";
278
+ }, {
279
+ readonly inputs: readonly [{
280
+ readonly internalType: "address";
281
+ readonly name: "newOwner";
282
+ readonly type: "address";
283
+ }];
284
+ readonly name: "transferOwnership";
285
+ readonly outputs: readonly [];
286
+ readonly stateMutability: "nonpayable";
287
+ readonly type: "function";
288
+ }, {
289
+ readonly inputs: readonly [];
290
+ readonly name: "usdc";
291
+ readonly outputs: readonly [{
292
+ readonly internalType: "address";
293
+ readonly name: "";
294
+ readonly type: "address";
295
+ }];
296
+ readonly stateMutability: "view";
297
+ readonly type: "function";
298
+ }, {
299
+ readonly inputs: readonly [];
300
+ readonly name: "vault";
301
+ readonly outputs: readonly [{
302
+ readonly internalType: "address";
303
+ readonly name: "";
304
+ readonly type: "address";
305
+ }];
306
+ readonly stateMutability: "view";
307
+ readonly type: "function";
308
+ }];