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

feat: generic chain with sendAnywhere demo contract #9460

Merged
merged 10 commits into from
Jun 10, 2024
24 changes: 8 additions & 16 deletions packages/orchestration/src/chain-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

/** @file temporary static lookup of chain info */

import { E } from '@endo/far';
import { registerChain } from './utils/chainHub.js';

/**
* @import {CosmosChainInfo, EthChainInfo} from './types.js';
*/
/** @import {CosmosChainInfo, EthChainInfo} from './types.js'; */

/** @typedef {CosmosChainInfo | EthChainInfo} ChainInfo */

Expand Down Expand Up @@ -83,7 +81,7 @@ export const wellKnownChainInfo =
stakingTokens: [{ denom: 'ustride' }],
},
cosmos: {
chainId: 'cosmoshub-4',
chainId: 'cosmoslocal',
connections: {},
icaEnabled: true,
icqEnabled: true,
Expand All @@ -101,7 +99,7 @@ export const wellKnownChainInfo =
stakingTokens: [{ denom: 'utia' }],
},
osmosis: {
chainId: 'osmosis-1',
chainId: 'osmosislocal',
connections: {},
icaEnabled: true,
icqEnabled: true,
Expand All @@ -117,14 +115,8 @@ export const wellKnownChainInfo =
* @param {(...messages: string[]) => void} log
*/
export const registerChainNamespace = async (agoricNamesAdmin, log) => {
const { nameAdmin } = await E(agoricNamesAdmin).provideChild('chain');

const registrationPromises = Object.entries(wellKnownChainInfo).map(
async ([name, info]) => {
log(`registering chain ${name}`);
return E(nameAdmin).update(name, info);
},
);

await Promise.all(registrationPromises);
for await (const [name, info] of Object.entries(wellKnownChainInfo)) {
log(`registering agoricNames chain.${name}`);
await registerChain(agoricNamesAdmin, name, info);
}
};
168 changes: 168 additions & 0 deletions packages/orchestration/src/examples/sendAnywhere.contract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { makeDurableZone } from '@agoric/zone/durable.js';
import { M, mustMatch } from '@endo/patterns';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/far';
import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';

import { AmountShape } from '@agoric/ertp';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { CosmosChainInfoShape } from '../typeGuards.js';
import { makeOrchestrationFacade } from '../facade.js';
import { prepareLocalChainAccountKit } from '../exos/local-chain-account-kit.js';
import { makeChainHub } from '../utils/chainHub.js';

const { entries } = Object;
const { Fail } = assert;

/**
* @import {Baggage} from '@agoric/vat-data';
* @import {CosmosChainInfo, IBCConnectionInfo} from '../cosmos-api';
* @import {TimerService, TimerBrand} from '@agoric/time';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
* @import {OrchestrationService} from '../service.js';
* @import {NameHub} from '@agoric/vats';
* @import {Remote} from '@agoric/vow';
*/

/**
* @typedef {{
* localchain: Remote<LocalChain>;
* orchestrationService: Remote<OrchestrationService>;
* storageNode: Remote<StorageNode>;
* timerService: Remote<TimerService>;
* agoricNames: Remote<NameHub>;
* }} OrchestrationPowers
*/

export const SingleAmountRecord = M.and(
M.recordOf(M.string(), AmountShape, {
numPropertiesLimit: 1,
}),
M.not(harden({})),
);

/**
* @param {ZCF} zcf
* @param {OrchestrationPowers & {
* marshaller: Marshaller;
* }} privateArgs
* @param {Baggage} baggage
*/
export const start = async (zcf, privateArgs, baggage) => {
const zone = makeDurableZone(baggage);

const chainHub = makeChainHub(privateArgs.agoricNames);

// TODO once durability is settled, provide some helpers to reduce boilerplate
const { marshaller, ...orchPowers } = privateArgs;
const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
const makeLocalChainAccountKit = prepareLocalChainAccountKit(
zone,
makeRecorderKit,
zcf,
privateArgs.timerService,
chainHub,
);
const { orchestrate } = makeOrchestrationFacade({
zcf,
zone,
chainHub,
makeLocalChainAccountKit,
...orchPowers,
});

let contractAccount;

const findBrandInVBank = async brand => {
turadg marked this conversation as resolved.
Show resolved Hide resolved
const assets = await E(
E(privateArgs.agoricNames).lookup('vbankAsset'),
).values();
const it = assets.find(a => a.brand === brand);
it || Fail`brand ${brand} not in agoricNames.vbankAsset`;
return it;
};

/** @type {OfferHandler} */
const sendIt = orchestrate(
'sendIt',
{ zcf },
// eslint-disable-next-line no-shadow -- this `zcf` is enclosed in a membrane
async (orch, { zcf }, seat, offerArgs) => {
mustMatch(
offerArgs,
harden({ chainName: M.scalar(), destAddr: M.string() }),
);
const { chainName, destAddr } = offerArgs;
const { give } = seat.getProposal();
const [[kw, amt]] = entries(give);
const { denom } = await findBrandInVBank(amt.brand);
const chain = await orch.getChain(chainName);

// XXX ok to use a heap var crossing the membrane scope this way?
if (!contractAccount) {
const agoricChain = await orch.getChain('agoric');
contractAccount = await agoricChain.makeAccount();
}

const info = await chain.getChainInfo();
const { chainId } = info;
const { [kw]: pmtP } = await withdrawFromSeat(zcf, seat, give);
await E.when(pmtP, pmt => contractAccount.deposit(pmt, amt));
await contractAccount.transfer(
{ denom, value: amt.value },
{
address: destAddr,
addressEncoding: 'bech32',
chainId,
},
);
},
);

const publicFacet = zone.exo(
'Send PF',
M.interface('Send PF', {
makeSendInvitation: M.callWhen().returns(InvitationShape),
}),
{
makeSendInvitation() {
return zcf.makeInvitation(
sendIt,
'send',
undefined,
M.splitRecord({ give: SingleAmountRecord }),
);
},
},
);

let nonce = 0n;
const ConnectionInfoShape = M.record(); // TODO
const creatorFacet = zone.exo(
'Send CF',
M.interface('Send CF', {
addChain: M.callWhen(CosmosChainInfoShape, ConnectionInfoShape).returns(
M.scalar(),
),
}),
{
/**
* @param {CosmosChainInfo} chainInfo
* @param {IBCConnectionInfo} connectionInfo
*/
async addChain(chainInfo, connectionInfo) {
const chainKey = `${chainInfo.chainId}-${(nonce += 1n)}`;
const agoricChainInfo = await chainHub.getChainInfo('agoric');
chainHub.registerChain(chainKey, chainInfo);
chainHub.registerConnection(
agoricChainInfo.chainId,
chainInfo.chainId,
connectionInfo,
);
return chainKey;
},
},
);

return { publicFacet, creatorFacet };
};
14 changes: 3 additions & 11 deletions packages/orchestration/src/examples/stakeBld.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import { E } from '@endo/far';
import { deeplyFulfilled } from '@endo/marshal';
import { M } from '@endo/patterns';
import { prepareLocalChainAccountKit } from '../exos/local-chain-account-kit.js';
import { makeChainHub } from '../utils/chainHub.js';

/**
* @import {NameHub} from '@agoric/vats';
* @import {Remote} from '@agoric/internal';
* @import {TimerBrand, TimerService} from '@agoric/time';
* @import {TimerService} from '@agoric/time';
* @import {LocalChain} from '@agoric/vats/src/localchain.js';
*/

Expand All @@ -28,7 +29,6 @@ const trace = makeTracer('StakeBld');
* marshaller: Marshaller;
* storageNode: StorageNode;
* timerService: TimerService;
* timerBrand: TimerBrand;
* }} privateArgs
* @param {import('@agoric/vat-data').Baggage} baggage
*/
Expand All @@ -45,20 +45,12 @@ export const start = async (zcf, privateArgs, baggage) => {
privateArgs.marshaller,
);

