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

chore(fast-usdc): limited operation before connecting to noble #10641

Merged
merged 4 commits into from
Dec 10, 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
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 },
Copy link
Member

Choose a reason for hiding this comment

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

consider making the option phrased positively. e.g. connectNoble.

I think the default could still be false. connecting to Noble has strictly more dependencies on the environment so makes sense to be opt-in

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, it's more dependencies, but without a connection to noble, the whole notion of "fast usdc" goes away. It's supported only for a limited form of testing. If somebody forgot to set the flag, the normal condition should be that the product works.

};
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 },
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised prettier change the second argument too

Copy link
Member Author

Choose a reason for hiding this comment

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

it didn't. I tried a helper for the 1st arg and then took it out, and used the more concise form when I took it out. I realized the second arg didn't need the line-breaks either.

{ 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
Copy link
Member

Choose a reason for hiding this comment

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

consider using "the state schema" to suggest the implications

Copy link
Member Author

Choose a reason for hiding this comment

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

The schema is below in stateShape, right? That's how I knew to write the code this way: it refused to store the state record without this property.

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;
Copy link
Member

Choose a reason for hiding this comment

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

consider putting it at the front (like Passable & was) so it's more noticeable.

Copy link
Member Author

Choose a reason for hiding this comment

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

Why would we want it to be noticeable? It's only here due to limitations in tsc or the Passable type or something.

Copy link
Member

@turadg turadg Dec 9, 2024

Choose a reason for hiding this comment

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

Why would we want it to be noticeable?

Because it's unexpected


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
Copy link
Member

Choose a reason for hiding this comment

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

👍

* 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
Loading