Skip to content

Commit

Permalink
feat(fast-usdc): add cli config and args for deposit and withdraw (#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
samsiegart authored Nov 15, 2024
1 parent 47bebb8 commit fb2d05c
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 20 deletions.
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',
]);

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

0 comments on commit fb2d05c

Please sign in to comment.