@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.
- package/README.md +144 -7
- package/dist/bundle.cjs.js +1667 -21
- package/dist/bundle.esm.js +1630 -22
- package/dist/domain/au-minimum.d.ts +135 -0
- package/dist/domain/au-minimum.js +154 -0
- package/dist/domain/au-minimum.js.map +1 -0
- package/dist/domain/index.d.ts +18 -3
- package/dist/domain/index.js +13 -3
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/loan-contract.d.ts +174 -0
- package/dist/domain/loan-contract.js +160 -0
- package/dist/domain/loan-contract.js.map +1 -0
- package/dist/domain/requests.d.ts +219 -0
- package/dist/domain/requests.js +218 -0
- package/dist/domain/requests.js.map +1 -0
- package/dist/domain/settlement.d.ts +97 -0
- package/dist/domain/settlement.js +117 -0
- package/dist/domain/settlement.js.map +1 -0
- package/dist/domain/wallet-errors.d.ts +92 -0
- package/dist/domain/wallet-errors.js +155 -0
- package/dist/domain/wallet-errors.js.map +1 -0
- package/dist/facade/chain-configs.js +7 -1
- package/dist/facade/chain-configs.js.map +1 -1
- package/dist/facade/deposits.js +2 -1
- package/dist/facade/deposits.js.map +1 -1
- package/dist/facade/flows.d.ts +95 -0
- package/dist/facade/flows.js +116 -0
- package/dist/facade/flows.js.map +1 -0
- package/dist/facade/index.d.ts +3 -0
- package/dist/facade/index.js +3 -0
- package/dist/facade/index.js.map +1 -1
- package/dist/facade/kasu.d.ts +6 -2
- package/dist/facade/kasu.js +28 -3
- package/dist/facade/kasu.js.map +1 -1
- package/dist/facade/read-only.d.ts +12 -0
- package/dist/facade/read-only.js +13 -0
- package/dist/facade/read-only.js.map +1 -0
- package/dist/facade/user-portfolio.d.ts +18 -0
- package/dist/facade/user-portfolio.js +23 -0
- package/dist/facade/user-portfolio.js.map +1 -1
- package/dist/flows/deposit-flow.d.ts +301 -0
- package/dist/flows/deposit-flow.js +358 -0
- package/dist/flows/deposit-flow.js.map +1 -0
- package/dist/flows/flow.d.ts +72 -0
- package/dist/flows/flow.js +110 -0
- package/dist/flows/flow.js.map +1 -0
- package/dist/flows/index.d.ts +19 -0
- package/dist/flows/index.js +17 -0
- package/dist/flows/index.js.map +1 -0
- package/dist/flows/observable.d.ts +59 -0
- package/dist/flows/observable.js +81 -0
- package/dist/flows/observable.js.map +1 -0
- package/dist/flows/withdraw-flow.d.ts +99 -0
- package/dist/flows/withdraw-flow.js +83 -0
- package/dist/flows/withdraw-flow.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/services/DataService/data-service.js +3 -11
- package/dist/services/DataService/data-service.js.map +1 -1
- package/dist/services/DataService/directus-client.d.ts +26 -0
- package/dist/services/DataService/directus-client.js +38 -0
- package/dist/services/DataService/directus-client.js.map +1 -0
- package/dist/services/UserLending/user-lending.js +11 -7
- package/dist/services/UserLending/user-lending.js.map +1 -1
- package/package.json +8 -2
- package/src/domain/au-minimum.ts +192 -0
- package/src/domain/index.ts +73 -3
- package/src/domain/loan-contract.ts +275 -0
- package/src/domain/requests.ts +467 -0
- package/src/domain/settlement.ts +161 -0
- package/src/domain/wallet-errors.ts +173 -0
- package/src/facade/chain-configs.ts +7 -1
- package/src/facade/deposits.ts +2 -3
- package/src/facade/flows.ts +172 -0
- package/src/facade/index.ts +8 -0
- package/src/facade/kasu.ts +43 -3
- package/src/facade/read-only.ts +13 -0
- package/src/facade/user-portfolio.ts +24 -0
- package/src/flows/deposit-flow.ts +775 -0
- package/src/flows/flow.ts +108 -0
- package/src/flows/index.ts +45 -0
- package/src/flows/observable.ts +97 -0
- package/src/flows/withdraw-flow.ts +210 -0
- package/src/index.ts +9 -0
- package/src/services/DataService/data-service.ts +7 -25
- package/src/services/DataService/directus-client.ts +54 -0
- package/src/services/UserLending/user-lending.ts +17 -21
- package/dist/domain/deposit-bounds.test.d.ts +0 -1
- package/dist/domain/deposit-bounds.test.js +0 -135
- package/dist/domain/deposit-bounds.test.js.map +0 -1
- package/dist/domain/partners.test.d.ts +0 -1
- package/dist/domain/partners.test.js +0 -53
- package/dist/domain/partners.test.js.map +0 -1
- package/dist/domain/pools.test.d.ts +0 -1
- package/dist/domain/pools.test.js +0 -184
- package/dist/domain/pools.test.js.map +0 -1
- package/dist/domain/rates.test.d.ts +0 -1
- package/dist/domain/rates.test.js +0 -181
- package/dist/domain/rates.test.js.map +0 -1
- package/dist/domain/tranche-display-name.test.d.ts +0 -1
- package/dist/domain/tranche-display-name.test.js +0 -58
- package/dist/domain/tranche-display-name.test.js.map +0 -1
- package/dist/domain/tranches.test.d.ts +0 -1
- package/dist/domain/tranches.test.js +0 -206
- package/dist/domain/tranches.test.js.map +0 -1
- package/dist/facade/config.test.d.ts +0 -1
- package/dist/facade/config.test.js +0 -216
- package/dist/facade/config.test.js.map +0 -1
- package/dist/facade/facade.test.d.ts +0 -1
- package/dist/facade/facade.test.js +0 -201
- package/dist/facade/facade.test.js.map +0 -1
- package/dist/services/Locking/calculate-apy.test.d.ts +0 -1
- package/dist/services/Locking/calculate-apy.test.js +0 -41
- package/dist/services/Locking/calculate-apy.test.js.map +0 -1
- package/dist/tests/sample.test.d.ts +0 -1
- package/dist/tests/sample.test.js +0 -59
- package/dist/tests/sample.test.js.map +0 -1
- package/src/domain/deposit-bounds.test.ts +0 -200
- package/src/domain/partners.test.ts +0 -83
- package/src/domain/pools.test.ts +0 -260
- package/src/domain/rates.test.ts +0 -254
- package/src/domain/tranche-display-name.test.ts +0 -96
- package/src/domain/tranches.test.ts +0 -300
- package/src/facade/config.test.ts +0 -265
- package/src/facade/facade.test.ts +0 -250
- package/src/services/Locking/calculate-apy.test.ts +0 -52
- package/src/tests/sample.test.ts +0 -63
|
@@ -1,201 +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
|
-
// Chain configs
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
describe('CHAIN_CONFIGS', () => {
|
|
15
|
-
it('should have base, xdc, and plume entries', () => {
|
|
16
|
-
expect(CHAIN_CONFIGS).toHaveProperty('base');
|
|
17
|
-
expect(CHAIN_CONFIGS).toHaveProperty('xdc');
|
|
18
|
-
expect(CHAIN_CONFIGS).toHaveProperty('plume');
|
|
19
|
-
});
|
|
20
|
-
it('base should be a full deployment', () => {
|
|
21
|
-
const base = CHAIN_CONFIGS.base;
|
|
22
|
-
expect(base.chainId).toBe(8453);
|
|
23
|
-
expect(base.isLiteDeployment).toBe(false);
|
|
24
|
-
expect(base.contracts.KSUToken).toBeDefined();
|
|
25
|
-
expect(base.contracts.KasuNFTs).toBeDefined();
|
|
26
|
-
});
|
|
27
|
-
it('xdc should be a lite deployment without KSU token', () => {
|
|
28
|
-
const xdc = CHAIN_CONFIGS.xdc;
|
|
29
|
-
expect(xdc.chainId).toBe(50);
|
|
30
|
-
expect(xdc.isLiteDeployment).toBe(true);
|
|
31
|
-
expect(xdc.contracts.KSUToken).toBeUndefined();
|
|
32
|
-
expect(xdc.contracts.KasuNFTs).toBeUndefined();
|
|
33
|
-
});
|
|
34
|
-
it('plume should be a lite deployment', () => {
|
|
35
|
-
const plume = CHAIN_CONFIGS.plume;
|
|
36
|
-
expect(plume.chainId).toBe(98866);
|
|
37
|
-
expect(plume.isLiteDeployment).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
it('all chains should have required contract addresses', () => {
|
|
40
|
-
for (const [, config] of Object.entries(CHAIN_CONFIGS)) {
|
|
41
|
-
expect(config.contracts.LendingPoolManager).toBeDefined();
|
|
42
|
-
expect(config.contracts.UserManager).toBeDefined();
|
|
43
|
-
expect(config.contracts.KasuAllowList).toBeDefined();
|
|
44
|
-
expect(config.contracts.SystemVariables).toBeDefined();
|
|
45
|
-
expect(config.subgraphUrl).toContain('goldsky.com');
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
it('xdc should have poolMetadataMapping', () => {
|
|
49
|
-
const mapping = CHAIN_CONFIGS.xdc.poolMetadataMapping;
|
|
50
|
-
expect(mapping).toBeDefined();
|
|
51
|
-
expect(Object.keys(mapping !== null && mapping !== void 0 ? mapping : {}).length).toBeGreaterThan(0);
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
// Kasu.create()
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
describe('Kasu.create()', () => {
|
|
58
|
-
// Mock provider — enough to construct the SDK without network calls
|
|
59
|
-
const mockProvider = {
|
|
60
|
-
getNetwork: jest.fn().mockResolvedValue({ chainId: 8453, name: 'base' }),
|
|
61
|
-
call: jest.fn(),
|
|
62
|
-
estimateGas: jest.fn(),
|
|
63
|
-
getBlockNumber: jest.fn(),
|
|
64
|
-
getBlock: jest.fn(),
|
|
65
|
-
getTransaction: jest.fn(),
|
|
66
|
-
getTransactionReceipt: jest.fn(),
|
|
67
|
-
getLogs: jest.fn(),
|
|
68
|
-
on: jest.fn(),
|
|
69
|
-
once: jest.fn(),
|
|
70
|
-
off: jest.fn(),
|
|
71
|
-
removeAllListeners: jest.fn(),
|
|
72
|
-
_isProvider: true,
|
|
73
|
-
};
|
|
74
|
-
it('should create an instance from a chain name', () => {
|
|
75
|
-
const kasu = Kasu.create({
|
|
76
|
-
chain: 'base',
|
|
77
|
-
signerOrProvider: mockProvider,
|
|
78
|
-
});
|
|
79
|
-
expect(kasu).toBeInstanceOf(Kasu);
|
|
80
|
-
expect(kasu.strategies).toBeInstanceOf(StrategiesFacade);
|
|
81
|
-
expect(kasu.deposits).toBeInstanceOf(DepositsFacade);
|
|
82
|
-
expect(kasu.portfolio).toBeInstanceOf(PortfolioFacade);
|
|
83
|
-
});
|
|
84
|
-
it('should expose chainConfig for the selected chain', () => {
|
|
85
|
-
const kasu = Kasu.create({
|
|
86
|
-
chain: 'xdc',
|
|
87
|
-
signerOrProvider: mockProvider,
|
|
88
|
-
});
|
|
89
|
-
expect(kasu.chainConfig.chainId).toBe(50);
|
|
90
|
-
expect(kasu.isLiteDeployment).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
it('should accept a custom ChainConfigEntry', () => {
|
|
93
|
-
const custom = Object.assign(Object.assign({}, CHAIN_CONFIGS.base), { chainId: 999, name: 'CustomChain' });
|
|
94
|
-
const kasu = Kasu.create({
|
|
95
|
-
chain: custom,
|
|
96
|
-
signerOrProvider: mockProvider,
|
|
97
|
-
});
|
|
98
|
-
expect(kasu.chainConfig.chainId).toBe(999);
|
|
99
|
-
expect(kasu.chainConfig.name).toBe('CustomChain');
|
|
100
|
-
});
|
|
101
|
-
it('should accept configOverrides', () => {
|
|
102
|
-
const kasu = Kasu.create({
|
|
103
|
-
chain: 'base',
|
|
104
|
-
signerOrProvider: mockProvider,
|
|
105
|
-
configOverrides: {
|
|
106
|
-
UNUSED_LENDING_POOL_IDS: ['0xdead'],
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
expect(kasu).toBeInstanceOf(Kasu);
|
|
110
|
-
});
|
|
111
|
-
it('should expose the underlying KasuSdk via .services', () => {
|
|
112
|
-
const kasu = Kasu.create({
|
|
113
|
-
chain: 'base',
|
|
114
|
-
signerOrProvider: mockProvider,
|
|
115
|
-
});
|
|
116
|
-
expect(kasu.services).toBeDefined();
|
|
117
|
-
expect(kasu.services.DataService).toBeDefined();
|
|
118
|
-
expect(kasu.services.UserLending).toBeDefined();
|
|
119
|
-
expect(kasu.services.Locking).toBeDefined();
|
|
120
|
-
expect(kasu.services.Portfolio).toBeDefined();
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
// ---------------------------------------------------------------------------
|
|
124
|
-
// StrategiesFacade.calculateDepositLimits()
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
describe('StrategiesFacade.calculateDepositLimits()', () => {
|
|
127
|
-
// We can test this pure function without network calls by constructing
|
|
128
|
-
// a StrategiesFacade with null services and calling calculateDepositLimits directly.
|
|
129
|
-
const facade = new StrategiesFacade(null, null);
|
|
130
|
-
it('should enforce minimum of 1 USDC', () => {
|
|
131
|
-
const tranche = makeTranche({
|
|
132
|
-
minimumDeposit: '0.5',
|
|
133
|
-
maximumDeposit: '10000',
|
|
134
|
-
poolCapacity: '50000',
|
|
135
|
-
});
|
|
136
|
-
const limits = facade.calculateDepositLimits(tranche);
|
|
137
|
-
expect(limits.min).toBe('1');
|
|
138
|
-
expect(limits.max).toBe('10000');
|
|
139
|
-
expect(limits.availableCapacity).toBe('50000');
|
|
140
|
-
});
|
|
141
|
-
it('should pass through minimum when >= 1', () => {
|
|
142
|
-
const tranche = makeTranche({
|
|
143
|
-
minimumDeposit: '100',
|
|
144
|
-
maximumDeposit: '10000',
|
|
145
|
-
poolCapacity: '50000',
|
|
146
|
-
});
|
|
147
|
-
const limits = facade.calculateDepositLimits(tranche);
|
|
148
|
-
expect(limits.min).toBe('100');
|
|
149
|
-
});
|
|
150
|
-
it('should cap max at pool capacity when capacity is lower', () => {
|
|
151
|
-
const tranche = makeTranche({
|
|
152
|
-
minimumDeposit: '100',
|
|
153
|
-
maximumDeposit: '50000',
|
|
154
|
-
poolCapacity: '10000',
|
|
155
|
-
});
|
|
156
|
-
const limits = facade.calculateDepositLimits(tranche);
|
|
157
|
-
expect(limits.max).toBe('10000');
|
|
158
|
-
});
|
|
159
|
-
it('should use maximumDeposit when capacity is higher', () => {
|
|
160
|
-
const tranche = makeTranche({
|
|
161
|
-
minimumDeposit: '100',
|
|
162
|
-
maximumDeposit: '5000',
|
|
163
|
-
poolCapacity: '50000',
|
|
164
|
-
});
|
|
165
|
-
const limits = facade.calculateDepositLimits(tranche);
|
|
166
|
-
expect(limits.max).toBe('5000');
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
// Type exports — verify key facade types are importable
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
describe('Type re-exports', () => {
|
|
173
|
-
it('should export facade classes from the main entry', () => {
|
|
174
|
-
// Verify the barrel exports resolve — these would fail at compile time
|
|
175
|
-
// if the re-exports were broken.
|
|
176
|
-
expect(Kasu).toBeDefined();
|
|
177
|
-
expect(CHAIN_CONFIGS).toBeDefined();
|
|
178
|
-
expect(StrategiesFacade).toBeDefined();
|
|
179
|
-
expect(DepositsFacade).toBeDefined();
|
|
180
|
-
expect(PortfolioFacade).toBeDefined();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
// ---------------------------------------------------------------------------
|
|
184
|
-
// Helpers
|
|
185
|
-
// ---------------------------------------------------------------------------
|
|
186
|
-
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
187
|
-
function makeTranche(raw) {
|
|
188
|
-
return {
|
|
189
|
-
id: '0xtest',
|
|
190
|
-
name: 'Test Tranche',
|
|
191
|
-
apy: 0.08,
|
|
192
|
-
minApy: 0.06,
|
|
193
|
-
maxApy: 0.10,
|
|
194
|
-
minimumDeposit: raw.minimumDeposit,
|
|
195
|
-
maximumDeposit: raw.maximumDeposit,
|
|
196
|
-
availableCapacity: raw.poolCapacity,
|
|
197
|
-
fixedTermOptions: [],
|
|
198
|
-
_raw: Object.assign(Object.assign({}, raw), { id: '0xtest', name: 'Test Tranche', apy: '0.08', minApy: '0.06', maxApy: '0.10', fixedTermConfig: [] }),
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
//# sourceMappingURL=facade.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"facade.test.js","sourceRoot":"","sources":["../../src/facade/facade.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { providers } from 'ethers';
|
|
2
|
-
import { apyToEpochRate, epochRateToApy } from '../../domain/rates';
|
|
3
|
-
import { CHAIN_CONFIGS } from '../../facade/chain-configs';
|
|
4
|
-
import { SdkConfig } from '../../sdk-config';
|
|
5
|
-
import { KSULocking } from './locking';
|
|
6
|
-
// Offline: the constructor only builds contract instances. Nothing here calls
|
|
7
|
-
// the RPC or the subgraph.
|
|
8
|
-
function locking() {
|
|
9
|
-
const chain = CHAIN_CONFIGS.base;
|
|
10
|
-
const config = new SdkConfig({
|
|
11
|
-
subgraphUrl: 'https://example.invalid/subgraph',
|
|
12
|
-
contracts: chain.contracts,
|
|
13
|
-
UNUSED_LENDING_POOL_IDS: [''],
|
|
14
|
-
});
|
|
15
|
-
return new KSULocking(config, new providers.StaticJsonRpcProvider(chain.rpcUrls[0], chain.chainId));
|
|
16
|
-
}
|
|
17
|
-
describe('KSULocking.calculateApy', () => {
|
|
18
|
-
it('compounds the epoch rate instead of XOR-ing it', () => {
|
|
19
|
-
// The old body was `(1 + r) ^ (EPOCHS_IN_YEAR - 1)` — bitwise XOR on
|
|
20
|
-
// int32 operands, so every realistic rate returned the constant 50.
|
|
21
|
-
for (const r of [0.001, 0.0025, 0.003, 0.005]) {
|
|
22
|
-
expect(locking().calculateApy(r)).toBe(epochRateToApy(r));
|
|
23
|
-
expect(locking().calculateApy(r)).not.toBe(50);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
it('lands on the APYs the platform actually quotes', () => {
|
|
27
|
-
// Every live tranche APY, back through its own per-epoch rate.
|
|
28
|
-
for (const apy of [0.1, 0.12, 0.14, 0.16, 0.22, 0.3]) {
|
|
29
|
-
expect(locking().calculateApy(apyToEpochRate(apy))).toBeCloseTo(apy, 10);
|
|
30
|
-
}
|
|
31
|
-
expect(locking().calculateApy(0)).toBe(0);
|
|
32
|
-
});
|
|
33
|
-
it('agrees with DataService.calculateApyForTranche', () => {
|
|
34
|
-
// The two used to carry separate copies of the constant AND separate
|
|
35
|
-
// (one of them wrong) formulas.
|
|
36
|
-
for (const r of [0.001, 0.003, 0.01]) {
|
|
37
|
-
expect(locking().calculateApy(r)).toBe(epochRateToApy(r));
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
//# sourceMappingURL=calculate-apy.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"calculate-apy.test.js","sourceRoot":"","sources":["../../../src/services/Locking/calculate-apy.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,8EAA8E;AAC9E,2BAA2B;AAC3B,SAAS,OAAO;IACZ,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QACzB,WAAW,EAAE,kCAAkC;QAC/C,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,uBAAuB,EAAE,CAAC,EAAE,CAAC;KAChC,CAAC,CAAC;IACH,OAAO,IAAI,UAAU,CACjB,MAAM,EACN,IAAI,SAAS,CAAC,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CACvE,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,qEAAqE;QACrE,oEAAoE;QACpE,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,+DAA+D;QAC/D,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAC3D,GAAG,EACH,EAAE,CACL,CAAC;QACN,CAAC;QACD,MAAM,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,qEAAqE;QACrE,gCAAgC;QAChC,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,OAAO,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Live-network spec. It talks to a real subgraph and a real RPC, so it is
|
|
12
|
-
* OPT-IN: it runs only under `LIVE_TESTS=1` (`npm run test:live`) and is
|
|
13
|
-
* skipped by `npm test` and `npm run test:unit`. A network problem must never
|
|
14
|
-
* be able to fail a pull request that did not touch the network.
|
|
15
|
-
*
|
|
16
|
-
* Known state: the stack it points at (`kasu-wip-sepolia` on The Graph Studio,
|
|
17
|
-
* with Base Sepolia addresses) is decommissioned, so `npm run test:live` fails
|
|
18
|
-
* on a missing deployment. That is pre-existing and unrelated to the gate; the
|
|
19
|
-
* spec needs retargeting at a live deployment before it means anything.
|
|
20
|
-
*/
|
|
21
|
-
import * as ethers from 'ethers';
|
|
22
|
-
import { KasuSdk } from '..';
|
|
23
|
-
import { SdkConfig } from '../sdk-config';
|
|
24
|
-
const live = process.env.LIVE_TESTS === '1';
|
|
25
|
-
(live ? describe : describe.skip)('live network — subgraph read', () => {
|
|
26
|
-
it('reads a user APY bonus through the subgraph', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
-
const provider = new ethers.providers.JsonRpcProvider('https://sepolia.base.org');
|
|
28
|
-
// Generated per run. This spec only reads, so the wallet never needs
|
|
29
|
-
// funds or a key that outlives the process — and a public repository
|
|
30
|
-
// must never carry one.
|
|
31
|
-
const wallet = ethers.Wallet.createRandom().connect(provider);
|
|
32
|
-
const config = new SdkConfig({
|
|
33
|
-
subgraphUrl: 'https://api.studio.thegraph.com/query/63245/kasu-wip-sepolia/version/latest',
|
|
34
|
-
contracts: {
|
|
35
|
-
IKSULocking: '0x529A81c11ab6176c5E88670d293BB771800066a2',
|
|
36
|
-
IKSULockBonus: '0xBBfFd5F744156FFc526df12F5e09dC7b208Be740',
|
|
37
|
-
// The allow below is for a public Base Sepolia contract
|
|
38
|
-
// address, not a secret: the generic-api-key rule fires on a
|
|
39
|
-
// "Token" key name sitting next to a long hex literal.
|
|
40
|
-
KSUToken: '0xa0f698Feb9Bc2BeA6E85eb071D5E3F59dc5bC56b', // gitleaks:allow
|
|
41
|
-
UserManager: '0xA788e9223fDd7c9Db91Ef133BbeB73515c174773',
|
|
42
|
-
LendingPoolManager: '0xc074Aaf2565aae18db2c7498ee1387610a809F40',
|
|
43
|
-
KasuAllowList: '0xaE94F9D187c9eA649ADd4966340831F7cc62B69c',
|
|
44
|
-
SystemVariables: '0xB174a3240B23e595e90bC2736A5b8ec674Cba73A',
|
|
45
|
-
UserLoyaltyRewards: '0x259631CE76FD9F65296549Cd81232aa59D53Dd0c',
|
|
46
|
-
KsuPrice: '0x2027192aCFB4810Ad734A212F19d7030F698aCE3',
|
|
47
|
-
ClearingCoordinator: '0x2027192aCFB4810Ad734A212F19d7030F698aCE3',
|
|
48
|
-
KasuNFTs: '0x0000000000000000000000000000000000000000',
|
|
49
|
-
ExternalTVL: '0x0000000000000000000000000000000000000000',
|
|
50
|
-
},
|
|
51
|
-
directusUrl: 'https://kasu-finance.directus.app',
|
|
52
|
-
UNUSED_LENDING_POOL_IDS: [''],
|
|
53
|
-
});
|
|
54
|
-
const sdk = new KasuSdk(config, wallet);
|
|
55
|
-
const bonus = yield sdk.UserLending.getUserApyBonus('0xef38c432682ab49095e00442aaa354e955ac03a5');
|
|
56
|
-
expect(bonus).toBeDefined();
|
|
57
|
-
}));
|
|
58
|
-
});
|
|
59
|
-
//# sourceMappingURL=sample.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sample.test.js","sourceRoot":"","sources":["../../src/tests/sample.test.ts"],"names":[],"mappings":";;;;;;;;;AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG,CAAC;AAE5C,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,8BAA8B,EAAE,GAAG,EAAE;IACnE,EAAE,CAAC,6CAA6C,EAAE,GAAS,EAAE;QACzD,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,SAAS,CAAC,eAAe,CACjD,0BAA0B,CAC7B,CAAC;QAEF,qEAAqE;QACrE,qEAAqE;QACrE,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE9D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;YACzB,WAAW,EACP,6EAA6E;YACjF,SAAS,EAAE;gBACP,WAAW,EAAE,4CAA4C;gBACzD,aAAa,EAAE,4CAA4C;gBAC3D,wDAAwD;gBACxD,6DAA6D;gBAC7D,uDAAuD;gBACvD,QAAQ,EAAE,4CAA4C,EAAE,iBAAiB;gBACzE,WAAW,EAAE,4CAA4C;gBACzD,kBAAkB,EAAE,4CAA4C;gBAChE,aAAa,EAAE,4CAA4C;gBAC3D,eAAe,EAAE,4CAA4C;gBAC7D,kBAAkB,EACd,4CAA4C;gBAChD,QAAQ,EAAE,4CAA4C;gBACtD,mBAAmB,EACf,4CAA4C;gBAChD,QAAQ,EAAE,4CAA4C;gBACtD,WAAW,EAAE,4CAA4C;aAC5D;YACD,WAAW,EAAE,mCAAmC;YAChD,uBAAuB,EAAE,CAAC,EAAE,CAAC;SAChC,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAExC,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,eAAe,CAC/C,4CAA4C,CAC/C,CAAC;QAEF,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAChC,CAAC,CAAA,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import * as fc from 'fast-check';
|
|
2
|
-
|
|
3
|
-
import { TrancheData } from '../services/DataService/types';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
ceilToCents,
|
|
7
|
-
floorToCents,
|
|
8
|
-
isBelowMinimumCapacity,
|
|
9
|
-
MAX_LENDING_AMOUNT_FALLBACK,
|
|
10
|
-
MIN_LENDING_AMOUNT_FALLBACK,
|
|
11
|
-
parseTrancheBound,
|
|
12
|
-
resolveBoundShortcuts,
|
|
13
|
-
resolveDepositBounds,
|
|
14
|
-
} from './deposit-bounds';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Ported from kasu-ui `src/features/lending/lib/tranche-bounds.property.test.ts`,
|
|
18
|
-
* with the `resolveDepositBounds` / `parseTrancheBound` cases the property file
|
|
19
|
-
* did not cover.
|
|
20
|
-
*
|
|
21
|
-
* The Min/Max shortcuts (clickable bound labels on a lend form) must never
|
|
22
|
-
* write an amount the form rejects, so the snapping is checked as invariants
|
|
23
|
-
* over the whole input domain rather than at a few hand-picked values.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
function tranche(over: Partial<TrancheData>): TrancheData {
|
|
27
|
-
return {
|
|
28
|
-
minimumDeposit: '500',
|
|
29
|
-
maximumDeposit: '500000',
|
|
30
|
-
poolCapacity: '250000',
|
|
31
|
-
...over,
|
|
32
|
-
} as unknown as TrancheData;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const amountArb = fc.double({
|
|
36
|
-
min: 0,
|
|
37
|
-
max: 1e12,
|
|
38
|
-
noNaN: true,
|
|
39
|
-
noDefaultInfinity: true,
|
|
40
|
-
});
|
|
41
|
-
/** `String(k / 100)` prints at most two decimals for any integer `k` in range. */
|
|
42
|
-
const AT_MOST_TWO_DP = /^\d+(\.\d{1,2})?$/;
|
|
43
|
-
|
|
44
|
-
describe('parseTrancheBound', () => {
|
|
45
|
-
it('accepts a positive finite bound from a string or a number', () => {
|
|
46
|
-
expect(parseTrancheBound('500')).toBe(500);
|
|
47
|
-
expect(parseTrancheBound(500)).toBe(500);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('rejects anything that is not a usable bound', () => {
|
|
51
|
-
expect(parseTrancheBound(null)).toBeNull();
|
|
52
|
-
expect(parseTrancheBound(undefined)).toBeNull();
|
|
53
|
-
expect(parseTrancheBound('')).toBeNull();
|
|
54
|
-
expect(parseTrancheBound('0')).toBeNull();
|
|
55
|
-
expect(parseTrancheBound('-1')).toBeNull();
|
|
56
|
-
expect(parseTrancheBound('not-a-number')).toBeNull();
|
|
57
|
-
expect(parseTrancheBound(Infinity)).toBeNull();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('resolveDepositBounds', () => {
|
|
62
|
-
it('clamps the max by the tranche’s REMAINING capacity', () => {
|
|
63
|
-
// A tranche can fill mid-epoch while `maximumDeposit` stays put.
|
|
64
|
-
expect(
|
|
65
|
-
resolveDepositBounds(
|
|
66
|
-
tranche({ maximumDeposit: '500000', poolCapacity: '25285.86' }),
|
|
67
|
-
),
|
|
68
|
-
).toEqual({ minDeposit: 500, maxDeposit: 25285.86 });
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('keeps the configured max when capacity is larger', () => {
|
|
72
|
-
expect(
|
|
73
|
-
resolveDepositBounds(
|
|
74
|
-
tranche({ maximumDeposit: '500000', poolCapacity: '900000' }),
|
|
75
|
-
),
|
|
76
|
-
).toEqual({ minDeposit: 500, maxDeposit: 500000 });
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('falls back when a "coming soon" pool has no bounds configured', () => {
|
|
80
|
-
expect(
|
|
81
|
-
resolveDepositBounds(
|
|
82
|
-
tranche({
|
|
83
|
-
minimumDeposit: '0',
|
|
84
|
-
maximumDeposit: '0',
|
|
85
|
-
poolCapacity: '900000',
|
|
86
|
-
}),
|
|
87
|
-
),
|
|
88
|
-
).toEqual({
|
|
89
|
-
minDeposit: MIN_LENDING_AMOUNT_FALLBACK,
|
|
90
|
-
maxDeposit: MAX_LENDING_AMOUNT_FALLBACK,
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('uses both fallbacks and skips the capacity clamp with no tranche at all', () => {
|
|
95
|
-
expect(resolveDepositBounds(undefined)).toEqual({
|
|
96
|
-
minDeposit: MIN_LENDING_AMOUNT_FALLBACK,
|
|
97
|
-
maxDeposit: MAX_LENDING_AMOUNT_FALLBACK,
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('treats unparseable or negative capacity as zero', () => {
|
|
102
|
-
expect(
|
|
103
|
-
resolveDepositBounds(tranche({ poolCapacity: 'not-a-number' }))
|
|
104
|
-
.maxDeposit,
|
|
105
|
-
).toBe(0);
|
|
106
|
-
expect(
|
|
107
|
-
resolveDepositBounds(tranche({ poolCapacity: '-5' })).maxDeposit,
|
|
108
|
-
).toBe(0);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('cent snapping', () => {
|
|
113
|
-
it('floorToCents never exceeds its input, stays within a cent of it, and has ≤ 2dp', () => {
|
|
114
|
-
fc.assert(
|
|
115
|
-
fc.property(amountArb, (n) => {
|
|
116
|
-
const snapped = floorToCents(n);
|
|
117
|
-
expect(snapped).toBeLessThanOrEqual(n);
|
|
118
|
-
expect(n - snapped).toBeLessThan(0.02);
|
|
119
|
-
expect(String(snapped)).toMatch(AT_MOST_TWO_DP);
|
|
120
|
-
}),
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('ceilToCents never undershoots its input, stays within a cent of it, and has ≤ 2dp', () => {
|
|
125
|
-
fc.assert(
|
|
126
|
-
fc.property(amountArb, (n) => {
|
|
127
|
-
const snapped = ceilToCents(n);
|
|
128
|
-
expect(snapped).toBeGreaterThanOrEqual(n);
|
|
129
|
-
expect(snapped - n).toBeLessThan(0.02);
|
|
130
|
-
expect(String(snapped)).toMatch(AT_MOST_TWO_DP);
|
|
131
|
-
}),
|
|
132
|
-
);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('resolveBoundShortcuts', () => {
|
|
137
|
-
it('keeps both shortcuts inside the validated range whenever they are enabled', () => {
|
|
138
|
-
fc.assert(
|
|
139
|
-
fc.property(amountArb, amountArb, (minDeposit, maxDeposit) => {
|
|
140
|
-
const { min, max, enabled } = resolveBoundShortcuts(
|
|
141
|
-
minDeposit,
|
|
142
|
-
maxDeposit,
|
|
143
|
-
);
|
|
144
|
-
if (!enabled) return;
|
|
145
|
-
expect(min).toBeGreaterThanOrEqual(minDeposit);
|
|
146
|
-
expect(max).toBeLessThanOrEqual(maxDeposit);
|
|
147
|
-
expect(max).toBeGreaterThanOrEqual(min);
|
|
148
|
-
expect(max).toBeGreaterThan(0);
|
|
149
|
-
}),
|
|
150
|
-
);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('disables the pair whenever the snapped range is empty', () => {
|
|
154
|
-
// Tranche at capacity (max clamps to 0), sub-cent dust the SDK's float
|
|
155
|
-
// subtraction leaves behind, remaining capacity below the tranche
|
|
156
|
-
// minimum, and an AU floor above the capacity.
|
|
157
|
-
expect(resolveBoundShortcuts(500, 0).enabled).toBe(false);
|
|
158
|
-
expect(
|
|
159
|
-
resolveBoundShortcuts(500, 1.4551915228366852e-11).enabled,
|
|
160
|
-
).toBe(false);
|
|
161
|
-
expect(resolveBoundShortcuts(500, 137).enabled).toBe(false);
|
|
162
|
-
expect(resolveBoundShortcuts(240_000, 100_000).enabled).toBe(false);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('snaps SDK float noise onto cents without crossing the bound', () => {
|
|
166
|
-
expect(resolveBoundShortcuts(500, 249999.99999999997)).toEqual({
|
|
167
|
-
min: 500,
|
|
168
|
-
max: 249999.99,
|
|
169
|
-
enabled: true,
|
|
170
|
-
});
|
|
171
|
-
expect(resolveBoundShortcuts(500, 25285.86)).toEqual({
|
|
172
|
-
min: 500,
|
|
173
|
-
max: 25285.86,
|
|
174
|
-
enabled: true,
|
|
175
|
-
});
|
|
176
|
-
// An AU remainder with more precision than the field shows rounds UP.
|
|
177
|
-
expect(resolveBoundShortcuts(358765.432109, 500_000).min).toBe(
|
|
178
|
-
358765.44,
|
|
179
|
-
);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('isBelowMinimumCapacity', () => {
|
|
184
|
-
it('is exactly the negation of the shortcuts being enabled', () => {
|
|
185
|
-
fc.assert(
|
|
186
|
-
fc.property(amountArb, amountArb, (minDeposit, maxDeposit) => {
|
|
187
|
-
// The "nothing to click" and "nothing to type" states must
|
|
188
|
-
// never be able to disagree.
|
|
189
|
-
expect(isBelowMinimumCapacity(minDeposit, maxDeposit)).toBe(
|
|
190
|
-
!resolveBoundShortcuts(minDeposit, maxDeposit).enabled,
|
|
191
|
-
);
|
|
192
|
-
}),
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('flags a capacity that has fallen under the tranche minimum', () => {
|
|
197
|
-
expect(isBelowMinimumCapacity(500, 137)).toBe(true);
|
|
198
|
-
expect(isBelowMinimumCapacity(500, 25285.86)).toBe(false);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
APXIUM,
|
|
3
|
-
getCreditOriginator,
|
|
4
|
-
getInstitutionalLender,
|
|
5
|
-
INVOICEMATE,
|
|
6
|
-
RIXON_CAPITAL,
|
|
7
|
-
} from './partners';
|
|
8
|
-
|
|
9
|
-
// Ported from kasu-ui `src/features/lending/lib/strategy-partners.test.ts`.
|
|
10
|
-
|
|
11
|
-
describe('getCreditOriginator', () => {
|
|
12
|
-
it('maps "Payment Finance (PayFi)" to InvoiceMate', () => {
|
|
13
|
-
expect(getCreditOriginator({ poolName: 'Payment Finance (PayFi)' })).toBe(
|
|
14
|
-
INVOICEMATE,
|
|
15
|
-
);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('keeps Apxium receivables pools as Apxium (regression: do not match "invoice")', () => {
|
|
19
|
-
// The bug: matching the asset class ("invoice financing") wrongly
|
|
20
|
-
// classified Apxium receivables pools as InvoiceMate. Name-only
|
|
21
|
-
// matching fixes it — none of these names contain payfi/payment
|
|
22
|
-
// finance.
|
|
23
|
-
expect(getCreditOriginator({ poolName: 'Professional Fee Funding' })).toBe(
|
|
24
|
-
APXIUM,
|
|
25
|
-
);
|
|
26
|
-
expect(getCreditOriginator({ poolName: 'Whole Ledger Funding' })).toBe(
|
|
27
|
-
APXIUM,
|
|
28
|
-
);
|
|
29
|
-
expect(
|
|
30
|
-
getCreditOriginator({ poolName: 'Taxation Funding (Tax Pay)' }),
|
|
31
|
-
).toBe(APXIUM);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('is case-insensitive and matches the "payfi" / "payment finance" name', () => {
|
|
35
|
-
expect(getCreditOriginator({ poolName: 'payment finance' })).toBe(
|
|
36
|
-
INVOICEMATE,
|
|
37
|
-
);
|
|
38
|
-
expect(getCreditOriginator({ poolName: 'PAYFI Strategy' })).toBe(
|
|
39
|
-
INVOICEMATE,
|
|
40
|
-
);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('defaults to Apxium for empty / missing names', () => {
|
|
44
|
-
expect(getCreditOriginator({ poolName: '' })).toBe(APXIUM);
|
|
45
|
-
expect(getCreditOriginator({})).toBe(APXIUM);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('getCreditOriginator — the widened PoolNameSignal', () => {
|
|
50
|
-
it('reads the facade Strategy shape (`name`)', () => {
|
|
51
|
-
expect(getCreditOriginator({ name: 'Payment Finance (PayFi)' })).toBe(
|
|
52
|
-
INVOICEMATE,
|
|
53
|
-
);
|
|
54
|
-
expect(getCreditOriginator({ name: 'Taxation Funding' })).toBe(APXIUM);
|
|
55
|
-
expect(getCreditOriginator({ name: null })).toBe(APXIUM);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('reads a bare string', () => {
|
|
59
|
-
expect(getCreditOriginator('Payment Finance (PayFi)')).toBe(INVOICEMATE);
|
|
60
|
-
expect(getCreditOriginator('Whole Ledger Funding')).toBe(APXIUM);
|
|
61
|
-
expect(getCreditOriginator('')).toBe(APXIUM);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('prefers poolName when an object carries both', () => {
|
|
65
|
-
const both = { poolName: 'Taxation Funding', name: 'Payment Finance' };
|
|
66
|
-
expect(getCreditOriginator(both)).toBe(APXIUM);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('treats an explicitly null poolName as missing, not as a match', () => {
|
|
70
|
-
expect(getCreditOriginator({ poolName: null })).toBe(APXIUM);
|
|
71
|
-
expect(getCreditOriginator({ poolName: undefined })).toBe(APXIUM);
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('getInstitutionalLender', () => {
|
|
76
|
-
it('gives Apxium pools Rixon Capital', () => {
|
|
77
|
-
expect(getInstitutionalLender(APXIUM)).toBe(RIXON_CAPITAL);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('gives InvoiceMate pools no senior lender', () => {
|
|
81
|
-
expect(getInstitutionalLender(INVOICEMATE)).toBeNull();
|
|
82
|
-
});
|
|
83
|
-
});
|