@kasufinance/kasu-sdk 2.5.0 → 2.7.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 (128) hide show
  1. package/README.md +144 -7
  2. package/dist/bundle.cjs.js +1667 -21
  3. package/dist/bundle.esm.js +1630 -22
  4. package/dist/domain/au-minimum.d.ts +135 -0
  5. package/dist/domain/au-minimum.js +154 -0
  6. package/dist/domain/au-minimum.js.map +1 -0
  7. package/dist/domain/index.d.ts +18 -3
  8. package/dist/domain/index.js +13 -3
  9. package/dist/domain/index.js.map +1 -1
  10. package/dist/domain/loan-contract.d.ts +174 -0
  11. package/dist/domain/loan-contract.js +160 -0
  12. package/dist/domain/loan-contract.js.map +1 -0
  13. package/dist/domain/requests.d.ts +219 -0
  14. package/dist/domain/requests.js +218 -0
  15. package/dist/domain/requests.js.map +1 -0
  16. package/dist/domain/settlement.d.ts +97 -0
  17. package/dist/domain/settlement.js +117 -0
  18. package/dist/domain/settlement.js.map +1 -0
  19. package/dist/domain/wallet-errors.d.ts +92 -0
  20. package/dist/domain/wallet-errors.js +155 -0
  21. package/dist/domain/wallet-errors.js.map +1 -0
  22. package/dist/facade/chain-configs.js +7 -1
  23. package/dist/facade/chain-configs.js.map +1 -1
  24. package/dist/facade/deposits.js +2 -1
  25. package/dist/facade/deposits.js.map +1 -1
  26. package/dist/facade/flows.d.ts +95 -0
  27. package/dist/facade/flows.js +116 -0
  28. package/dist/facade/flows.js.map +1 -0
  29. package/dist/facade/index.d.ts +3 -0
  30. package/dist/facade/index.js +3 -0
  31. package/dist/facade/index.js.map +1 -1
  32. package/dist/facade/kasu.d.ts +6 -2
  33. package/dist/facade/kasu.js +28 -3
  34. package/dist/facade/kasu.js.map +1 -1
  35. package/dist/facade/read-only.d.ts +12 -0
  36. package/dist/facade/read-only.js +13 -0
  37. package/dist/facade/read-only.js.map +1 -0
  38. package/dist/facade/user-portfolio.d.ts +18 -0
  39. package/dist/facade/user-portfolio.js +23 -0
  40. package/dist/facade/user-portfolio.js.map +1 -1
  41. package/dist/flows/deposit-flow.d.ts +301 -0
  42. package/dist/flows/deposit-flow.js +358 -0
  43. package/dist/flows/deposit-flow.js.map +1 -0
  44. package/dist/flows/flow.d.ts +72 -0
  45. package/dist/flows/flow.js +110 -0
  46. package/dist/flows/flow.js.map +1 -0
  47. package/dist/flows/index.d.ts +19 -0
  48. package/dist/flows/index.js +17 -0
  49. package/dist/flows/index.js.map +1 -0
  50. package/dist/flows/observable.d.ts +59 -0
  51. package/dist/flows/observable.js +81 -0
  52. package/dist/flows/observable.js.map +1 -0
  53. package/dist/flows/withdraw-flow.d.ts +99 -0
  54. package/dist/flows/withdraw-flow.js +83 -0
  55. package/dist/flows/withdraw-flow.js.map +1 -0
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.js +7 -0
  58. package/dist/index.js.map +1 -1
  59. package/dist/services/DataService/data-service.js +3 -11
  60. package/dist/services/DataService/data-service.js.map +1 -1
  61. package/dist/services/DataService/directus-client.d.ts +26 -0
  62. package/dist/services/DataService/directus-client.js +38 -0
  63. package/dist/services/DataService/directus-client.js.map +1 -0
  64. package/dist/services/UserLending/user-lending.js +11 -7
  65. package/dist/services/UserLending/user-lending.js.map +1 -1
  66. package/package.json +8 -2
  67. package/src/domain/au-minimum.ts +192 -0
  68. package/src/domain/index.ts +73 -3
  69. package/src/domain/loan-contract.ts +275 -0
  70. package/src/domain/requests.ts +467 -0
  71. package/src/domain/settlement.ts +161 -0
  72. package/src/domain/wallet-errors.ts +173 -0
  73. package/src/facade/chain-configs.ts +7 -1
  74. package/src/facade/deposits.ts +2 -3
  75. package/src/facade/flows.ts +172 -0
  76. package/src/facade/index.ts +8 -0
  77. package/src/facade/kasu.ts +43 -3
  78. package/src/facade/read-only.ts +13 -0
  79. package/src/facade/user-portfolio.ts +24 -0
  80. package/src/flows/deposit-flow.ts +775 -0
  81. package/src/flows/flow.ts +108 -0
  82. package/src/flows/index.ts +45 -0
  83. package/src/flows/observable.ts +97 -0
  84. package/src/flows/withdraw-flow.ts +210 -0
  85. package/src/index.ts +9 -0
  86. package/src/services/DataService/data-service.ts +7 -25
  87. package/src/services/DataService/directus-client.ts +54 -0
  88. package/src/services/UserLending/user-lending.ts +17 -21
  89. package/dist/domain/deposit-bounds.test.d.ts +0 -1
  90. package/dist/domain/deposit-bounds.test.js +0 -135
  91. package/dist/domain/deposit-bounds.test.js.map +0 -1
  92. package/dist/domain/partners.test.d.ts +0 -1
  93. package/dist/domain/partners.test.js +0 -53
  94. package/dist/domain/partners.test.js.map +0 -1
  95. package/dist/domain/pools.test.d.ts +0 -1
  96. package/dist/domain/pools.test.js +0 -184
  97. package/dist/domain/pools.test.js.map +0 -1
  98. package/dist/domain/rates.test.d.ts +0 -1
  99. package/dist/domain/rates.test.js +0 -181
  100. package/dist/domain/rates.test.js.map +0 -1
  101. package/dist/domain/tranche-display-name.test.d.ts +0 -1
  102. package/dist/domain/tranche-display-name.test.js +0 -58
  103. package/dist/domain/tranche-display-name.test.js.map +0 -1
  104. package/dist/domain/tranches.test.d.ts +0 -1
  105. package/dist/domain/tranches.test.js +0 -206
  106. package/dist/domain/tranches.test.js.map +0 -1
  107. package/dist/facade/config.test.d.ts +0 -1
  108. package/dist/facade/config.test.js +0 -216
  109. package/dist/facade/config.test.js.map +0 -1
  110. package/dist/facade/facade.test.d.ts +0 -1
  111. package/dist/facade/facade.test.js +0 -201
  112. package/dist/facade/facade.test.js.map +0 -1
  113. package/dist/services/Locking/calculate-apy.test.d.ts +0 -1
  114. package/dist/services/Locking/calculate-apy.test.js +0 -41
  115. package/dist/services/Locking/calculate-apy.test.js.map +0 -1
  116. package/dist/tests/sample.test.d.ts +0 -1
  117. package/dist/tests/sample.test.js +0 -59
  118. package/dist/tests/sample.test.js.map +0 -1
  119. package/src/domain/deposit-bounds.test.ts +0 -200
  120. package/src/domain/partners.test.ts +0 -83
  121. package/src/domain/pools.test.ts +0 -260
  122. package/src/domain/rates.test.ts +0 -254
  123. package/src/domain/tranche-display-name.test.ts +0 -96
  124. package/src/domain/tranches.test.ts +0 -300
  125. package/src/facade/config.test.ts +0 -265
  126. package/src/facade/facade.test.ts +0 -250
  127. package/src/services/Locking/calculate-apy.test.ts +0 -52
  128. package/src/tests/sample.test.ts +0 -63
