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

248 orchestration depositForBurn #11037

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Draft
2 changes: 1 addition & 1 deletion a3p-integration/proposals/g:gtm-fast-usdc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dependencies": {
"@agoric/client-utils": "dev",
"@agoric/fast-usdc": "dev",
"@agoric/synthetic-chain": "0.5.1",
"@agoric/synthetic-chain": "0.5.3",
"@endo/init": "^1.1.8",
"agoric": "dev",
"ava": "^5.3.1"
Expand Down
37 changes: 19 additions & 18 deletions a3p-integration/proposals/g:gtm-fast-usdc/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -464,19 +464,20 @@ __metadata:
languageName: node
linkType: hard

"@agoric/synthetic-chain@npm:0.5.1":
version: 0.5.1
resolution: "@agoric/synthetic-chain@npm:0.5.1"
"@agoric/synthetic-chain@npm:0.5.3":
version: 0.5.3
resolution: "@agoric/synthetic-chain@npm:0.5.3"
dependencies:
"@endo/zip": "npm:^1.0.7"
better-sqlite3: "npm:^11.8.0"
"@endo/zip": "npm:^1.0.9"
better-sqlite3: "npm:^11.8.1"
chalk: "npm:^5.4.1"
cosmjs-types: "npm:^0.9.0"
execa: "npm:^9.5.2"
glob: "npm:^11.0.0"
glob: "npm:^11.0.1"
tmp: "npm:0.2.3"
bin:
synthetic-chain: dist/cli/cli.js
checksum: 10c0/38d796556a0940d124b750bcf82cbbbd9caee64031feeca4086350c4d05d68256e5b188d418354b511eeb1da0f95160205cc2701ae397bd86c304fc29850bc85
checksum: 10c0/47e984c05a64fd520c1c7063b3f0a63d231f0da431bd169e0dece1cba5535696c3c582d32afc7771d869ecb6aa9f2945991c47e1ae015ba321d43a465e37573b
languageName: node
linkType: hard

Expand Down Expand Up @@ -1264,7 +1265,7 @@ __metadata:
languageName: node
linkType: hard

"@endo/zip@npm:^1.0.7, @endo/zip@npm:^1.0.9":
"@endo/zip@npm:^1.0.9":
version: 1.0.9
resolution: "@endo/zip@npm:1.0.9"
checksum: 10c0/3fccea31bd5dad938a3b5f531454d3c49513892d6d5aba1f0af1034ff0ae54c3e28a346a9df08bd9e5201354acccd631e45c9c0e68fa2848a876a3919f3830dc
Expand Down Expand Up @@ -1993,7 +1994,7 @@ __metadata:
languageName: node
linkType: hard

"better-sqlite3@npm:^11.8.0":
"better-sqlite3@npm:^11.8.1":
version: 11.8.1
resolution: "better-sqlite3@npm:11.8.1"
dependencies:
Expand Down Expand Up @@ -3151,7 +3152,7 @@ __metadata:
languageName: node
linkType: hard

"glob@npm:^11.0.0":
"glob@npm:^11.0.1":
version: 11.0.1
resolution: "glob@npm:11.0.1"
dependencies:
Expand Down Expand Up @@ -4796,7 +4797,7 @@ __metadata:
dependencies:
"@agoric/client-utils": "npm:dev"
"@agoric/fast-usdc": "npm:dev"
"@agoric/synthetic-chain": "npm:0.5.1"
"@agoric/synthetic-chain": "npm:0.5.3"
"@endo/init": "npm:^1.1.8"
agoric: "npm:dev"
ava: "npm:^5.3.1"
Expand Down Expand Up @@ -5266,6 +5267,13 @@ __metadata:
languageName: node
linkType: hard

"tmp@npm:0.2.3, tmp@npm:^0.2.1":
version: 0.2.3
resolution: "tmp@npm:0.2.3"
checksum: 10c0/3e809d9c2f46817475b452725c2aaa5d11985cf18d32a7a970ff25b568438e2c076c2e8609224feef3b7923fa9749b74428e3e634f6b8e520c534eef2fd24125
languageName: node
linkType: hard

