Skip to content

Commit

Permalink
chore(fast-usdc): limited operation before connecting to noble (#10641)
Browse files Browse the repository at this point in the history
closes: #10657 
refs: #10328

## Description

 - advancer, settler can operate without a noble account address
 - creatorFacet method to synchronize on fulfillment of `nobleAccountV` vow
 - restore fast-usdc a3p-integration test to working order

### Security / Documentation Considerations

While the noble account vow has not fulfilled, diagnostics for advancing to non-agoric chains are likely to be obscure.

Configuration flexibility leads to possibility of configuration errors.

### Scaling Considerations

n/a

### Testing Considerations

existing tests, but with `set -e` so that when `fastUsdc` is not in `agoricNames.instance`, we actually notice.

### Upgrade Considerations

`nobleAccountV` persists across incarnations, so we won't try to make another one on restart / upgrade.

TODO:
 - [x] what to do if `nobleAccountV` rejects?
  • Loading branch information
mergify[bot] authored Dec 10, 2024
2 parents 86e3b2d + c9ba09e commit 340ef0d
Show file tree
Hide file tree
Showing 12 changed files with 96 additions and 52 deletions.
2 changes: 1 addition & 1 deletion a3p-integration/proposals/f:fast-usdc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"agoricProposal": {
"source": "subdir",
"sdk-generate": [
"fast-usdc/init-fast-usdc.js submission --net A3P_INTEGRATION"
"fast-usdc/init-fast-usdc.js submission --net A3P_INTEGRATION --noNoble"
],
"type": "/agoric.swingset.CoreEvalProposal"
},
Expand Down
2 changes: 2 additions & 0 deletions a3p-integration/proposals/f:fast-usdc/test.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#!/bin/bash
set -euo pipefail

yarn ava

# TODO get CLI test passing and part of CI
Expand Down
8 changes: 6 additions & 2 deletions packages/builders/scripts/fast-usdc/init-fast-usdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const options = {
},
chainInfo: { type: 'string' },
assetInfo: { type: 'string' },
noNoble: { type: 'boolean', default: false },
};
const oraclesUsage = 'use --oracle name:address ...';

Expand All @@ -57,8 +58,9 @@ const assetInfoUsage =
* oracle?: string[];
* usdcDenom: string;
* feedPolicy?: string;
* chainInfo: string;
* assetInfo: string;
* chainInfo?: string;
* assetInfo?: string;
* noNoble: boolean;
* }} FastUSDCOpts
*/

Expand Down Expand Up @@ -107,6 +109,7 @@ export default async (homeP, endowments) => {
feedPolicy,
chainInfo,
assetInfo,
noNoble,
...fees
},
} = parseArgs({ args: scriptArgs, options });
Expand Down Expand Up @@ -185,6 +188,7 @@ export default async (homeP, endowments) => {
feedPolicy: parseFeedPolicy(),
chainInfo: parseChainInfo(),
assetInfo: parseAssetInfo(),
noNoble,
});

