Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fast-usdc): add cli config and args for deposit and withdraw #10487

Merged
merged 2 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 52 additions & 4 deletions packages/fast-usdc/src/cli/cli.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { Command } from 'commander';
import { assertParsableNumber } from '@agoric/zoe/src/contractSupport/ratio.js';
import {
Command,
InvalidArgumentError,
InvalidOptionArgumentError,
} from 'commander';
import { existsSync, mkdirSync, readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
Expand Down Expand Up @@ -69,19 +74,28 @@ export const initProgram = (
'--eth-seed <seed>',
'Seed phrase for Ethereum account. CAUTION: Stored unencrypted in file system',
)
.requiredOption(
'--agoric-seed <seed>',
'Seed phrase for Agoric LP account. CAUTION: Stored unencrypted in file system',
)
.option(
'--agoric-rpc [url]',
'Agoric RPC endpoint',
'http://127.0.0.1:26656',
)
.option(
'--agoric-api [url]',
'Agoric RPC endpoint',
'http://127.0.0.1:1317',
)
.option('--noble-rpc [url]', 'Noble RPC endpoint', 'http://127.0.0.1:26657')
.option('--noble-api [url]', 'Noble API endpoint', 'http://127.0.0.1:1318')
.option('--eth-rpc [url]', 'Ethereum RPC Endpoint', 'http://127.0.0.1:8545')
.option(
'--noble-to-agoric-channel [channel]',
'Channel ID on Noble for Agoric',
'channel-21',
)
.option('--noble-rpc [url]', 'Noble RPC endpoint', 'http://127.0.0.1:26657')
.option('--eth-rpc [url]', 'Ethereum RPC Endpoint', 'http://127.0.0.1:8545')
.option(
'--token-messenger-address [address]',
'Address of TokenMessenger contract',
Expand Down Expand Up @@ -109,10 +123,15 @@ export const initProgram = (
'--eth-seed [string]',
'Seed phrase for Ethereum account. CAUTION: Stored unencrypted in file system',
)
.option(
'--agoric-seed <seed>',
'Seed phrase for Agoric LP account. CAUTION: Stored unencrypted in file system',
)
.option('--agoric-rpc [url]', 'Agoric RPC endpoint')
.option('--agoric-api [url]', 'Agoric API endpoint')
.option('--noble-rpc [url]', 'Noble RPC endpoint')
.option('--eth-rpc [url]', 'Ethereum RPC Endpoint')
.option('--noble-api [url]', 'Noble API endpoint')
.option('--eth-rpc [url]', 'Ethereum RPC Endpoint')
.option(
'--noble-to-agoric-channel [channel]',
'Channel ID on Noble for Agoric',
Expand All @@ -129,9 +148,35 @@ export const initProgram = (
await configHelpers.update(makeConfigFile(), options);
});

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

/**
* @param {string} str
* @returns {'auto' | number}
*/
const parseFee = str => {
if (str === 'auto') return 'auto';
const num = parseFloat(str);
if (Number.isNaN(num)) {
throw new InvalidOptionArgumentError('Fee must be a number.');
}
return num;
};

program
.command('deposit')
.description('Offer assets to the liquidity pool')
.argument('<give>', 'USDC to give', parseDecimal)
.option('--id [offer-id]', 'Offer ID')
.option('--fee [fee]', 'Cosmos fee', parseFee)
.action(() => {
console.error('TODO actually send deposit');
// TODO: Implement deposit logic
Expand All @@ -140,6 +185,9 @@ export const initProgram = (
program
.command('withdraw')
.description('Withdraw assets from the liquidity pool')
.argument('<want>', 'USDC to withdraw', parseDecimal)
.option('--id [offer-id]', 'Offer ID')
.option('--fee [fee]', 'Cosmos fee', parseFee)
.action(() => {
console.error('TODO actually send withdrawal');
// TODO: Implement withdraw logic
Expand Down
114 changes: 113 additions & 1 deletion packages/fast-usdc/test/cli/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,115 @@ test('shows help for config show command', async t => {
t.snapshot(output);
});

test('shows help for deposit command', async t => {
const output = await collectStdOut([CLI_PATH, 'deposit', '-h']);

t.snapshot(output);
});

test('shows help for withdraw command', async t => {
const output = await collectStdOut([CLI_PATH, 'withdraw', '-h']);

t.snapshot(output);
});

test('shows error when deposit command is run without options', async t => {
const output = await collectStdErr([CLI_PATH, 'deposit']);

t.snapshot(output);
});

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

t.snapshot(output);
});

test('shows error when deposit command is run with invalid fee', async t => {
const output = await collectStdErr([
CLI_PATH,
'deposit',
'50',
'--fee',
'not-a-number',
]);
Comment on lines +133 to +139
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make these look more like command lines, consider...

Suggested change
const output = await collectStdErr([
CLI_PATH,
'deposit',
'50',
'--fee',
'not-a-number',
]);
const output = await collectStdErr([CLI_PATH, ...'deposit 50 --fee not-a-number'.split(' ')]);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really my preference, though I see where you're coming from.


t.snapshot(output);
});

test('shows error when withdraw command is run without options', async t => {
const output = await collectStdErr([CLI_PATH, 'withdraw']);

t.snapshot(output);
});

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

t.snapshot(output);
});

test('shows error when withdraw command is run with invalid fee', async t => {
const output = await collectStdErr([
CLI_PATH,
'withdraw',
'50',
'--fee',
'not-a-number',
]);

t.snapshot(output);
});

test('shows error when config init command is run without options', async t => {
const output = await collectStdErr([CLI_PATH, 'config', 'init']);

t.snapshot(output);
});

test('shows error when transfer command is run without options', async t => {
const output = await collectStdErr([CLI_PATH, 'transfer']);

t.snapshot(output);
});

test('shows error when config init command is run without eth seed', async t => {
const output = await collectStdErr([
CLI_PATH,
'config',
'init',
'--noble-seed',
'foo',
'--agoric-seed',
'bar',
]);

t.snapshot(output);
});

test('shows error when config init command is run without agoric seed', async t => {
const output = await collectStdErr([
CLI_PATH,
'config',
'init',
'--noble-seed',
'foo',
'--eth-seed',
'bar',
]);

t.snapshot(output);
});

test('shows error when config init command is run without noble seed', async t => {
const output = await collectStdErr([
CLI_PATH,
'config',
'init',
'--agoric-seed',
'foo',
'--eth-seed',
'bar',
]);

t.snapshot(output);
Expand All @@ -139,13 +235,17 @@ test('calls config init with default args', t => {
'foo',
'--eth-seed',
'bar',
'--agoric-seed',
'bazinga',
]);

const args = config.getInitArgs();
t.is(args.shift().path, `${homeDir}config.json`);
t.deepEqual(args, [
{
agoricRpc: 'http://127.0.0.1:1317',
agoricSeed: 'bazinga',
agoricApi: 'http://127.0.0.1:1317',
agoricRpc: 'http://127.0.0.1:26656',
ethRpc: 'http://127.0.0.1:8545',
ethSeed: 'bar',
nobleRpc: 'http://127.0.0.1:26657',
Expand Down Expand Up @@ -174,6 +274,10 @@ test('calls config init with optional args', t => {
'foo',
'--eth-seed',
'bar',
'--agoric-seed',
'bazinga',
'--agoric-api',
'127.0.0.1:0000',
'--agoric-rpc',
'127.0.0.1:1111',
'--eth-rpc',
Expand All @@ -194,6 +298,8 @@ test('calls config init with optional args', t => {
t.is(args.shift().path, `${homeDir}config.json`);
t.deepEqual(args, [
{
agoricApi: '127.0.0.1:0000',
agoricSeed: 'bazinga',
agoricRpc: '127.0.0.1:1111',
ethRpc: '127.0.0.1:2222',
ethSeed: 'bar',
Expand Down Expand Up @@ -223,6 +329,10 @@ test('calls config update with args', t => {
'foo',
'--eth-seed',
'bar',
'--agoric-seed',
'bazinga',
'--agoric-api',
'127.0.0.1:0000',
'--agoric-rpc',
'127.0.0.1:1111',
'--eth-rpc',
Expand All @@ -243,6 +353,8 @@ test('calls config update with args', t => {
t.is(args.shift().path, `${homeDir}config.json`);
t.deepEqual(args, [
{
agoricSeed: 'bazinga',
agoricApi: '127.0.0.1:0000',
agoricRpc: '127.0.0.1:1111',
ethRpc: '127.0.0.1:2222',
ethSeed: 'bar',
Expand Down
Loading
Loading