Skip to content

Commit

Permalink
fixup! feat(fast-usdc): cli for lp deposit and withdraw
Browse files Browse the repository at this point in the history
  • Loading branch information
samsiegart committed Dec 10, 2024
1 parent 9ec5c44 commit 2411b6a
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 42 deletions.
42 changes: 29 additions & 13 deletions packages/fast-usdc/src/cli/lp-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
*/

import { fetchEnvNetworkConfig, makeVstorageKit } from '@agoric/client-utils';
import { Nat } from '@endo/nat';
import { InvalidArgumentError } from 'commander';
import {
assertParsableNumber,
multiplyBy,
parseRatio,
} from '@agoric/zoe/src/contractSupport/ratio.js';
import { Nat } from '@endo/nat';
import { AmountMath } from '@agoric/ertp';
import { outputActionAndHint } from './bridge-action.js';

/** @param {string} arg */
Expand All @@ -19,6 +25,17 @@ const parseNat = arg => {
}
};

/** @param {string} arg */
const parseDecimal = arg => {
try {
assertParsableNumber(arg);
const n = Number(arg);
return n;
} catch {
throw new InvalidArgumentError('Not a number');
}
};

/**
* @param {Command} program
* @param {{
Expand All @@ -34,10 +51,6 @@ export const addLPCommands = (
program,
{ fetch, vstorageKit, stderr, stdout, env, now },
) => {
const operator = program
.command('lp')
.description('Liquidity Provider commands');

const loadVsk = async () => {
if (vstorageKit) {
return vstorageKit;
Expand All @@ -49,24 +62,27 @@ export const addLPCommands = (
/** @type {undefined | ReturnType<typeof loadVsk>} */
let vskP;

operator
program
.command('deposit')
.description('Deposit USDC into pool')
.addHelpText(
'after',
'\nPipe the STDOUT to a file such as deposit.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer accept.json --from gov1 --keyring-backend="test"',
'\nPipe the STDOUT to a file such as deposit.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer deposit.json --from gov1 --keyring-backend="test"',
)
.requiredOption('--amount <number>', 'uUSDC amount', parseNat)
.requiredOption('--amount <number>', 'USDC amount', parseDecimal)
.option('--offerId <string>', 'Offer id', String, `lpDeposit-${now()}`)
.action(async opts => {
vskP ||= loadVsk();
const vsk = await vskP;

/** @type {Brand<'nat'>} */
// @ts-expect-error it doesnt recognize usdc as a Brand type
const usdc = vsk.agoricNames.brand.USDC;
assert(usdc, 'USDC brand not in agoricNames');

const USDC_DECIMALS = 6;
const unit = AmountMath.make(usdc, 10n ** BigInt(USDC_DECIMALS));
const usdcAmount = multiplyBy(unit, parseRatio(opts.amount, usdc));

/** @type {OfferSpec} */
const offer = {
id: opts.offerId,
Expand All @@ -77,7 +93,7 @@ export const addLPCommands = (
},
proposal: {
give: {
USDC: { brand: usdc, value: opts.amount },
USDC: usdcAmount,
},
},
};
Expand All @@ -91,12 +107,12 @@ export const addLPCommands = (
outputActionAndHint(bridgeAction, { stderr, stdout }, vsk.marshaller);
});

operator
program
.command('withdraw')
.description("Withdraw USDC from the LP's pool share")
.addHelpText(
'after',
'\nPipe the STDOUT to a file such as withdraw.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer accept.json --from gov1 --keyring-backend="test"',
'\nPipe the STDOUT to a file such as withdraw.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer withdraw.json --from gov1 --keyring-backend="test"',
)
.requiredOption('--amount <number>', 'FastLP amount', parseNat)
.option('--offerId <string>', 'Offer id', String, `lpWithdraw-${now()}`)
Expand Down Expand Up @@ -131,5 +147,5 @@ export const addLPCommands = (
);
});