@@ -1,250 +0,0 @@
1
- /**
2
- * Offline facade specs — a mock provider and pure helpers, no network. Moved
3
- * here from `src/tests/` so it runs beside the code it covers, and so the
4
- * opt-in `LIVE_TESTS` gate on `src/tests/` does not take it out of CI.
5
- */
6
- import { CHAIN_CONFIGS } from './chain-configs';
7
- import { DepositsFacade } from './deposits';
8
- import { Kasu } from './kasu';
9
- import { StrategiesFacade } from './strategies';
10
- import { PortfolioFacade } from './user-portfolio';
11
-
12
- // ---------------------------------------------------------------------------
13
- // Chain configs
14
- // ---------------------------------------------------------------------------
15
-
16
- describe('CHAIN_CONFIGS', () => {
17
- it('should have base, xdc, and plume entries', () => {
18
- expect(CHAIN_CONFIGS).toHaveProperty('base');
19
- expect(CHAIN_CONFIGS).toHaveProperty('xdc');
20
- expect(CHAIN_CONFIGS).toHaveProperty('plume');
21
- });
22
-
23
- it('base should be a full deployment', () => {
24
- const base = CHAIN_CONFIGS.base;
25
- expect(base.chainId).toBe(8453);
26
- expect(base.isLiteDeployment).toBe(false);
27
- expect(base.contracts.KSUToken).toBeDefined();
28
- expect(base.contracts.KasuNFTs).toBeDefined();
29
- });
30
-
31
- it('xdc should be a lite deployment without KSU token', () => {
32
- const xdc = CHAIN_CONFIGS.xdc;
33
- expect(xdc.chainId).toBe(50);
34
- expect(xdc.isLiteDeployment).toBe(true);
35
- expect(xdc.contracts.KSUToken).toBeUndefined();
36
- expect(xdc.contracts.KasuNFTs).toBeUndefined();
37
- });
38
-
39
- it('plume should be a lite deployment', () => {
40
- const plume = CHAIN_CONFIGS.plume;
41
- expect(plume.chainId).toBe(98866);
42
- expect(plume.isLiteDeployment).toBe(true);
43
- });
44
-
45
- it('all chains should have required contract addresses', () => {
46
- for (const [, config] of Object.entries(CHAIN_CONFIGS)) {
47
- expect(config.contracts.LendingPoolManager).toBeDefined();
48
- expect(config.contracts.UserManager).toBeDefined();
49
- expect(config.contracts.KasuAllowList).toBeDefined();
50
- expect(config.contracts.SystemVariables).toBeDefined();
51
- expect(config.subgraphUrl).toContain('goldsky.com');
52
- }
53
- });
54
-
55
- it('xdc should have poolMetadataMapping', () => {
56
- const mapping = CHAIN_CONFIGS.xdc.poolMetadataMapping;
57
- expect(mapping).toBeDefined();
58
- expect(Object.keys(mapping ?? {}).length).toBeGreaterThan(0);
59
- });
60
- });
61
-
62
- // ---------------------------------------------------------------------------
63
- // Kasu.create()
64
- // ---------------------------------------------------------------------------
65
-
66
- describe('Kasu.create()', () => {
67
- // Mock provider — enough to construct the SDK without network calls
68
- const mockProvider = {
69
- getNetwork: jest.fn().mockResolvedValue({ chainId: 8453, name: 'base' }),
70
- call: jest.fn(),
71
- estimateGas: jest.fn(),
72
- getBlockNumber: jest.fn(),
73
- getBlock: jest.fn(),
74
- getTransaction: jest.fn(),
75
- getTransactionReceipt: jest.fn(),
76
- getLogs: jest.fn(),
77
- on: jest.fn(),
78
- once: jest.fn(),
79
- off: jest.fn(),
80
- removeAllListeners: jest.fn(),
81
- _isProvider: true,
82
- };
83
-
84
- it('should create an instance from a chain name', () => {
85
- const kasu = Kasu.create({
86
- chain: 'base',
87
- signerOrProvider: mockProvider as never,
88
- });
89
-
90
- expect(kasu).toBeInstanceOf(Kasu);
91
- expect(kasu.strategies).toBeInstanceOf(StrategiesFacade);
92
- expect(kasu.deposits).toBeInstanceOf(DepositsFacade);
93
- expect(kasu.portfolio).toBeInstanceOf(PortfolioFacade);
94
- });
95
-
96
- it('should expose chainConfig for the selected chain', () => {
97
- const kasu = Kasu.create({
98
- chain: 'xdc',
99
- signerOrProvider: mockProvider as never,
100
- });
101
-
102
- expect(kasu.chainConfig.chainId).toBe(50);
103
- expect(kasu.isLiteDeployment).toBe(true);
104
- });
105
-
106
- it('should accept a custom ChainConfigEntry', () => {
107
- const custom = {
108
- ...CHAIN_CONFIGS.base,
109
- chainId: 999,
110
- name: 'CustomChain',
111
- };
112
-
113
- const kasu = Kasu.create({
114
- chain: custom,
115
- signerOrProvider: mockProvider as never,
116
- });
117
-
118
- expect(kasu.chainConfig.chainId).toBe(999);
119
- expect(kasu.chainConfig.name).toBe('CustomChain');
120
- });
121
-
122
- it('should accept configOverrides', () => {
123
- const kasu = Kasu.create({
124
- chain: 'base',
125
- signerOrProvider: mockProvider as never,
126
- configOverrides: {
127
- UNUSED_LENDING_POOL_IDS: ['0xdead'],
128
- },
129
- });
130
-
131
- expect(kasu).toBeInstanceOf(Kasu);
132
- });
133
-
134
- it('should expose the underlying KasuSdk via .services', () => {
135
- const kasu = Kasu.create({
136
- chain: 'base',
137
- signerOrProvider: mockProvider as never,
138
- });
139
-
140
- expect(kasu.services).toBeDefined();
141
- expect(kasu.services.DataService).toBeDefined();
142
- expect(kasu.services.UserLending).toBeDefined();
143
- expect(kasu.services.Locking).toBeDefined();
144
- expect(kasu.services.Portfolio).toBeDefined();
145
- });
146
- });
147
-
148
- // ---------------------------------------------------------------------------
149
- // StrategiesFacade.calculateDepositLimits()
150
- // ---------------------------------------------------------------------------
151
-
152
- describe('StrategiesFacade.calculateDepositLimits()', () => {
153
- // We can test this pure function without network calls by constructing
154
- // a StrategiesFacade with null services and calling calculateDepositLimits directly.
155
- const facade = new StrategiesFacade(null as never, null as never);
156
-
157
- it('should enforce minimum of 1 USDC', () => {
158
- const tranche = makeTranche({
159
- minimumDeposit: '0.5',
160
- maximumDeposit: '10000',
161
- poolCapacity: '50000',
162
- });
163
-
164
- const limits = facade.calculateDepositLimits(tranche);
165
- expect(limits.min).toBe('1');
166
- expect(limits.max).toBe('10000');
167
- expect(limits.availableCapacity).toBe('50000');
168
- });
169
-
170
- it('should pass through minimum when >= 1', () => {
171
- const tranche = makeTranche({
172
- minimumDeposit: '100',
173
- maximumDeposit: '10000',
174
- poolCapacity: '50000',
175
- });
176
-
177
- const limits = facade.calculateDepositLimits(tranche);
178
- expect(limits.min).toBe('100');
179
- });
180
-
181
- it('should cap max at pool capacity when capacity is lower', () => {
182
- const tranche = makeTranche({
183
- minimumDeposit: '100',
184
- maximumDeposit: '50000',
185
- poolCapacity: '10000',
186
- });
187
-
188
- const limits = facade.calculateDepositLimits(tranche);
189
- expect(limits.max).toBe('10000');
190
- });
191
-
192
- it('should use maximumDeposit when capacity is higher', () => {
193
- const tranche = makeTranche({
194
- minimumDeposit: '100',
195
- maximumDeposit: '5000',
196
- poolCapacity: '50000',
197
- });
198
-
199
- const limits = facade.calculateDepositLimits(tranche);
200
- expect(limits.max).toBe('5000');
201
- });
202
- });
203
-
204
- // ---------------------------------------------------------------------------
205
- // Type exports — verify key facade types are importable
206
- // ---------------------------------------------------------------------------
207
-
208
- describe('Type re-exports', () => {
209
- it('should export facade classes from the main entry', () => {
210
- // Verify the barrel exports resolve — these would fail at compile time
211
- // if the re-exports were broken.
212
- expect(Kasu).toBeDefined();
213
- expect(CHAIN_CONFIGS).toBeDefined();
214
- expect(StrategiesFacade).toBeDefined();
215
- expect(DepositsFacade).toBeDefined();
216
- expect(PortfolioFacade).toBeDefined();
217
- });
218
- });
219
-
220
- // ---------------------------------------------------------------------------
221
- // Helpers
222
- // ---------------------------------------------------------------------------
223
-
224
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
225
- function makeTranche(raw: {
226
- minimumDeposit: string;
227
- maximumDeposit: string;
228
- poolCapacity: string;
229
- }) {
230
- return {
231
- id: '0xtest',
232
- name: 'Test Tranche',
233
- apy: 0.08,
234
- minApy: 0.06,
235
- maxApy: 0.10,
236
- minimumDeposit: raw.minimumDeposit,
237
- maximumDeposit: raw.maximumDeposit,
238
- availableCapacity: raw.poolCapacity,
239
- fixedTermOptions: [],
240
- _raw: {
241
- ...raw,
242
- id: '0xtest',
243
- name: 'Test Tranche',
244
- apy: '0.08',
245
- minApy: '0.06',
246
- maxApy: '0.10',
247
- fixedTermConfig: [],
248
- },
249
- } as never;
250
- }
@@ -1,52 +0,0 @@
1
- import { providers } from 'ethers';
2
-
3
- import { apyToEpochRate, epochRateToApy } from '../../domain/rates';
4
- import { CHAIN_CONFIGS } from '../../facade/chain-configs';
5
- import { SdkConfig } from '../../sdk-config';
6
-
7
- import { KSULocking } from './locking';
8
-
9
- // Offline: the constructor only builds contract instances. Nothing here calls
10
- // the RPC or the subgraph.
11
- function locking(): KSULocking {
12
- const chain = CHAIN_CONFIGS.base;
13
- const config = new SdkConfig({
14
- subgraphUrl: 'https://example.invalid/subgraph',
15
- contracts: chain.contracts,
16
- UNUSED_LENDING_POOL_IDS: [''],
17
- });
18
- return new KSULocking(
19
- config,
20
- new providers.StaticJsonRpcProvider(chain.rpcUrls[0], chain.chainId),
21
- );
22
- }
23
-
24
- describe('KSULocking.calculateApy', () => {
25
- it('compounds the epoch rate instead of XOR-ing it', () => {
26
- // The old body was `(1 + r) ^ (EPOCHS_IN_YEAR - 1)` — bitwise XOR on
27
- // int32 operands, so every realistic rate returned the constant 50.
28
- for (const r of [0.001, 0.0025, 0.003, 0.005]) {
29
- expect(locking().calculateApy(r)).toBe(epochRateToApy(r));
30
- expect(locking().calculateApy(r)).not.toBe(50);
31
- }
32
- });
33
-
34
- it('lands on the APYs the platform actually quotes', () => {
35
- // Every live tranche APY, back through its own per-epoch rate.
36
- for (const apy of [0.1, 0.12, 0.14, 0.16, 0.22, 0.3]) {
37
- expect(locking().calculateApy(apyToEpochRate(apy))).toBeCloseTo(
38
- apy,
39
- 10,
40
- );
41
- }
42
- expect(locking().calculateApy(0)).toBe(0);
43
- });
44
-
45
- it('agrees with DataService.calculateApyForTranche', () => {
46
- // The two used to carry separate copies of the constant AND separate
47
- // (one of them wrong) formulas.
48
- for (const r of [0.001, 0.003, 0.01]) {
49
- expect(locking().calculateApy(r)).toBe(epochRateToApy(r));
50
- }
51
- });
52
- });
@@ -1,63 +0,0 @@
1
- /**
2
- * Live-network spec. It talks to a real subgraph and a real RPC, so it is
3
- * OPT-IN: it runs only under `LIVE_TESTS=1` (`npm run test:live`) and is
4
- * skipped by `npm test` and `npm run test:unit`. A network problem must never
5
- * be able to fail a pull request that did not touch the network.
6
- *
7
- * Known state: the stack it points at (`kasu-wip-sepolia` on The Graph Studio,
8
- * with Base Sepolia addresses) is decommissioned, so `npm run test:live` fails
9
- * on a missing deployment. That is pre-existing and unrelated to the gate; the
10
- * spec needs retargeting at a live deployment before it means anything.
11
- */
12
- import * as ethers from 'ethers';
13
-
14
- import { KasuSdk } from '..';
15
- import { SdkConfig } from '../sdk-config';
16
-
17
- const live = process.env.LIVE_TESTS === '1';
18
-
19
- (live ? describe : describe.skip)('live network — subgraph read', () => {
20
- it('reads a user APY bonus through the subgraph', async () => {
21
- const provider = new ethers.providers.JsonRpcProvider(
22
- 'https://sepolia.base.org',
23
- );
24
-
25
- // Generated per run. This spec only reads, so the wallet never needs
26
- // funds or a key that outlives the process — and a public repository
27
- // must never carry one.
28
- const wallet = ethers.Wallet.createRandom().connect(provider);
29
-
30
- const config = new SdkConfig({
31
- subgraphUrl:
32
- 'https://api.studio.thegraph.com/query/63245/kasu-wip-sepolia/version/latest',
33
- contracts: {
34
- IKSULocking: '0x529A81c11ab6176c5E88670d293BB771800066a2',
35
- IKSULockBonus: '0xBBfFd5F744156FFc526df12F5e09dC7b208Be740',
36
- // The allow below is for a public Base Sepolia contract
37
- // address, not a secret: the generic-api-key rule fires on a
38
- // "Token" key name sitting next to a long hex literal.
39
- KSUToken: '0xa0f698Feb9Bc2BeA6E85eb071D5E3F59dc5bC56b', // gitleaks:allow
40
- UserManager: '0xA788e9223fDd7c9Db91Ef133BbeB73515c174773',
41
- LendingPoolManager: '0xc074Aaf2565aae18db2c7498ee1387610a809F40',
42
- KasuAllowList: '0xaE94F9D187c9eA649ADd4966340831F7cc62B69c',
43
- SystemVariables: '0xB174a3240B23e595e90bC2736A5b8ec674Cba73A',
44
- UserLoyaltyRewards:
45
- '0x259631CE76FD9F65296549Cd81232aa59D53Dd0c',
46
- KsuPrice: '0x2027192aCFB4810Ad734A212F19d7030F698aCE3',
47
- ClearingCoordinator:
48
- '0x2027192aCFB4810Ad734A212F19d7030F698aCE3',
49
- KasuNFTs: '0x0000000000000000000000000000000000000000',
50
- ExternalTVL: '0x0000000000000000000000000000000000000000',
51
- },
52
- directusUrl: 'https://kasu-finance.directus.app',
53
- UNUSED_LENDING_POOL_IDS: [''],
54
- });
55
- const sdk = new KasuSdk(config, wallet);
56
-
57
- const bonus = await sdk.UserLending.getUserApyBonus(
58
- '0xef38c432682ab49095e00442aaa354e955ac03a5',
59
- );
60
-
61
- expect(bonus).toBeDefined();
62
- });
63
- });