"tmp@npm:^0.0.33":
version: 0.0.33
resolution: "tmp@npm:0.0.33"
Expand All @@ -5275,13 +5283,6 @@ __metadata:
languageName: node
linkType: hard

"tmp@npm:^0.2.1":
version: 0.2.3
resolution: "tmp@npm:0.2.3"
checksum: 10c0/3e809d9c2f46817475b452725c2aaa5d11985cf18d32a7a970ff25b568438e2c076c2e8609224feef3b7923fa9749b74428e3e634f6b8e520c534eef2fd24125
languageName: node
linkType: hard

"to-regex-range@npm:^5.0.1":
version: 5.0.1
resolution: "to-regex-range@npm:5.0.1"
Expand Down
1 change: 1 addition & 0 deletions packages/orchestration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@agoric/vow": "^0.1.0",
"@agoric/zoe": "^0.26.2",
"@agoric/zone": "^0.2.2",
"@cosmjs/encoding": "^0.32.4",
"@endo/base64": "^1.0.9",
"@endo/errors": "^1.2.9",
"@endo/far": "^1.1.10",
Expand Down
8 changes: 8 additions & 0 deletions packages/orchestration/src/chain-capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,18 @@ const IcaEnabled = /** @type {const} */ ({
});
harden(IcaEnabled);

const cctpDomains = /** @type {const} */ ({
noble: 4,
});
harden(cctpDomains);

