Skip to content

Commit

Permalink
client support for Fast USDC (#10735)
Browse files Browse the repository at this point in the history
closes: #10677

## Description
Everything in #10677 including some offer makers. There are more opportunities but this lays the groundwork for those to be added as valuable.

### Security Considerations
none


### Scaling Considerations
none

### Documentation Considerations
none

### Testing Considerations
nothing special

### Upgrade Considerations
not yet released
  • Loading branch information
mergify[bot] authored Jan 29, 2025
2 parents 37e30ec + 948d7b7 commit 35cb8ca
Show file tree
Hide file tree
Showing 17 changed files with 233 additions and 91 deletions.
2 changes: 1 addition & 1 deletion multichain-testing/test/fast-usdc/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IBCChannelID } from '@agoric/vats';
import type { FeedPolicy } from '@agoric/fast-usdc/src/types.js';
import type { FeedPolicy } from '@agoric/fast-usdc';

export const oracleMnemonics = {
oracle1:
Expand Down
2 changes: 1 addition & 1 deletion multichain-testing/tools/noble-tools.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { IBCChannelID } from '@agoric/vats';
import type { ExecSync } from './agd-lib.js';
import type { ChainAddress } from '@agoric/orchestration';
import type { NobleAddress } from '@agoric/fast-usdc/src/types.js';
import type { NobleAddress } from '@agoric/fast-usdc';

const kubectlBinary = 'kubectl';
const noblePod = 'noblelocal-genesis-0';
Expand Down
2 changes: 1 addition & 1 deletion packages/SwingSet/src/controller/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ export async function makeSwingsetController(
*
* The first `controller.run()` after this call will delete all
* the old vat's state at once, unless you use a
* [`runPolicy`](../docs/run-policy.md) to rate-limit cleanups.
* [`runPolicy`](../../docs/run-policy.md) to rate-limit cleanups.
*
* @param {VatID} vatID
* @param {SwingSetCapData} reasonCD
Expand Down
47 changes: 15 additions & 32 deletions packages/boot/test/fast-usdc/fast-usdc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { TestFn } from 'ava';
import { encodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
import { configurations } from '@agoric/fast-usdc/src/utils/deploy-config.js';
import { MockCctpTxEvidences } from '@agoric/fast-usdc/test/fixtures.js';
import { Offers } from '@agoric/fast-usdc/src/clientSupport.js';
import { documentStorageSchema } from '@agoric/governance/tools/storageDoc.js';
import { BridgeId, NonNullish } from '@agoric/internal';
import { unmarshalFromVstorage } from '@agoric/internal/src/marshal.js';
Expand Down Expand Up @@ -217,22 +218,13 @@ test.serial('LP deposits', async t => {
},
});

await lp.sendOffer({
id: 'deposit-lp-1',
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeDepositInvitation', []]],
},
proposal: {
give: {
USDC: { brand: usdc, value: 150_000_000n },
},
want: {
PoolShare: { brand: fastLP, value: 150_000_000n },
},
},
});
await lp.sendOffer(
Offers.fastUsdc.Deposit(agoricNamesRemotes, {
offerId: 'deposit-lp-1',
fastLPAmount: 150_000_000n,
usdcAmount: 150_000_000n,
}),
);
await eventLoopIteration();

const { getOutboundMessages } = t.context.bridgeUtils;
Expand Down Expand Up @@ -486,22 +478,13 @@ test.serial('LP withdraws', async t => {
},
});

await lp.sendOffer({
id: 'withdraw-lp-1',
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeWithdrawInvitation', []]],
},
proposal: {
give: {
PoolShare: { brand: fastLP, value: 369_000n },
},
want: {
USDC: { brand: usdc, value: 369_000n },
},
},
});
await lp.sendOffer(
Offers.fastUsdc.Withdraw(agoricNamesRemotes, {
offerId: 'withdraw-lp-1',
fastLPAmount: 369_000n,
usdcAmount: 369_000n,
}),
);
await eventLoopIteration();

const { denom: usdcDenom } = agoricNamesRemotes.vbankAsset.USDC;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { makeHelpers } from '@agoric/deploy-script-support';
/**
* @import {CoreEvalBuilder, DeployScriptFunction} from '@agoric/deploy-script-support/src/externalTypes.js'
* @import {ParseArgsConfig} from 'node:util'
* @import {FastUSDCConfig} from '@agoric/fast-usdc/src/types.js'
* @import {FastUSDCConfig} from '@agoric/fast-usdc';
*/