// FIXME in a second incarnation we can't make a remote call before defining all kinds
// UNTIL https://github.com/Agoric/agoric-sdk/issues/8879
const agoricChainInfo = await E(privateArgs.agoricNames).lookup(
'chain',
'agoric',
);

const makeLocalChainAccountKit = prepareLocalChainAccountKit(
zone,
makeRecorderKit,
zcf,
privateArgs.timerService,
privateArgs.timerBrand,
agoricChainInfo,
makeChainHub(privateArgs.agoricNames),
);

async function makeLocalAccountKit() {
Expand Down
19 changes: 18 additions & 1 deletion packages/orchestration/src/examples/swapExample.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { makeDurableZone } from '@agoric/zone/durable.js';
import { Far } from '@endo/far';
import { deeplyFulfilled } from '@endo/marshal';
import { M, objectMap } from '@endo/patterns';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { makeOrchestrationFacade } from '../facade.js';
import { orcUtils } from '../utils/orc.js';
import { makeChainHub } from '../utils/chainHub.js';
import { prepareLocalChainAccountKit } from '../exos/local-chain-account-kit.js';

/**
* @import {Orchestrator, IcaAccount, CosmosValidatorAddress} from '../types.js'
Expand All @@ -25,6 +28,7 @@ export const meta = {
localchain: M.remotable('localchain'),
orchestrationService: M.or(M.remotable('orchestration'), null),
storageNode: StorageNodeShape,
marshaller: M.remotable('marshaller'),
timerService: M.or(TimerServiceShape, null),
},
upgradability: 'canUpgrade',
Expand All @@ -48,6 +52,7 @@ export const makeNatAmountShape = (brand, min) =>
* orchestrationService: Remote<OrchestrationService>;
* storageNode: Remote<StorageNode>;
* timerService: Remote<TimerService>;
* marshaller: Marshaller;
* }} privateArgs
* @param {Baggage} baggage
*/
Expand All @@ -62,16 +67,28 @@ export const start = async (zcf, privateArgs, baggage) => {
orchestrationService,
storageNode,
timerService,
marshaller,
} = privateArgs;

const chainHub = makeChainHub(agoricNames);
const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
const makeLocalChainAccountKit = prepareLocalChainAccountKit(
zone,
makeRecorderKit,
zcf,
timerService,
chainHub,
);

const { orchestrate } = makeOrchestrationFacade({
agoricNames,
localchain,
orchestrationService,
storageNode,
timerService,
zcf,
zone,
chainHub,
makeLocalChainAccountKit,
});

/** deprecated historical example */
Expand Down
17 changes: 16 additions & 1 deletion packages/orchestration/src/examples/unbondExample.contract.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { makeDurableZone } from '@agoric/zone/durable.js';
import { Far } from '@endo/far';
import { M } from '@endo/patterns';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { makeOrchestrationFacade } from '../facade.js';
import { makeChainHub } from '../utils/chainHub.js';
import { prepareLocalChainAccountKit } from '../exos/local-chain-account-kit.js';

/**
* @import {Orchestrator, IcaAccount, CosmosValidatorAddress} from '../types.js'
Expand All @@ -20,6 +23,7 @@ import { makeOrchestrationFacade } from '../facade.js';
* localchain: Remote<LocalChain>;
* orchestrationService: Remote<OrchestrationService>;
* storageNode: Remote<StorageNode>;
* marshaller: Marshaller;
* timerService: Remote<TimerService>;
* }} privateArgs
* @param {Baggage} baggage
Expand All @@ -30,18 +34,29 @@ export const start = async (zcf, privateArgs, baggage) => {
localchain,
orchestrationService,
storageNode,
marshaller,
timerService,
} = privateArgs;
const zone = makeDurableZone(baggage);

const chainHub = makeChainHub(agoricNames);
const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
const makeLocalChainAccountKit = prepareLocalChainAccountKit(
zone,
makeRecorderKit,
zcf,
privateArgs.timerService,
chainHub,
);
const { orchestrate } = makeOrchestrationFacade({
agoricNames,
localchain,
orchestrationService,
storageNode,
timerService,
zcf,
zone,
chainHub: makeChainHub(agoricNames),
makeLocalChainAccountKit,
});

/** @type {OfferHandler} */
Expand Down
Loading
Loading