await writeCoreEval('start-fast-usdc', utils =>
Expand Down
29 changes: 16 additions & 13 deletions packages/fast-usdc/src/exos/advancer.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const AdvancerVowCtxShape = M.splitRecord(
const AdvancerKitI = harden({
advancer: M.interface('AdvancerI', {
handleTransactionEvent: M.callWhen(CctpTxEvidenceShape).returns(),
setIntermediateRecipient: M.call(ChainAddressShape).returns(),
}),
depositHandler: M.interface('DepositHandlerI', {
onFulfilled: M.call(M.undefined(), AdvancerVowCtxShape).returns(VowShape),
Expand Down Expand Up @@ -95,7 +96,7 @@ export const prepareAdvancerKit = (
usdc,
vowTools: { watch, when },
zcf,
} = /** @type {AdvancerKitPowers} */ ({}),
},
) => {
assertAllDefined({
chainHub,
Expand All @@ -116,10 +117,15 @@ export const prepareAdvancerKit = (
* notifyFacet: import('./settler.js').SettlerKit['notify'];
* borrowerFacet: LiquidityPoolKit['borrower'];
* poolAccount: HostInterface<OrchestrationAccount<{chainId: 'agoric'}>>;
* intermediateRecipient: ChainAddress;
* intermediateRecipient?: ChainAddress;
* }} config
*/
config => harden(config),
config =>
harden({
...config,
// make sure the state record has this property, perhaps with an undefined value
intermediateRecipient: config.intermediateRecipient,
}),
{
advancer: {
/**
Expand Down Expand Up @@ -181,6 +187,10 @@ export const prepareAdvancerKit = (
statusManager.observe(evidence);
}
},
/** @param {ChainAddress} intermediateRecipient */
setIntermediateRecipient(intermediateRecipient) {
this.state.intermediateRecipient = intermediateRecipient;
},
},
depositHandler: {
/**
Expand All @@ -192,15 +202,8 @@ export const prepareAdvancerKit = (
const { destination, advanceAmount, ...detail } = ctx;
const transferV = E(poolAccount).transfer(
destination,
{
denom: usdc.denom,
value: advanceAmount.value,
},
{
forwardOpts: {
intermediateRecipient,
},
},
{ denom: usdc.denom, value: advanceAmount.value },
{ forwardOpts: { intermediateRecipient } },
);
return watch(transferV, this.facets.transferHandler, {
destination,
Expand Down Expand Up @@ -259,7 +262,7 @@ export const prepareAdvancerKit = (
notifyFacet: M.remotable(),
borrowerFacet: M.remotable(),
poolAccount: M.remotable(),
intermediateRecipient: ChainAddressShape,
intermediateRecipient: M.opt(ChainAddressShape),
}),
},
);
Expand Down
17 changes: 10 additions & 7 deletions packages/fast-usdc/src/exos/settler.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const prepareSettler = (
{
creator: M.interface('SettlerCreatorI', {
monitorMintingDeposits: M.callWhen().returns(M.any()),
setIntermediateRecipient: M.call(ChainAddressShape).returns(),
}),
tap: M.interface('SettlerTapI', {
receiveUpcall: M.call(M.record()).returns(M.promise()),
Expand Down Expand Up @@ -94,13 +95,15 @@ export const prepareSettler = (
* remoteDenom: Denom;
* repayer: LiquidityPoolKit['repayer'];
* settlementAccount: HostInterface<OrchestrationAccount<{ chainId: 'agoric' }>>
* intermediateRecipient: ChainAddress;
* intermediateRecipient?: ChainAddress;
* }} config
*/
config => {
log('config', config);
return {
...config,
// make sure the state record has this property, perhaps with an undefined value
intermediateRecipient: config.intermediateRecipient,
/** @type {HostInterface<TargetRegistration>|undefined} */
registration: undefined,
/** @type {SetStore<ReturnType<typeof makeMintedEarlyKey>>} */
Expand All @@ -117,6 +120,10 @@ export const prepareSettler = (
assert.typeof(registration, 'object');
this.state.registration = registration;
},
/** @param {ChainAddress} intermediateRecipient */
setIntermediateRecipient(intermediateRecipient) {
this.state.intermediateRecipient = intermediateRecipient;
},
},
tap: {
/** @param {VTransferIBCEvent} event */
Expand Down Expand Up @@ -265,11 +272,7 @@ export const prepareSettler = (
const txfrV = E(settlementAccount).transfer(
dest,
AmountMath.make(USDC, fullValue),
{
forwardOpts: {
intermediateRecipient,
},
},
{ forwardOpts: { intermediateRecipient } },
);
void vowTools.watch(txfrV, this.facets.transferHandler, {
txHash,
Expand Down Expand Up @@ -312,7 +315,7 @@ export const prepareSettler = (
sourceChannel: M.string(),
remoteDenom: M.string(),
mintedEarly: M.remotable('mintedEarly'),
intermediateRecipient: ChainAddressShape,
intermediateRecipient: M.opt(ChainAddressShape),
}),
},
);
Expand Down
33 changes: 20 additions & 13 deletions packages/fast-usdc/src/fast-usdc.contract.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AssetKind } from '@agoric/ertp';
import { assertAllDefined, makeTracer } from '@agoric/internal';
import { makeTracer } from '@agoric/internal';
import { observeIteration, subscribeEach } from '@agoric/notifier';
import {
CosmosChainInfoShape,
Expand Down Expand Up @@ -126,7 +126,6 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
});

const makeFeedKit = prepareTransactionFeedKit(zone, zcf);
assertAllDefined({ makeFeedKit, makeAdvancer, makeSettler, statusManager });

const makeLiquidityPoolKit = prepareLiquidityPoolKit(
zone,
Expand All @@ -147,6 +146,20 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
async makeOperatorInvitation(operatorId) {
return feedKit.creator.makeOperatorInvitation(operatorId);
},
async connectToNoble() {
return vowTools.when(nobleAccountV, nobleAccount => {
trace('nobleAccount', nobleAccount);
return vowTools.when(
E(nobleAccount).getAddress(),
intermediateRecipient => {
trace('intermediateRecipient', intermediateRecipient);
advancer.setIntermediateRecipient(intermediateRecipient);
settlerKit.creator.setIntermediateRecipient(intermediateRecipient);
return intermediateRecipient;
},
);
});
},
});

const publicFacet = zone.exo('Fast USDC Public', undefined, {
Expand Down Expand Up @@ -214,26 +227,22 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
privateArgs.assetInfo,
);
}

const nobleAccountV = zone.makeOnce('NobleAccount', () => makeNobleAccount());

const feedKit = zone.makeOnce('Feed Kit', () => makeFeedKit());

const poolAccountV = zone.makeOnce('PoolAccount', () => makeLocalAccount());
const settleAccountV = zone.makeOnce('SettleAccount', () =>
makeLocalAccount(),
);
// when() is OK here since this clearly resolves promptly.
/** @type {[HostInterface<OrchestrationAccount<{chainId: 'noble-1';}>>, HostInterface<OrchestrationAccount<{chainId: 'agoric-3';}>>, HostInterface<OrchestrationAccount<{chainId: 'agoric-3';}>>]} */
const [nobleAccount, poolAccount, settlementAccount] = await vowTools.when(
vowTools.all([nobleAccountV, poolAccountV, settleAccountV]),
/** @type {[HostInterface<OrchestrationAccount<{chainId: 'agoric-3';}>>, HostInterface<OrchestrationAccount<{chainId: 'agoric-3';}>>]} */
const [poolAccount, settlementAccount] = await vowTools.when(
vowTools.all([poolAccountV, settleAccountV]),
);
trace('settlementAccount', settlementAccount);
trace('poolAccount', poolAccount);
trace('nobleAccount', nobleAccount);

const intermediateRecipient = await vowTools.when(
E(nobleAccount).getAddress(),
);
trace('intermediateRecipient', intermediateRecipient);

const [_agoric, _noble, agToNoble] = await vowTools.when(
chainHub.getChainsAndConnection('agoric', 'noble'),
Expand All @@ -243,15 +252,13 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
sourceChannel: agToNoble.transferChannel.counterPartyChannelId,
remoteDenom: 'uusdc',
settlementAccount,
intermediateRecipient,
});

const advancer = zone.makeOnce('Advancer', () =>
makeAdvancer({
borrowerFacet: poolKit.borrower,
notifyFacet: settlerKit.notify,
poolAccount,
intermediateRecipient,
}),
);
// Connect evidence stream to advancer
Expand Down
20 changes: 12 additions & 8 deletions packages/fast-usdc/src/fast-usdc.start.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,11 @@ export const startFastUSDC = async (
USDC: await E(USDCissuer).getBrand(),
});

const { terms, oracles, feeConfig, feedPolicy, chainInfo, assetInfo } =
fromExternalConfig(
config?.options, // just in case config is missing somehow
brands,
FastUSDCConfigShape,
);
const { terms, oracles, feeConfig, feedPolicy, ...net } = fromExternalConfig(
config.options,
brands,
FastUSDCConfigShape,
);
trace('using terms', terms);
trace('using fee config', feeConfig);

Expand Down Expand Up @@ -186,8 +185,8 @@ export const startFastUSDC = async (
storageNode,
timerService,
marshaller,
chainInfo,
assetInfo,
chainInfo: net.chainInfo,
assetInfo: net.assetInfo,
}),
);

Expand Down Expand Up @@ -224,6 +223,11 @@ export const startFastUSDC = async (

produceInstance.reset();
produceInstance.resolve(instance);

if (!net.noNoble) {
const addr = await E(kit.creatorFacet).connectToNoble();
trace('noble intermediate recipient', addr);
}
trace('startFastUSDC done', instance);
};
harden(startFastUSDC);
Expand Down
7 changes: 4 additions & 3 deletions packages/fast-usdc/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
} from '@agoric/orchestration';
import type { IBCChannelID } from '@agoric/vats';
import type { Amount } from '@agoric/ertp';
import type { Passable } from '@endo/pass-style';
import type { CopyRecord, Passable } from '@endo/pass-style';
import type { PendingTxStatus } from './constants.js';
import type { FastUsdcTerms } from './fast-usdc.contract.js';

Expand Down Expand Up @@ -78,14 +78,15 @@ export interface FeedPolicy {
eventFilter?: string;
}

export type FastUSDCConfig = Passable & {
export type FastUSDCConfig = {
terms: FastUsdcTerms;
oracles: Record<string, string>;
feeConfig: FeeConfig;
feedPolicy: FeedPolicy & Passable;
noNoble: boolean; // support a3p-integration, which has no noble chain
chainInfo: Record<string, CosmosChainInfo & Passable>;
assetInfo: [Denom, DenomDetail & { brandKey?: string }][];
};
} & CopyRecord;

export type * from './constants.js';
export type { LiquidityPoolKit } from './exos/liquidity-pool.js';
21 changes: 18 additions & 3 deletions packages/fast-usdc/src/utils/deploy-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export const defaultAssetInfo = [
},
],
];
harden(defaultAssetInfo);

const agoricAssetInfo = defaultAssetInfo.filter(
([_d, i]) => i.chainName === 'agoric',
);

/**
* @type {Record<string, Pick<FastUSDCConfig, 'oracles' | 'feedPolicy' | 'chainInfo' | 'assetInfo' >>}
Expand All @@ -43,14 +48,19 @@ export const defaultAssetInfo = [
* meanwhile, use price oracle addresses (from updatePriceFeeds.js).
*/
export const configurations = {
/**
* NOTE: The a3p-integration runtime does _not_ include
* a noble chain; this limits functionality to advancing
* to the Agoric chain.
*/
A3P_INTEGRATION: {
oracles: {
gov1: 'agoric1ee9hr0jyrxhy999y755mp862ljgycmwyp4pl7q',
gov2: 'agoric1wrfh296eu2z34p6pah7q04jjuyj3mxu9v98277',
gov3: 'agoric1ydzxwh6f893jvpaslmaz6l8j2ulup9a7x8qvvq',
},
feedPolicy: {
nobleAgoricChannelId: 'TODO',
nobleAgoricChannelId: 'channel-does-not-exist',
nobleDomainId: 4,
chainPolicies: {
Arbitrum: {
Expand All @@ -63,9 +73,13 @@ export const configurations = {
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo)
withChainCapabilities({
agoric: fetchedChainInfo.agoric,
// registering USDC-on-agoric requires registering the noble chain
noble: fetchedChainInfo.noble,
})
),
assetInfo: defaultAssetInfo,
assetInfo: agoricAssetInfo,
},
MAINNET: {
oracles: {
Expand Down Expand Up @@ -139,3 +153,4 @@ export const configurations = {
assetInfo: defaultAssetInfo, // TODO: use emerynet values
},
};
harden(configurations);
1 change: 1 addition & 0 deletions packages/fast-usdc/test/fast-usdc.contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const startContract = async (
E(startKit.creatorFacet).makeOperatorInvitation(`operator-${opIx}`),
),
);
await E(startKit.creatorFacet).connectToNoble();

return {
...startKit,
Expand Down
Loading

0 comments on commit 340ef0d

Please sign in to comment.