/** @type {ParseArgsConfig['options']} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { parseArgs } from 'node:util';
/**
* @import {CoreEvalBuilder, DeployScriptFunction} from '@agoric/deploy-script-support/src/externalTypes.js'
* @import {ParseArgsConfig} from 'node:util'
* @import {FastUSDCConfig, FeedPolicy} from '@agoric/fast-usdc/src/types.js'
* @import {FastUSDCConfig, FeedPolicy} from '@agoric/fast-usdc';
*/

const { keys } = Object;
Expand Down
11 changes: 10 additions & 1 deletion packages/client-utils/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
CurrentWalletRecord,
UpdateRecord,
} from '@agoric/smart-wallet/src/smartWallet.js';
import type { ContractRecord, PoolMetrics } from '@agoric/fast-usdc';

// For static string key types. String template matching has to be in the ternary below.
type PublishedTypeMap = {
Expand All @@ -34,4 +35,12 @@ export type TypedPublished<T extends string> = T extends keyof PublishedTypeMap
? OutcomeRecord
: T extends `vaultFactory.managers.manager${number}.metrics`
? VaultManagerMetrics
: unknown;
: T extends 'agoricNames.instance'
? Array<[string, Instance]>
: T extends 'agoricNames.brand'
? Array<[string, Brand]>
: T extends 'fastUsdc'
? ContractRecord
: T extends 'fastUsdc.poolMetrics'
? PoolMetrics
: unknown;
1 change: 1 addition & 0 deletions packages/fast-usdc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"src",
"tools"
],
"main": "src/main.js",
"bin": {
"fast-usdc": "./src/cli/bin.js"
},
Expand Down
55 changes: 11 additions & 44 deletions packages/fast-usdc/src/cli/lp-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from '@agoric/zoe/src/contractSupport/ratio.js';
import { InvalidArgumentError } from 'commander';
import { outputActionAndHint } from './bridge-action.js';
import { Offers } from '../clientSupport.js';

export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

Expand Down Expand Up @@ -96,31 +97,14 @@ export const addLPCommands = (

const usdcAmount = parseUSDCAmount(opts.amount, usdc);

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

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

/** @type {OfferSpec} */
const offer = {
id: opts.offerId,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeDepositInvitation', []]],
},
proposal,
};
const offer = Offers.fastUsdc.Deposit(swk.agoricNames, {
offerId: opts.offerId,
fastLPAmount: fastLPAmount.value,
usdcAmount: usdcAmount.value,
});

/** @type {ExecuteOfferAction} */
const bridgeAction = {
Expand Down Expand Up @@ -156,31 +140,14 @@ export const addLPCommands = (

const usdcAmount = parseUSDCAmount(opts.amount, usdc);

/** @type {import('../types.js').PoolMetrics} */
// @ts-expect-error it treats this as "unknown"
const metrics = await swk.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,
};
const offer = Offers.fastUsdc.Withdraw(swk.agoricNames, {
offerId: opts.offerId,
fastLPAmount: fastLPAmount.value,
usdcAmount: usdcAmount.value,
});

outputActionAndHint(
{ method: 'executeOffer', offer },
Expand Down
98 changes: 98 additions & 0 deletions packages/fast-usdc/src/clientSupport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// @ts-check
import { assertAllDefined } from '@agoric/internal';

/**
* @import {USDCProposalShapes} from './pool-share-math.js';
*/

/**
* @param {Pick<
* import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes,
* 'brand'
* >} agoricNames
* @param {object} opts
* @param {string} opts.offerId
* @param {bigint} opts.fastLPAmount
* @param {bigint} opts.usdcAmount
* @returns {import('@agoric/smart-wallet/src/offers.js').OfferSpec}
*/
const makeDepositOffer = ({ brand }, { offerId, fastLPAmount, usdcAmount }) => {
assertAllDefined({ offerId, fastLPAmount, usdcAmount });

return {
id: offerId,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeDepositInvitation']],
},
/** @type {USDCProposalShapes['deposit']} */
// @ts-expect-error https://github.com/Agoric/agoric-sdk/issues/10491
proposal: {
give: {
USDC: {
brand: brand.USDC,
value: usdcAmount,
},
},
want: {
PoolShare: {
brand: brand.FastLP,
value: fastLPAmount,
},
},
},
};
};

/**
* @param {Pick<
* import('@agoric/vats/tools/board-utils.js').AgoricNamesRemotes,
* 'brand'
* >} agoricNames
* @param {object} opts
* @param {string} opts.offerId
* @param {bigint} opts.fastLPAmount
* @param {bigint} opts.usdcAmount
* @returns {import('@agoric/smart-wallet/src/offers.js').OfferSpec}
*/
const makeWithdrawOffer = (
{ brand },
{ offerId, fastLPAmount, usdcAmount },
) => ({
id: offerId,
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeWithdrawInvitation']],
},
proposal: {
give: {
PoolShare: {
// @ts-expect-error https://github.com/Agoric/agoric-sdk/issues/10491
brand: brand.FastLP,
value: fastLPAmount,
},
},
want: {
USDC: {
// @ts-expect-error https://github.com/Agoric/agoric-sdk/issues/10491
brand: brand.USDC,
value: usdcAmount,
},
},
},
});

