Skip to content

Commit 718453d

Browse files
committed
fix: handleDeposit fixes and test
1 parent faae00f commit 718453d

File tree

2 files changed

+259
-11
lines changed

2 files changed

+259
-11
lines changed

packages/portfolio-contract/test/deposit-tools.test.ts

Lines changed: 254 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { makeFakeBoard } from '@agoric/vats/tools/board-utils.js';
55
import { planDepositTransfers } from '../tools/portfolio-actors.js';
66
import { makeIssuerKit } from '@agoric/ertp';
77
import type { TargetAllocation } from '../src/type-guards.js';
8+
import { handleDeposit } from '@aglocal/portfolio-planner/src/plan-deposit.ts';
9+
import type { VstorageKit } from '@agoric/client-utils';
10+
import { SpectrumClient } from '@aglocal/portfolio-planner/src/spectrum-client.ts';
11+
import { CosmosRestClient } from '@aglocal/portfolio-planner/src/cosmos-rest-client.ts';
812

913
const { brand } = makeIssuerKit('USDC');
1014

@@ -39,7 +43,11 @@ test('planDepositTransfers works in a handful of cases', t => {
3943
Aave_Arbitrum: make(100n),
4044
Compound_Arbitrum: make(0n),
4145
};
42-
const targetAllocation2 = { USDN: 40, Aave_Arbitrum: 40, Compound_Arbitrum: 20 };
46+
const targetAllocation2: TargetAllocation = {
47+
USDN: 40n,
48+
Aave_Arbitrum: 40n,
49+
Compound_Arbitrum: 20n,
50+
};
4351

4452
const result2 = planDepositTransfers(
4553
deposit2,
@@ -63,7 +71,11 @@ test('planDepositTransfers works in a handful of cases', t => {
6371
Aave_Arbitrum: make(100n),
6472
Compound_Arbitrum: make(50n),
6573
};
66-
const targetAllocation3 = { USDN: 50, Aave_Arbitrum: 30, Compound_Arbitrum: 20 };
74+
const targetAllocation3: TargetAllocation = {
75+
USDN: 50n,
76+
Aave_Arbitrum: 30n,
77+
Compound_Arbitrum: 20n,
78+
};
6779

6880
const result3 = planDepositTransfers(
6981
deposit3,
@@ -89,7 +101,11 @@ test('planDepositTransfers works in a handful of cases', t => {
89101
Aave_Arbitrum: make(0n),
90102
Compound_Arbitrum: make(0n),
91103
};
92-
const targetAllocation4 = { USDN: 60, Aave_Arbitrum: 30, Compound_Arbitrum: 10 };
104+
const targetAllocation4: TargetAllocation = {
105+
USDN: 60n,
106+
Aave_Arbitrum: 30n,
107+
Compound_Arbitrum: 10n,
108+
};
93109

94110
const result4 = planDepositTransfers(
95111
deposit4,
@@ -107,7 +123,7 @@ test('planDepositTransfers works in a handful of cases', t => {
107123
// Test case 5: Single position target
108124
const deposit5 = make(1000n);
109125
const currentBalances5 = { USDN: make(500n) };
110-
const targetAllocation5 = { USDN: 100 };
126+
const targetAllocation5: TargetAllocation = { USDN: 100n };
111127

112128
const result5 = planDepositTransfers(
113129
deposit5,
@@ -120,3 +136,237 @@ test('planDepositTransfers works in a handful of cases', t => {
120136
USDN: make(1000n),
121137
});
122138
});
139+
140+
test('handleDeposit works with mocked dependencies', async t => {
141+
const make = value => AmountMath.make(brand, value);
142+
const deposit = make(1000n);
143+
const portfolioKey = 'test.portfolios.portfolio1' as const;
144+
145+
// Mock VstorageKit readPublished
146+
const mockReadPublished = async (path: string) => {
147+
if (path === portfolioKey) {
148+
return {
149+
positionKeys: ['USDN', 'Aave_Arbitrum', 'Compound_Arbitrum'],
150+
flowCount: 0,
151+
accountIdByChain: {
152+
noble: 'noble:test:addr1',
153+
Arbitrum: 'arbitrum:test:addr2',
154+
},
155+
targetAllocation: {
156+
USDN: 50n,
157+
Aave_Arbitrum: 30n,
158+
Compound_Arbitrum: 20n,
159+
},
160+
};
161+
}
162+
throw new Error(`Unexpected path: ${path}`);
163+
};
164+
165+
// Mock SpectrumClient
166+
class MockSpectrumClient extends SpectrumClient {
167+
async getPoolBalance(chain: any, pool: any, addr: any) {
168+
// Return different balances for different pools
169+
if (pool === 'aave' && chain === 'arbitrum') {
170+
return {
171+
pool,
172+
chain,
173+
address: addr,
174+
balance: { supplyBalance: 100, borrowAmount: 0 },
175+
};
176+
}
177+
if (pool === 'compound' && chain === 'arbitrum') {
178+
return {
179+
pool,
180+
chain,
181+
address: addr,
182+
balance: { supplyBalance: 50, borrowAmount: 0 },
183+
};
184+
}
185+
return {
186+
pool,
187+
chain,
188+
address: addr,
189+
balance: { supplyBalance: 0, borrowAmount: 0 },
190+
};
191+
}
192+
}
193+
const mockSpectrumClient = new MockSpectrumClient();
194+
195+
// Mock CosmosRestClient
196+
class MockCosmosRestClient extends CosmosRestClient {
197+
async getAccountBalance(chainName: string, addr: string, denom: string) {
198+
if (chainName === 'noble' && denom === 'usdn') {
199+
return { denom, amount: '200' };
200+
}
201+
return { denom, amount: '0' };
202+
}
203+
}
204+
const mockCosmosRestClient = new MockCosmosRestClient();
205+
206+
// Mock VstorageKit
207+
const mockVstorageKit: VstorageKit = {
208+
readPublished: mockReadPublished,
209+
} as VstorageKit;
210+
211+
try {
212+
await handleDeposit(
213+
deposit,
214+
portfolioKey,
215+
mockVstorageKit.readPublished,
216+
mockSpectrumClient,
217+
mockCosmosRestClient,
218+
);
219+
t.fail('Expected handleDeposit to throw Error with "moar TODO"');
220+
} catch (error) {
221+
t.is(error.message, 'moar TODO');
222+
}
223+
});
224+
225+
test('handleDeposit handles missing targetAllocation gracefully', async t => {
226+
const make = value => AmountMath.make(brand, value);
227+
const deposit = make(1000n);
228+
const portfolioKey = 'test.portfolios.portfolio1' as const;
229+
230+
// Mock VstorageKit readPublished with no targetAllocation
231+
const mockReadPublished = async (path: string) => {
232+
if (path === portfolioKey) {
233+
return {
234+
positionKeys: ['USDN', 'Aave_Arbitrum'],
235+
flowCount: 0,
236+
accountIdByChain: {
237+
noble: 'noble:test:addr1',
238+
Arbitrum: 'arbitrum:test:addr2',
239+
},
240+
// No targetAllocation
241+
};
242+
}
243+
throw new Error(`Unexpected path: ${path}`);
244+
};
245+
246+
// Mock SpectrumClient
247+
class MockSpectrumClient2 extends SpectrumClient {
248+
async getPoolBalance(chain: any, pool: any, addr: any) {
249+
return {
250+
pool,
251+
chain,
252+
address: addr,
253+
balance: { supplyBalance: 0, borrowAmount: 0 },
254+
};
255+
}
256+
}
257+
const mockSpectrumClient = new MockSpectrumClient2();
258+
259+
// Mock CosmosRestClient
260+
class MockCosmosRestClient2 extends CosmosRestClient {
261+
async getAccountBalance(chainName: string, addr: string, denom: string) {
262+
return { denom, amount: '0' };
263+
}
264+
}
265+
const mockCosmosRestClient = new MockCosmosRestClient2();
266+
267+
// Mock VstorageKit
268+
const mockVstorageKit: VstorageKit = {
269+
readPublished: mockReadPublished,
270+
} as VstorageKit;
271+
272+
const result = await handleDeposit(
273+
deposit,
274+
portfolioKey,
275+
mockVstorageKit.readPublished,
276+
mockSpectrumClient,
277+
mockCosmosRestClient,
278+
);
279+
280+
t.is(result, undefined);
281+
});
282+
283+
test('handleDeposit handles different position types correctly', async t => {
284+
const make = value => AmountMath.make(brand, value);
285+
const deposit = make(1000n);
286+
const portfolioKey = 'test.portfolios.portfolio1' as const;
287+
288+
// Mock VstorageKit readPublished with various position types
289+
const mockReadPublished = async (path: string) => {
290+
if (path === portfolioKey) {
291+
return {
292+
positionKeys: [
293+
'USDN',
294+
'USDNVault',
295+
'Aave_Avalanche',
296+
'Compound_Polygon',
297+
],
298+
flowCount: 0,
299+
accountIdByChain: {
300+
noble: 'noble:test:addr1',
301+
Avalanche: 'avalanche:test:addr2',
302+
Polygon: 'polygon:test:addr3',
303+
},
304+
targetAllocation: {
305+
USDN: 40n,
306+
USDNVault: 20n,
307+
Aave_Avalanche: 25n,
308+
Compound_Polygon: 15n,
309+
},
310+
};
311+
}
312+
throw new Error(`Unexpected path: ${path}`);
313+
};
314+
315+
// Mock SpectrumClient with different chain responses
316+
class MockSpectrumClient3 extends SpectrumClient {
317+
async getPoolBalance(chain: any, pool: any, addr: any) {
318+
if (chain === 'avalanche' && pool === 'aave') {
319+
return {
320+
pool,
321+
chain,
322+
address: addr,
323+
balance: { supplyBalance: 150, borrowAmount: 0 },
324+
};
325+
}
326+
if (chain === 'polygon' && pool === 'compound') {
327+
return {
328+
pool,
329+
chain,
330+
address: addr,
331+
balance: { supplyBalance: 75, borrowAmount: 0 },
332+
};
333+
}
334+
return {
335+
pool,
336+
chain,
337+
address: addr,
338+
balance: { supplyBalance: 0, borrowAmount: 0 },
339+
};
340+
}
341+
}
342+
const mockSpectrumClient = new MockSpectrumClient3();
343+
344+
// Mock CosmosRestClient
345+
class MockCosmosRestClient3 extends CosmosRestClient {
346+
async getAccountBalance(chainName: string, addr: string, denom: string) {
347+
if (chainName === 'noble' && denom === 'usdn') {
348+
return { denom, amount: '300' };
349+
}
350+
return { denom, amount: '0' };
351+
}
352+
}
353+
const mockCosmosRestClient = new MockCosmosRestClient3();
354+
355+
// Mock VstorageKit
356+
const mockVstorageKit: VstorageKit = {
357+
readPublished: mockReadPublished,
358+
} as VstorageKit;
359+
360+
try {
361+
await handleDeposit(
362+
deposit,
363+
portfolioKey,
364+
mockVstorageKit.readPublished,
365+
mockSpectrumClient,
366+
mockCosmosRestClient,
367+
);
368+
t.fail('Expected handleDeposit to throw Error with "moar TODO"');
369+
} catch (error) {
370+
t.is(error.message, 'moar TODO');
371+
}
372+
});

packages/portfolio-planner/src/plan-deposit.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import {
77
planDepositTransfers,
88
planTransfer,
99
} from '@aglocal/portfolio-contract/tools/portfolio-actors.ts';
10-
import { VstorageKit } from '@agoric/client-utils';
10+
import type { VstorageKit } from '@agoric/client-utils';
1111
import { AmountMath } from '@agoric/ertp/src/amountMath.js';
1212
import type { Brand, NatAmount } from '@agoric/ertp/src/types.ts';
13-
import { assert } from '@endo/errors';
14-
import { X, q } from '@endo/errors';
13+
import { X, q, assert } from '@endo/errors';
1514
import type { CosmosRestClient } from './cosmos-rest-client.ts';
1615
import type { Chain, Pool, SpectrumClient } from './spectrum-client.ts';
1716

@@ -32,7 +31,7 @@ export const getCurrentBalances = async (
3231
cosmosRest: CosmosRestClient,
3332
brand: Brand<'nat'>,
3433
) => {
35-
const pq = makePortfolioQuery(portfolioKey, readPublished);
34+
const pq = makePortfolioQuery(readPublished, portfolioKey);
3635
const status = await pq.getPortfolioStatus();
3736
const balances: Partial<Record<PoolKey, NatAmount>> = {};
3837

@@ -48,7 +47,7 @@ export const getCurrentBalances = async (
4847
addr,
4948
denom,
5049
);
51-
balances[posKey] = make(brand, amount);
50+
balances[posKey] = make(brand, BigInt(amount));
5251
break;
5352
}
5453
case 'Aave':
@@ -84,15 +83,14 @@ export const handleDeposit = async (
8483
cosmosRest,
8584
amount.brand,
8685
);
87-
const pq = makePortfolioQuery(portfolioKey, readPublished);
86+
const pq = makePortfolioQuery(readPublished, portfolioKey);
8887
const { targetAllocation } = await pq.getPortfolioStatus();
8988
if (!targetAllocation) {
9089
// XXX warn?
9190
return;
9291
}
9392
const txfrs = planDepositTransfers(amount, currentBalances, targetAllocation);
9493
const steps = [
95-
{ src: '<Deposit>', dest: '@agoric', amount },
9694
{ src: '@agoric', dest: '@noble', amount },
9795
...Object.entries(txfrs)
9896
// XXX Object.entries() type is goofy

0 commit comments

Comments
 (0)