/**
* @param {Record<string, CosmosChainInfo>} chainInfo
* @param {{
* PfmEnabled: Record<string, boolean>;
* IcqEnabled: Record<string, boolean>;
* IcaEnabled: Record<string, boolean>;
* cctpDomains: Record<string, number>;
* }} [opts]
*/
export const withChainCapabilities = (
Expand All @@ -71,12 +77,14 @@ export const withChainCapabilities = (
PfmEnabled,
IcqEnabled,
IcaEnabled,
cctpDomains,
},
) => {
return objectMap(chainInfo, (info, name) => ({
...info,
pfmEnabled: !!opts.PfmEnabled[name],
icqEnabled: !!opts.IcqEnabled[name],
icaEnabled: !!opts.IcqEnabled[name],
...(cctpDomains[name] ? { cctpDestinationDomain: cctpDomains[name] } : {}),
}));
};
11 changes: 11 additions & 0 deletions packages/orchestration/src/cosmos-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export interface CosmosAssetInfo extends Record<string, unknown> {
export type CosmosChainInfo = Readonly<{
/** can be used to lookup chainInfo (chainId) from an address value */
bech32Prefix?: string;
cctpDestinationDomain?: number;
chainId: string;

connections?: Record<string, IBCConnectionInfo>; // chainId or wellKnownName
Expand Down Expand Up @@ -309,6 +310,16 @@ export interface LiquidStakingMethods {
liquidStake: (amount: AmountArg) => Promise<void>;
}

export interface NobleMethods {
/** burn USDC on Noble and mint on a destination chain via CCTP */
depositForBurn: (
mintRecipient: CosmosChainAddress,
amount: AmountArg,
) => Promise<void>;
// consider including `registerForwardingAccount` (`MsgRegisterAccount`), so a contract can create its own forwarding address
// Requires `noble/forwarding` protos: https://github.com/noble-assets/forwarding/blob/main/proto/noble/forwarding/v1/tx.proto
}

// TODO support StakingAccountQueries
/** Methods supported only on Agoric chain accounts */
export interface LocalAccountMethods extends StakingAccountActions {
Expand Down
4 changes: 3 additions & 1 deletion packages/orchestration/src/ethereum-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Info for an Ethereum-based chain.
*/
export type EthChainInfo = Readonly<{
chainId: string;
// XXX consider ~BaseChainInfo type, with `cctpDestinationDomain` + `chainId`
cctpDestinationDomain?: number;
chainId: number;
allegedName: string;
}>;
30 changes: 20 additions & 10 deletions packages/orchestration/src/examples/send-anywhere.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { prepareChainHubAdmin } from '../exos/chain-hub-admin.js';
import { AnyNatAmountShape } from '../typeGuards.js';
import { withOrchestration } from '../utils/start-helper.js';
import { registerChainsAndAssets } from '../utils/chain-hub-helper.js';
import * as flows from './send-anywhere.flows.js';
import * as sharedFlows from './shared.flows.js';

import * as cctpFlows from './send-cctp.flows.js';

/**
* @import {Remote, Vow} from '@agoric/vow';
* @import {Zone} from '@agoric/zone';
Expand Down Expand Up @@ -40,7 +41,7 @@ export const contract = async (
zcf,
privateArgs,
zone,
{ chainHub, orchestrateAll, vowTools, zoeTools },
{ chainHub, orchestrate, vowTools, zoeTools },
) => {
const creatorFacet = prepareChainHubAdmin(zone, chainHub);

Expand All @@ -49,7 +50,10 @@ export const contract = async (
/** @type {(msg: string) => Vow<void>} */
const log = msg => vowTools.watch(E(logNode).setValue(msg));

const { makeLocalAccount } = orchestrateAll(sharedFlows, {});
// XXX why can't we do both at once?
const makeLocalAccount = orchestrate('f1', {}, sharedFlows.makeLocalAccount);
const makeNobleAccount = orchestrate('f2', {}, cctpFlows.makeNobleAccount);

/**
* Setup a shared local account for use in async-flow functions. Typically,
* exo initState functions need to resolve synchronously, but `makeOnce`
Expand All @@ -62,13 +66,19 @@ export const contract = async (
const sharedLocalAccountP = zone.makeOnce('localAccount', () =>
makeLocalAccount(),
);

const nobleAccountP = zone.makeOnce('nobleAccount', () => makeNobleAccount());
// orchestrate uses the names on orchestrationFns to do a "prepare" of the associated behavior
const orchFns = orchestrateAll(flows, {
log,
sharedLocalAccountP,
zoeTools,
});
const sendByCCTP = orchestrate(
'sendByCCTP',
{
log,
sharedLocalAccountP,
nobleAccountP,
zoeTools,
},
// @ts-expect-error deprecated, but alternative is TBD.
cctpFlows.sendByCCTP,
);

const publicFacet = zone.exo(
'Send PF',
Expand All @@ -78,7 +88,7 @@ export const contract = async (
{
makeSendInvitation() {
return zcf.makeInvitation(
orchFns.sendIt,
sendByCCTP,
'send',
undefined,
M.splitRecord({ give: SingleNatAmountRecord }),
Expand Down
3 changes: 3 additions & 0 deletions packages/orchestration/src/examples/send-anywhere.flows.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const sendIt = async (
assets.find(a => a.brand === amt.brand),
`${amt.brand} not registered in vbank`,
);
debugger;

const chain = await orch.getChain(chainName);
const info = await chain.getChainInfo();
Expand All @@ -59,6 +60,7 @@ export const sendIt = async (
await localTransfer(seat, sharedLocalAccount, give);

void log(`completed transfer to localAccount`);
console.log(`completed transfer to localAccount`);

try {
await sharedLocalAccount.transfer(
Expand All @@ -69,6 +71,7 @@ export const sendIt = async (
},
{ denom, value: amt.value },
);
console.log(`completed transfer to ${destAddr}`);
void log(`completed transfer to ${destAddr}`);
} catch (e) {
await withdrawToSeat(sharedLocalAccount, seat, give);
Expand Down
126 changes: 126 additions & 0 deletions packages/orchestration/src/examples/send-cctp.flows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { NonNullish } from '@agoric/internal';
import { makeError, q } from '@endo/errors';
import { M, mustMatch } from '@endo/patterns';

/**
* @import {GuestInterface, GuestOf} from '@agoric/async-flow';
* @import {Vow} from '@agoric/vow';
* @import {LocalOrchestrationAccountKit} from '../exos/local-orchestration-account.js';
* @import {ZoeTools} from '../utils/zoe-tools.js';
* @import {Orchestrator, OrchestrationFlow, LocalAccountMethods, OrchestrationAccountCommon, OrchestrationAccount, IBCConnectionInfo, AccountIdArg, ForwardInfo, Chain} from '../types.js';
* @import {IBCChannelID} from '@agoric/vats';
*/

const { entries } = Object;

/**
* @satisfies {OrchestrationFlow}
* @param {Orchestrator} orch
*/
export const makeNobleAccount = async orch => {
const nobleChain = await orch.getChain('noble');
return nobleChain.makeAccount();
};
harden(makeNobleAccount);

// TODO use case should be handled by `sendIt` based on the destination
/**
* @satisfies {OrchestrationFlow}
* @param {Orchestrator} orch
* @param {object} ctx
* @param {Promise<GuestInterface<LocalOrchestrationAccountKit['holder']>>} ctx.sharedLocalAccountP
* @param {Promise<
* GuestInterface<
* import('../exos/cosmos-orchestration-account.js').CosmosOrchestrationAccountKit['holder']
* >
* >} ctx.nobleAccountP
* @param {GuestInterface<ZoeTools>} ctx.zoeTools
* @param {GuestOf<(msg: string) => Vow<void>>} ctx.log
* @param {ZCFSeat} seat
* @param {{ chainName: string; destAddr: string }} offerArgs
*/
export const sendByCCTP = async (
orch,
{
sharedLocalAccountP,
nobleAccountP,
log,
zoeTools: { localTransfer, withdrawToSeat },
},
seat,
offerArgs,
) => {
mustMatch(offerArgs, harden({ chainName: M.scalar(), destAddr: M.string() }));
const { chainName, destAddr } = offerArgs;
// NOTE the proposal shape ensures that the `give` is a single asset
const { give } = seat.getProposal();
const [[_kw, amt]] = entries(give);
void log(`sending {${amt.value}} from ${chainName} to ${destAddr}`);
const agoric = await orch.getChain('agoric');
const assets = await agoric.getVBankAssetInfo();
void log(`got info for denoms: ${assets.map(a => a.denom).join(', ')}`);
const { denom } = NonNullish(
assets.find(a => a.brand === amt.brand),
`${amt.brand} not registered in vbank`,
);

/** @type {Chain<any>} */
const chain = await orch.getChain(chainName);
const info = await chain.getChainInfo();
const { chainId } = info;
assert(typeof chainId === 'string', 'bad chainId');
console.log(`got info for chain: ${chainName} ${chainId}`, info);

/**
* @type {OrchestrationAccount<{ chainId: 'agoric' }>}
*/
// @ts-expect-error XXX methods returning vows https://github.com/Agoric/agoric-sdk/issues/9822
const sharedLocalAccount = await sharedLocalAccountP;
await localTransfer(seat, sharedLocalAccount, give);

debugger;

if (typeof info.cctpDestinationDomain !== 'number') {
// within the inter-chain; no CCTP needed
console.log('CCTP via IBC');
try {
debugger;

await sharedLocalAccount.transfer(
{
value: destAddr,
encoding: 'bech32',
chainId,
},
{ denom, value: amt.value },
);
void log(`completed transfer to ${destAddr}`);
console.log(`completed transfer to ${destAddr}`);
} catch (e) {
await withdrawToSeat(sharedLocalAccount, seat, give);
const errorMsg = `IBC Transfer failed ${q(e)}`;
void log(`ERROR: ${errorMsg}`);
seat.exit(errorMsg);
throw makeError(errorMsg);
}
} else {
console.log(`CCTP case: assume USDC`, amt.brand);

debugger;
const nobleAccount = await nobleAccountP;
const nobleAddr = await nobleAccount.getAddress();
const denomAmt = { denom, value: amt.value };
await sharedLocalAccount.transfer(nobleAddr, denomAmt);
console.log('assets are now on noble');

const encoding = 'ethereum'; // XXX TODO. could be solana?
/** @type {AccountIdArg} */
const mintRecipient = { chainId, encoding, value: destAddr };
await nobleAccount.depositForBurn(mintRecipient, denomAmt);
console.log(
`transfer complete, we hope; could have FAILed between noble and dest, though`,
);
}
seat.exit();
};
harden(sendByCCTP);
Loading