Skip to content

Commit 11c895a

Browse files
authored
chore(fast-usdc): prune testBorrow, testRepay methods (#10607)
closes: #10388 refs: #10511 ## Description Prune `testBorrow`, `testRepay` methods along with contract tests that depend on them. - Several tests are largely covered by share-pool-math tests - 1 was not, so I made a pool-share-math test to cover _repay succeeds with no Pool or Contract Fee_. - Several tests basically checked the interface guards of `lp.borrower.borrow` / `lp.repayer.repay`. These are internal APIs with static types. - testing consistency between interface guards and static types might have some value, but, I suggest, not enough for the cost ### Scaling / Documentation / Upgrade Considerations none ### Security / Testing Considerations small loss in test coverage - mostly in test redundancy `makeTestPushInvitation` remains on the public facet. Getting rid of it in due course remains critical. I expect / hope we can get rid of it in #10606 (cc @samsiegart ) . Ideally, the liquidity-pool exo would have unit test coverage. I looked into that but found that I would have to build substantial ZCF / Zoe test tooling. Since `liquidity-pool.js` is just 360 lines of straightforward Zoe API usage, I suggest we postpone that under... - #10558
2 parents ef82fbf + d96a45e commit 11c895a

File tree

3 files changed

+35
-268
lines changed

3 files changed

+35
-268
lines changed

packages/fast-usdc/src/fast-usdc.contract.js

+5-39
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { AssetKind } from '@agoric/ertp';
2-
import {
3-
assertAllDefined,
4-
deeplyFulfilledObject,
5-
makeTracer,
6-
} from '@agoric/internal';
2+
import { assertAllDefined, makeTracer } from '@agoric/internal';
73
import { observeIteration, subscribeEach } from '@agoric/notifier';
84
import {
95
CosmosChainInfoShape,
@@ -12,20 +8,19 @@ import {
128
registerChainsAndAssets,
139
withOrchestration,
1410
} from '@agoric/orchestration';
11+
import { makeZoeTools } from '@agoric/orchestration/src/utils/zoe-tools.js';
1512
import { provideSingleton } from '@agoric/zoe/src/contractSupport/durability.js';
1613
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
17-
import { makeZoeTools } from '@agoric/orchestration/src/utils/zoe-tools.js';
18-
import { depositToSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
1914
import { E } from '@endo/far';
20-
import { M, objectMap } from '@endo/patterns';
15+
import { M } from '@endo/patterns';
2116
import { prepareAdvancer } from './exos/advancer.js';
2217
import { prepareLiquidityPoolKit } from './exos/liquidity-pool.js';
2318
import { prepareSettler } from './exos/settler.js';
2419
import { prepareStatusManager } from './exos/status-manager.js';
2520
import { prepareTransactionFeedKit } from './exos/transaction-feed.js';
26-
import { defineInertInvitation } from './utils/zoe.js';
27-
import { FastUSDCTermsShape, FeeConfigShape } from './type-guards.js';
2821
import * as flows from './fast-usdc.flows.js';
22+
import { FastUSDCTermsShape, FeeConfigShape } from './type-guards.js';
23+
import { defineInertInvitation } from './utils/zoe.js';
2924

3025
const trace = makeTracer('FastUsdc');
3126

@@ -151,35 +146,6 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
151146
async makeOperatorInvitation(operatorId) {
152147
return feedKit.creator.makeOperatorInvitation(operatorId);
153148
},
154-
/**
155-
* @param {{ USDC: Amount<'nat'>}} amounts
156-
*/
157-
testBorrow(amounts) {
158-
console.log('🚧🚧 UNTIL: borrow is integrated (#10388) 🚧🚧', amounts);
159-
const { zcfSeat: tmpAssetManagerSeat } = zcf.makeEmptySeatKit();
160-
poolKit.borrower.borrow(tmpAssetManagerSeat, amounts);
161-
return tmpAssetManagerSeat.getCurrentAllocation();
162-
},
163-
/**
164-
*
165-
* @param {RepayAmountKWR} amounts
166-
* @param {RepayPaymentKWR} payments
167-
* @returns {Promise<AmountKeywordRecord>}
168-
*/
169-
async testRepay(amounts, payments) {
170-
console.log('🚧🚧 UNTIL: repay is integrated (#10388) 🚧🚧', amounts);
171-
const { zcfSeat: tmpAssetManagerSeat } = zcf.makeEmptySeatKit();
172-
await depositToSeat(
173-
zcf,
174-
tmpAssetManagerSeat,
175-
await deeplyFulfilledObject(
176-
objectMap(payments, pmt => E(terms.issuers.USDC).getAmountOf(pmt)),
177-
),
178-
payments,
179-
);
180-
poolKit.repayer.repay(tmpAssetManagerSeat, amounts);
181-
return tmpAssetManagerSeat.getCurrentAllocation();
182-
},
183149
});
184150

185151
const publicFacet = zone.exo('Fast USDC Public', undefined, {

packages/fast-usdc/test/fast-usdc.contract.test.ts

-229
Original file line numberDiff line numberDiff line change
@@ -317,235 +317,6 @@ const makeLP = async (
317317
return me;
318318
};
319319

320-
test.skip('LP borrow - TODO: move to exo test', async t => {
321-
const common = await commonSetup(t);
322-
const {
323-
brands: { usdc },
324-
utils,
325-
} = common;
326-
327-
const { instance, creatorFacet, zoe, metricsSub, terms } =
328-
await startContract(common);
329-
330-
const usdcPurse = purseOf(terms.issuers.USDC, utils);
331-
const lps = {
332-
alice: makeLP('Alice', usdcPurse(100n), zoe, instance),
333-
};
334-
// seed pool with funds
335-
await E(lps.alice).deposit(t, 100n);
336-
337-
const { value } = await E(metricsSub).getUpdateSince();
338-
const { shareWorth, encumberedBalance } = value;
339-
const poolSeatAllocation = subtract(
340-
subtract(shareWorth.numerator, encumberedBalance),
341-
usdc.make(1n),
342-
);
343-
t.log('Attempting to borrow entire pool seat allocation', poolSeatAllocation);
344-
await t.throwsAsync(
345-
E(creatorFacet).testBorrow({ USDC: poolSeatAllocation }),
346-
{
347-
message: /Cannot borrow/,
348-
},
349-
'borrow fails when requested equals pool seat allocation',
350-
);
351-
352-
await t.throwsAsync(
353-
E(creatorFacet).testBorrow({ USDC: usdc.make(200n) }),
354-
{
355-
message: /Cannot borrow/,
356-
},
357-
'borrow fails when requested exceeds pool seat allocation',
358-
);
359-
360-
await t.throwsAsync(E(creatorFacet).testBorrow({ USDC: usdc.make(0n) }), {
361-
message: /arg 1: USDC: value: "\[0n\]" - Must be >= "\[1n\]"/,
362-
});
363-
364-
await t.throwsAsync(
365-
E(creatorFacet).testBorrow(
366-
// @ts-expect-error intentionally incorrect KW
367-
{ Fee: usdc.make(1n) },
368-
),
369-
{
370-
message: /Must have missing properties \["USDC"\]/,
371-
},
372-
);
373-
374-
// LPs can still withdraw (contract did not shutdown)
375-
await E(lps.alice).withdraw(t, 0.5);
376-
377-
const amt = await E(creatorFacet).testBorrow({ USDC: usdc.make(30n) });
378-
t.deepEqual(amt, { USDC: usdc.make(30n) }, 'borrow succeeds');
379-
380-
await eventLoopIteration();
381-
t.like(await E(metricsSub).getUpdateSince(), {
382-
value: {
383-
encumberedBalance: {
384-
value: 30n,
385-
},
386-
totalBorrows: {
387-
value: 30n,
388-
},
389-
totalRepays: {
390-
value: 0n,
391-
},
392-
},
393-
});
394-
});
395-
396-
test.skip('LP repay - TODO: move to exo test', async t => {
397-
const common = await commonSetup(t);
398-
const {
399-
commonPrivateArgs,
400-
brands: { usdc },
401-
utils,
402-
} = common;
403-
404-
const { instance, creatorFacet, zoe, metricsSub, terms } =
405-
await startContract(common);
406-
const usdcPurse = purseOf(terms.issuers.USDC, utils);
407-
const lps = {
408-
alice: makeLP('Alice', usdcPurse(100n), zoe, instance),
409-
};
410-
// seed pool with funds
411-
await E(lps.alice).deposit(t, 100n);
412-
413-
// borrow funds from pool to increase encumbered balance
414-
await E(creatorFacet).testBorrow({ USDC: usdc.make(50n) });
415-
const feeTools = makeFeeTools(commonPrivateArgs.feeConfig);
416-
{
417-
t.log('cannot repay more than encumbered balance');
418-
const repayAmounts = feeTools.calculateSplit(usdc.make(100n));
419-
const repayPayments = await deeplyFulfilledObject(
420-
objectMap(repayAmounts, utils.pourPayment),
421-
);
422-
await t.throwsAsync(
423-
E(creatorFacet).testRepay(repayAmounts, repayPayments),
424-
{
425-
message: /Cannot repay. Principal .* exceeds encumberedBalance/,
426-
},
427-
);
428-
}
429-
430-
{
431-
const pmt = utils.pourPayment(usdc.make(50n));
432-
await t.throwsAsync(
433-
E(creatorFacet).testRepay(
434-
// @ts-expect-error intentionally incorrect KWR
435-
{ USDC: usdc.make(50n) },
436-
{ USDC: pmt },
437-
),
438-
{
439-
message:
440-
/Must have missing properties \["Principal","PoolFee","ContractFee"\]/,
441-
},
442-
);
443-
}
444-
{
445-
const pmt = utils.pourPayment(usdc.make(50n));
446-
await t.throwsAsync(
447-
E(creatorFacet).testRepay(
448-
// @ts-expect-error intentionally incorrect KWR
449-
{ Principal: usdc.make(50n) },
450-
{ Principal: pmt },
451-
),
452-
{
453-
message: /Must have missing properties \["PoolFee","ContractFee"\]/,
454-
},
455-
);
456-
}
457-
{
458-
const amts = {
459-
Principal: usdc.make(0n),
460-
ContractFee: usdc.make(0n),
461-
PoolFee: usdc.make(0n),
462-
};
463-
const pmts = await deeplyFulfilledObject(
464-
objectMap(amts, utils.pourPayment),
465-
);
466-
await t.throwsAsync(E(creatorFacet).testRepay(amts, pmts), {
467-
message: /arg 1: Principal: value: "\[0n\]" - Must be >= "\[1n\]"/,
468-
});
469-
}
470-
471-
{
472-
t.log('repay fails when amounts do not match seat allocation');
473-
const amts = {
474-
Principal: usdc.make(25n),
475-
ContractFee: usdc.make(1n),
476-
PoolFee: usdc.make(2n),
477-
};
478-
const pmts = await deeplyFulfilledObject(
479-
harden({
480-
Principal: utils.pourPayment(usdc.make(24n)),
481-
ContractFee: utils.pourPayment(usdc.make(1n)),
482-
PoolFee: utils.pourPayment(usdc.make(2n)),
483-
}),
484-
);
485-
await t.throwsAsync(E(creatorFacet).testRepay(amts, pmts), {
486-
message: /Cannot repay. From seat allocation .* does not equal amounts/,
487-
});
488-
}
489-
490-
{
491-
t.log('repay succeeds with no Pool or Contract Fee');
492-
const amts = {
493-
Principal: usdc.make(25n),
494-
ContractFee: usdc.make(0n),
495-
PoolFee: usdc.make(0n),
496-
};
497-
const pmts = await deeplyFulfilledObject(
498-
objectMap(amts, utils.pourPayment),
499-
);
500-
const repayResult = await E(creatorFacet).testRepay(amts, pmts);
501-
502-
for (const r of Object.values(repayResult)) {
503-
t.is(r.value, 0n, 'testRepay consumes all payments');
504-
}
505-
}
506-
507-
const amts = {
508-
Principal: usdc.make(25n),
509-
ContractFee: usdc.make(1n),
510-
PoolFee: usdc.make(2n),
511-
};
512-
const pmts = await deeplyFulfilledObject(objectMap(amts, utils.pourPayment));
513-
const repayResult = await E(creatorFacet).testRepay(amts, pmts);
514-
515-
for (const r of Object.values(repayResult)) {
516-
t.is(r.value, 0n, 'testRepay consumes all payments');
517-
}
518-
519-
await eventLoopIteration();
520-
t.like(await E(metricsSub).getUpdateSince(), {
521-
value: {
522-
encumberedBalance: {
523-
value: 0n,
524-
},
525-
totalBorrows: {
526-
value: 50n,
527-
},
528-
totalRepays: {
529-
value: 50n,
530-
},
531-
totalContractFees: {
532-
value: 1n,
533-
},
534-
totalPoolFees: {
535-
value: 2n,
536-
},
537-
shareWorth: {
538-
numerator: {
539-
value: 103n, // 100n (alice lp) + 1n (dust) + 2n (pool fees)
540-
},
541-
},
542-
},
543-
});
544-
545-
// LPs can still withdraw (contract did not shutdown)
546-
await E(lps.alice).withdraw(t, 1);
547-
});
548-
549320
const makeEVM = (template = MockCctpTxEvidences.AGORIC_PLUS_OSMO()) => {
550321
const [settleAddr] = template.aux.recipientAddress.split('?');
551322
let nonce = 0;

packages/fast-usdc/test/pool-share-math.test.ts

+30
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,33 @@ test('repay fails when seat allocation does not equal amounts', t => {
494494
},
495495
);
496496
});
497+
498+
test('repay succeeds with no Pool or Contract Fee', t => {
499+
const { USDC } = brands;
500+
const encumberedBalance = make(USDC, 100n);
501+
const shareWorth = makeParity(make(USDC, 1n), brands.PoolShares);
502+
503+
const amounts = {
504+
Principal: make(USDC, 25n),
505+
ContractFee: make(USDC, 0n),
506+
PoolFee: make(USDC, 0n),
507+
};
508+
const poolStats = {
509+
...makeInitialPoolStats(),
510+
totalBorrows: make(USDC, 100n),
511+
};
512+
const fromSeatAllocation = amounts;
513+
const actual = repayCalc(
514+
shareWorth,
515+
fromSeatAllocation,
516+
amounts,
517+
encumberedBalance,
518+
poolStats,
519+
);
520+
t.like(actual, {
521+
shareWorth,
522+
encumberedBalance: {
523+
value: 75n,
524+
},
525+
});
526+
});

0 commit comments

Comments
 (0)