Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into mfig-bech32-address…
Browse files Browse the repository at this point in the history
…-hooks
  • Loading branch information
michaelfig committed Dec 10, 2024
2 parents 967ced8 + c1ae023 commit 5023e23
Show file tree
Hide file tree
Showing 19 changed files with 422 additions and 260 deletions.
1 change: 0 additions & 1 deletion packages/boot/test/bootstrapTests/vaults-upgrade.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @jessie.js/safe-await-separator */
/**
* @file Bootstrap test integration vaults with smart-wallet. The tests in this
* file are NOT independent; a single `test.before()` handler creates shared
Expand Down
1 change: 0 additions & 1 deletion packages/boot/test/bootstrapTests/vtransfer.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @jessie.js/safe-await-separator -- confused by casting 'as' */
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import type { TestFn } from 'ava';
Expand Down
1 change: 0 additions & 1 deletion packages/boot/test/upgrading/upgrade-vats.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable @jessie.js/safe-await-separator -- test */
import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js';

import { BridgeId, deepCopyJsonable } from '@agoric/internal';
Expand Down
16 changes: 12 additions & 4 deletions packages/fast-usdc/src/cli/bridge-action.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import { boardSlottingMarshaller } from '@agoric/client-utils';
* @import {BridgeAction} from '@agoric/smart-wallet/src/smartWallet.js';
*/

const marshaller = boardSlottingMarshaller();
const defaultMarshaller = boardSlottingMarshaller();

/** @typedef {ReturnType<boardSlottingMarshaller>} BoardSlottingMarshaller */

/**
* @param {BridgeAction} bridgeAction
* @param {Pick<import('stream').Writable,'write'>} stdout
* @param {BoardSlottingMarshaller} marshaller
*/
const outputAction = (bridgeAction, stdout) => {
const outputAction = (bridgeAction, stdout, marshaller) => {
const capData = marshaller.toCapData(harden(bridgeAction));
stdout.write(JSON.stringify(capData));
stdout.write('\n');
Expand All @@ -25,8 +28,13 @@ export const sendHint =
* stdout: Pick<import('stream').Writable,'write'>,
* stderr: Pick<import('stream').Writable,'write'>,
* }} io
* @param {BoardSlottingMarshaller | undefined} marshaller
*/
export const outputActionAndHint = (bridgeAction, { stdout, stderr }) => {
outputAction(bridgeAction, stdout);
export const outputActionAndHint = (
bridgeAction,
{ stdout, stderr },
marshaller = defaultMarshaller,
) => {
outputAction(bridgeAction, stdout, marshaller);
stderr.write(sendHint);
};
54 changes: 3 additions & 51 deletions packages/fast-usdc/src/cli/cli.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
/* eslint-env node */
/* global globalThis */
import { assertParsableNumber } from '@agoric/zoe/src/contractSupport/ratio.js';
import {
Command,
InvalidArgumentError,
InvalidOptionArgumentError,
} from 'commander';
import { Command } from 'commander';
import { existsSync, mkdirSync, readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
Expand All @@ -19,6 +14,7 @@ import { addOperatorCommands } from './operator-commands.js';
import * as configLib from './config.js';
import transferLib from './transfer.js';
import { makeFile } from '../util/file.js';
import { addLPCommands } from './lp-commands.js';

const packageJson = JSON.parse(
readFileSync(
Expand Down Expand Up @@ -83,51 +79,7 @@ export const initProgram = (
env,
now,
});

/** @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
});

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
});
addLPCommands(program, { fetch, stdout, stderr, env, now });

program
.command('transfer')
Expand Down
171 changes: 171 additions & 0 deletions packages/fast-usdc/src/cli/lp-commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* @import {Command} from 'commander';
* @import {OfferSpec} from '@agoric/smart-wallet/src/offers.js';
* @import {ExecuteOfferAction} from '@agoric/smart-wallet/src/smartWallet.js';
* @import {USDCProposalShapes} from '../pool-share-math.js';
*/

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

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

/**
* @param {string} amountString
* @param {Brand} usdc
*/
const parseUSDCAmount = (amountString, usdc) => {
const USDC_DECIMALS = 6;
const unit = AmountMath.make(usdc, 10n ** BigInt(USDC_DECIMALS));
return multiplyBy(unit, parseRatio(amountString, usdc));
};

/**
* @param {Command} program
* @param {{
* fetch?: Window['fetch'];
* vstorageKit?: Awaited<ReturnType<typeof makeVstorageKit>>;
* stdout: typeof process.stdout;
* stderr: typeof process.stderr;
* env: typeof process.env;
* now: typeof Date.now;
* }} io
*/
export const addLPCommands = (
program,
{ fetch, vstorageKit, stderr, stdout, env, now },
) => {
const loadVsk = async () => {
if (vstorageKit) {
return vstorageKit;
}
assert(fetch);
const networkConfig = await fetchEnvNetworkConfig({ env, fetch });
return makeVstorageKit({ fetch }, networkConfig);
};
/** @type {undefined | ReturnType<typeof loadVsk>} */
let vskP;

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 deposit.json --from gov1 --keyring-backend="test"',
)
.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 usdcAmount = parseUSDCAmount(opts.amount, usdc);

/** @type {USDCProposalShapes['deposit']} */
const proposal = {
give: {
USDC: usdcAmount,
},
};

/** @type {OfferSpec} */
const offer = {
id: opts.offerId,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeDepositInvitation', []]],
},
proposal,
};

/** @type {ExecuteOfferAction} */
const bridgeAction = {
method: 'executeOffer',
offer,
};

outputActionAndHint(bridgeAction, { stderr, stdout }, vsk.marshaller);
});

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 withdraw.json --from gov1 --keyring-backend="test"',
)
.requiredOption('--amount <number>', 'USDC amount', parseDecimal)
.option('--offerId <string>', 'Offer id', String, `lpWithdraw-${now()}`)
.action(async opts => {
vskP ||= loadVsk();
const vsk = await vskP;

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

/** @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 usdcAmount = parseUSDCAmount(opts.amount, usdc);

/** @type {import('../types.js').PoolMetrics} */
// @ts-expect-error it treats this as "unknown"
const metrics = await vsk.readPublished('fastUsdc.poolMetrics');
const fastLPAmount = ceilDivideBy(usdcAmount, metrics.shareWorth);

/** @type {USDCProposalShapes['withdraw']} */
const proposal = {
give: {
PoolShare: fastLPAmount,
},
want: {
USDC: usdcAmount,
},
};

/** @type {OfferSpec} */
const offer = {
id: opts.offerId,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeWithdrawInvitation', []]],
},
proposal,
};

outputActionAndHint(
{ method: 'executeOffer', offer },
{ stderr, stdout },
vsk.marshaller,
);
});

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
Loading

0 comments on commit 5023e23

Please sign in to comment.