Skip to content

Commit

Permalink
test(fast-usdc): prune testBorrow(...), testRepay(...) tests
Browse files Browse the repository at this point in the history
 - largely covered by share-pool-math tests
   - move 'repay succeeds with no Pool or Contract Fee'
     from contract test to pool-share-math test.
 - borrow / repay are internal APIs with static types
   - testing consistency between interface guards and static types
     might have some value, but not enough
  • Loading branch information
dckc committed Dec 3, 2024
1 parent cf56e91 commit 1f5f385
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 229 deletions.
229 changes: 0 additions & 229 deletions packages/fast-usdc/test/fast-usdc.contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 30 additions & 0 deletions packages/fast-usdc/test/pool-share-math.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,3 +494,33 @@ 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;
const actual = repayCalc(
shareWorth,
fromSeatAllocation,
amounts,
encumberedBalance,
poolStats,
);
t.like(actual, {
shareWorth,
encumberedBalance: {
value: 75n,
},
});
});

0 comments on commit 1f5f385

Please sign in to comment.