return operator;
return program;
};
4 changes: 4 additions & 0 deletions packages/fast-usdc/src/cli/operator-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ export const addOperatorCommands = (
operator
.command('attest')
.description('Attest to an observed Fast USDC transfer')
.addHelpText(
'after',
'\nPipe the STDOUT to a file such as attest.json, then use the Agoric CLI to broadcast it:\n agoric wallet send --offer attest.json --from gov1 --keyring-backend="test"',
)
.requiredOption('--previousOfferId <string>', 'Offer id', String)
.requiredOption('--forwardingChannel <string>', 'Channel id', String)
.requiredOption('--recipientAddress <string>', 'bech32 address', String)
Expand Down
8 changes: 4 additions & 4 deletions packages/fast-usdc/test/cli/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,22 @@ test('shows help for config show command', async t => {
});

test('shows error when deposit command is run without options', async t => {
const output = await runCli(['lp', 'deposit']);
const output = await runCli(['deposit']);
t.snapshot(output);
});

test('shows error when deposit command is run with invalid amount', async t => {
const output = await runCli(['lp', 'deposit', '--amount', 'not-a-number']);
const output = await runCli(['deposit', '--amount', 'not-a-number']);
t.snapshot(output);
});

test('shows error when withdraw command is run without options', async t => {
const output = await runCli(['lp', 'withdraw']);
const output = await runCli(['withdraw']);
t.snapshot(output);
});

test('shows error when withdraw command is run with invalid amount', async t => {
const output = await runCli(['lp', 'withdraw', '--amount', 'not-a-number']);
const output = await runCli(['withdraw', '--amount', 'not-a-number']);
t.snapshot(output);
});

Expand Down
45 changes: 21 additions & 24 deletions packages/fast-usdc/test/cli/lp-commands.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { makeMarshal } from '@endo/marshal';
import { Far, makeMarshal } from '@endo/marshal';
import anyTest, { type TestFn } from 'ava';
import { Command } from 'commander';
import { flags } from '../../tools/cli-tools.js';
Expand All @@ -11,20 +11,23 @@ const makeTestContext = () => {
const out = [] as string[];
const err = [] as string[];

const USDC = 'usdcbrand';
const FastLP = 'fastlpbrand';
const USDC = Far('usdcbrand');
const FastLP = Far('fastlpbrand');
const slotToVal = {
'0': USDC,
'1': FastLP,
};
const valToSlot = {
USDC: '0',
FastLP: '1',
const valToSlot = val => {
if (val === USDC) {
return '0';
}
if (val === FastLP) {
return '1';
}
return 'none';
};
const marshaller = makeMarshal(
val => valToSlot[val],
slot => slotToVal[slot],
);

const marshaller = makeMarshal(valToSlot, slot => slotToVal[slot]);
const now = () => 1234;

addLPCommands(program, {
Expand All @@ -46,28 +49,25 @@ const test = anyTest as TestFn<Awaited<ReturnType<typeof makeTestContext>>>;
test.beforeEach(async t => (t.context = await makeTestContext()));

test('fast-usdc lp deposit sub-command', async t => {
const { program, marshaller, out, err, USDC, now } = t.context;
const amount = 100;
const argv = [
...`node fast-usdc lp deposit`.split(' '),
...flags({ amount }),
];
const { program, marshaller, out, err, USDC } = t.context;
const amount = 100.05;
const argv = [...`node fast-usdc deposit`.split(' '), ...flags({ amount })];
t.log(...argv);
await program.parseAsync(argv);

const action = marshaller.fromCapData(JSON.parse(out.join('')));
t.deepEqual(action, {
method: 'executeOffer',
offer: {
id: `lpDeposit-${now()}`,
id: `lpDeposit-1234`,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeDepositInvitation', []]],
},
proposal: {
give: {
USDC: { brand: USDC, value: BigInt(amount) },
USDC: { brand: USDC, value: 100_050_000n },
},
},
},
Expand All @@ -82,26 +82,23 @@ test('fast-usdc lp deposit sub-command', async t => {
test('fast-usdc lp withdraw sub-command', async t => {
const { program, marshaller, out, err, FastLP, now } = t.context;
const amount = 100;
const argv = [
...`node fast-usdc lp withdraw`.split(' '),
...flags({ amount }),
];
const argv = [...`node fast-usdc withdraw`.split(' '), ...flags({ amount })];
t.log(...argv);
await program.parseAsync(argv);

const action = marshaller.fromCapData(JSON.parse(out.join('')));
t.deepEqual(action, {
method: 'executeOffer',
offer: {
id: `lpWithdraw-${now()}`,
id: `lpWithdraw-1234`,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeWithdrawInvitation', []]],
},
proposal: {
give: {
PoolShare: { brand: FastLP, value: BigInt(amount) },
PoolShare: { brand: FastLP, value: 100n },
},
},
},
Expand Down
3 changes: 2 additions & 1 deletion packages/fast-usdc/test/cli/snapshots/cli.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Generated by [AVA](https://avajs.dev).
Commands:␊
config Manage config␊
operator Oracle operator commands␊
lp Liquidity Provider commands␊
deposit [options] Deposit USDC into pool␊
withdraw [options] Withdraw USDC from the LP's pool share␊
transfer <amount> <dest> Transfer USDC from Ethereum/L2 to Cosmos via Fast␊
USDC␊
help [command] display help for command␊
Expand Down
Binary file modified packages/fast-usdc/test/cli/snapshots/cli.test.ts.snap
Binary file not shown.

0 comments on commit 2411b6a

Please sign in to comment.