diff --git a/packages/fast-usdc/test/fast-usdc.contract.test.ts b/packages/fast-usdc/test/fast-usdc.contract.test.ts index 38e80e76e71..7ec53744173 100644 --- a/packages/fast-usdc/test/fast-usdc.contract.test.ts +++ b/packages/fast-usdc/test/fast-usdc.contract.test.ts @@ -317,235 +317,6 @@ const makeLP = async ( return me; }; -test.skip('LP borrow - TODO: move to exo test', async t => { - const common = await commonSetup(t); - const { - brands: { usdc }, - utils, - } = common; - - const { instance, creatorFacet, zoe, metricsSub, terms } = - await startContract(common); - - const usdcPurse = purseOf(terms.issuers.USDC, utils); - const lps = { - alice: makeLP('Alice', usdcPurse(100n), zoe, instance), - }; - // seed pool with funds - await E(lps.alice).deposit(t, 100n); - - const { value } = await E(metricsSub).getUpdateSince(); - const { shareWorth, encumberedBalance } = value; - const poolSeatAllocation = subtract( - subtract(shareWorth.numerator, encumberedBalance), - usdc.make(1n), - ); - t.log('Attempting to borrow entire pool seat allocation', poolSeatAllocation); - await t.throwsAsync( - E(creatorFacet).testBorrow({ USDC: poolSeatAllocation }), - { - message: /Cannot borrow/, - }, - 'borrow fails when requested equals pool seat allocation', - ); - - await t.throwsAsync( - E(creatorFacet).testBorrow({ USDC: usdc.make(200n) }), - { - message: /Cannot borrow/, - }, - 'borrow fails when requested exceeds pool seat allocation', - ); - - await t.throwsAsync(E(creatorFacet).testBorrow({ USDC: usdc.make(0n) }), { - message: /arg 1: USDC: value: "\[0n\]" - Must be >= "\[1n\]"/, - }); - - await t.throwsAsync( - E(creatorFacet).testBorrow( - // @ts-expect-error intentionally incorrect KW - { Fee: usdc.make(1n) }, - ), - { - message: /Must have missing properties \["USDC"\]/, - }, - ); - - // LPs can still withdraw (contract did not shutdown) - await E(lps.alice).withdraw(t, 0.5); - - const amt = await E(creatorFacet).testBorrow({ USDC: usdc.make(30n) }); - t.deepEqual(amt, { USDC: usdc.make(30n) }, 'borrow succeeds'); - - await eventLoopIteration(); - t.like(await E(metricsSub).getUpdateSince(), { - value: { - encumberedBalance: { - value: 30n, - }, - totalBorrows: { - value: 30n, - }, - totalRepays: { - value: 0n, - }, - }, - }); -}); - -test.skip('LP repay - TODO: move to exo test', async t => { - const common = await commonSetup(t); - const { - commonPrivateArgs, - brands: { usdc }, - utils, - } = common; - - const { instance, creatorFacet, zoe, metricsSub, terms } = - await startContract(common); - const usdcPurse = purseOf(terms.issuers.USDC, utils); - const lps = { - alice: makeLP('Alice', usdcPurse(100n), zoe, instance), - }; - // seed pool with funds - await E(lps.alice).deposit(t, 100n); - - // borrow funds from pool to increase encumbered balance - await E(creatorFacet).testBorrow({ USDC: usdc.make(50n) }); - const feeTools = makeFeeTools(commonPrivateArgs.feeConfig); - { - t.log('cannot repay more than encumbered balance'); - const repayAmounts = feeTools.calculateSplit(usdc.make(100n)); - const repayPayments = await deeplyFulfilledObject( - objectMap(repayAmounts, utils.pourPayment), - ); - await t.throwsAsync( - E(creatorFacet).testRepay(repayAmounts, repayPayments), - { - message: /Cannot repay. Principal .* exceeds encumberedBalance/, - }, - ); - } - - { - const pmt = utils.pourPayment(usdc.make(50n)); - await t.throwsAsync( - E(creatorFacet).testRepay( - // @ts-expect-error intentionally incorrect KWR - { USDC: usdc.make(50n) }, - { USDC: pmt }, - ), - { - message: - /Must have missing properties \["Principal","PoolFee","ContractFee"\]/, - }, - ); - } - { - const pmt = utils.pourPayment(usdc.make(50n)); - await t.throwsAsync( - E(creatorFacet).testRepay( - // @ts-expect-error intentionally incorrect KWR - { Principal: usdc.make(50n) }, - { Principal: pmt }, - ), - { - message: /Must have missing properties \["PoolFee","ContractFee"\]/, - }, - ); - } - { - const amts = { - Principal: usdc.make(0n), - ContractFee: usdc.make(0n), - PoolFee: usdc.make(0n), - }; - const pmts = await deeplyFulfilledObject( - objectMap(amts, utils.pourPayment), - ); - await t.throwsAsync(E(creatorFacet).testRepay(amts, pmts), { - message: /arg 1: Principal: value: "\[0n\]" - Must be >= "\[1n\]"/, - }); - } - - { - t.log('repay fails when amounts do not match seat allocation'); - const amts = { - Principal: usdc.make(25n), - ContractFee: usdc.make(1n), - PoolFee: usdc.make(2n), - }; - const pmts = await deeplyFulfilledObject( - harden({ - Principal: utils.pourPayment(usdc.make(24n)), - ContractFee: utils.pourPayment(usdc.make(1n)), - PoolFee: utils.pourPayment(usdc.make(2n)), - }), - ); - await t.throwsAsync(E(creatorFacet).testRepay(amts, pmts), { - message: /Cannot repay. From seat allocation .* does not equal amounts/, - }); - } - - { - t.log('repay succeeds with no Pool or Contract Fee'); - const amts = { - Principal: usdc.make(25n), - ContractFee: usdc.make(0n), - PoolFee: usdc.make(0n), - }; - const pmts = await deeplyFulfilledObject( - objectMap(amts, utils.pourPayment), - ); - const repayResult = await E(creatorFacet).testRepay(amts, pmts); - - for (const r of Object.values(repayResult)) { - t.is(r.value, 0n, 'testRepay consumes all payments'); - } - } - - const amts = { - Principal: usdc.make(25n), - ContractFee: usdc.make(1n), - PoolFee: usdc.make(2n), - }; - const pmts = await deeplyFulfilledObject(objectMap(amts, utils.pourPayment)); - const repayResult = await E(creatorFacet).testRepay(amts, pmts); - - for (const r of Object.values(repayResult)) { - t.is(r.value, 0n, 'testRepay consumes all payments'); - } - - await eventLoopIteration(); - t.like(await E(metricsSub).getUpdateSince(), { - value: { - encumberedBalance: { - value: 0n, - }, - totalBorrows: { - value: 50n, - }, - totalRepays: { - value: 50n, - }, - totalContractFees: { - value: 1n, - }, - totalPoolFees: { - value: 2n, - }, - shareWorth: { - numerator: { - value: 103n, // 100n (alice lp) + 1n (dust) + 2n (pool fees) - }, - }, - }, - }); - - // LPs can still withdraw (contract did not shutdown) - await E(lps.alice).withdraw(t, 1); -}); - const makeEVM = (template = MockCctpTxEvidences.AGORIC_PLUS_OSMO()) => { const [settleAddr] = template.aux.recipientAddress.split('?'); let nonce = 0; diff --git a/packages/fast-usdc/test/pool-share-math.test.ts b/packages/fast-usdc/test/pool-share-math.test.ts index 65870aaecfa..d1ce9e570b4 100644 --- a/packages/fast-usdc/test/pool-share-math.test.ts +++ b/packages/fast-usdc/test/pool-share-math.test.ts @@ -494,3 +494,29 @@ test('repay fails when seat allocation does not equal amounts', t => { }, ); }); + +test('repay succeeds with no Pool or Contract Fee', t => { + const { USDC } = brands; + const encumberedBalance = make(USDC, 100n); + const shareWorth = makeParity(make(USDC, 1n), brands.PoolShares); + + const amounts = { + Principal: make(USDC, 25n), + ContractFee: make(USDC, 0n), + PoolFee: make(USDC, 0n), + }; + const poolStats = { + ...makeInitialPoolStats(), + totalBorrows: make(USDC, 100n), + }; + const fromSeatAllocation = amounts; + t.notThrows(() => + repayCalc( + shareWorth, + fromSeatAllocation, + amounts, + encumberedBalance, + poolStats, + ), + ); +});