/**
* @satisfies {Record<
* string,
* Record<string, import('@agoric/smart-wallet/src/types.js').OfferMaker>
* >}
*/
export const Offers = {
fastUsdc: {
Deposit: makeDepositOffer,
Withdraw: makeWithdrawOffer,
},
};
2 changes: 1 addition & 1 deletion packages/fast-usdc/src/exos/advancer.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ export const prepareAdvancerKit = (
notifyFacet: M.remotable(),
borrowerFacet: M.remotable(),
poolAccount: M.remotable(),
settlementAddress: ChainAddressShape,
intermediateRecipient: M.opt(ChainAddressShape),
settlementAddress: M.opt(ChainAddressShape),
}),
},
);
Expand Down
8 changes: 3 additions & 5 deletions packages/fast-usdc/src/fast-usdc.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const ADDRESSES_BAGGAGE_KEY = 'addresses';
* @import {Marshaller, StorageNode} from '@agoric/internal/src/lib-chainStorage.js'
* @import {Zone} from '@agoric/zone';
* @import {OperatorOfferResult} from './exos/transaction-feed.js';
* @import {CctpTxEvidence, FeeConfig, RiskAssessment} from './types.js';
* @import {OperatorKit} from './exos/operator-kit.js';
* @import {CctpTxEvidence, ContractRecord, FeeConfig} from './types.js';
*/

/**
Expand Down Expand Up @@ -75,10 +76,7 @@ const publishFeeConfig = async (node, marshaller, feeConfig) => {

/**
* @param {Remote<StorageNode>} contractNode
* @param {{
* poolAccount: ChainAddress['value'];
* settlementAccount: ChainAddress['value'];
* }} addresses
* @param {ContractRecord} addresses
*/
const publishAddresses = (contractNode, addresses) => {
return E(contractNode).setValue(JSON.stringify(addresses));
Expand Down
1 change: 1 addition & 0 deletions packages/fast-usdc/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types.js';
6 changes: 6 additions & 0 deletions packages/fast-usdc/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export interface TransactionRecord extends CopyRecord {
status: TxStatus;
}

/** the record in vstorage at the path of the contract's node */
export interface ContractRecord extends CopyRecord {
poolAccount: ChainAddress['value'];
settlementAccount: ChainAddress['value'];
}

export type LogFn = (...args: unknown[]) => void;

export interface PendingTx extends CctpTxEvidence {
Expand Down
2 changes: 1 addition & 1 deletion packages/fast-usdc/src/utils/deploy-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
import { ChainPolicies } from './chain-policies.js';

/**
* @import {FastUSDCConfig} from '@agoric/fast-usdc/src/types.js'
* @import {FastUSDCConfig} from '@agoric/fast-usdc';
* @import {Passable} from '@endo/marshal';
* @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration';
*/
Expand Down
4 changes: 2 additions & 2 deletions packages/fast-usdc/test/cli/lp-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ test('fast-usdc deposit command', async t => {
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeDepositInvitation', []]],
callPipe: [['makeDepositInvitation']],
},
proposal: {
give: {
Expand Down Expand Up @@ -106,7 +106,7 @@ test('fast-usdc withdraw command', async t => {
invitationSpec: {
source: 'agoricContract',
instancePath: ['fastUsdc'],
callPipe: [['makeWithdrawInvitation', []]],
callPipe: [['makeWithdrawInvitation']],
},
proposal: {
give: {
Expand Down
Loading

0 comments on commit 35cb8ca

Please sign in to comment.