-
Notifications
You must be signed in to change notification settings - Fork 207
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
9796 continuing ica #10023
9796 continuing ica #10023
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
/** | ||
* @file This contract demonstrates the continuing invitation pattern with async | ||
* flows. | ||
* | ||
* The primary offer result is a power for invitation makers that can perform | ||
* actions with an ICA account. | ||
*/ | ||
import { AmountShape } from '@agoric/ertp'; | ||
import { VowShape } from '@agoric/vow'; | ||
import { M } from '@endo/patterns'; | ||
import { prepareCombineInvitationMakers } from '../exos/combine-invitation-makers.js'; | ||
import { CosmosOrchestrationInvitationMakersInterface } from '../exos/cosmos-orchestration-account.js'; | ||
import { withOrchestration } from '../utils/start-helper.js'; | ||
import * as flows from './staking-combinations.flows.js'; | ||
|
||
/** | ||
* @import {GuestInterface} from '@agoric/async-flow'; | ||
* @import {Delegation} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js'; | ||
* @import {ContinuingOfferResult} from '@agoric/smart-wallet/src/types.js'; | ||
* @import {TimerService} from '@agoric/time'; | ||
* @import {LocalChain} from '@agoric/vats/src/localchain.js'; | ||
* @import {NameHub} from '@agoric/vats'; | ||
* @import {Vow} from '@agoric/vow'; | ||
* @import {Remote} from '@agoric/internal'; | ||
* @import {Zone} from '@agoric/zone'; | ||
* @import {CosmosInterchainService} from '../exos/cosmos-interchain-service.js'; | ||
* @import {OrchestrationTools} from '../utils/start-helper.js'; | ||
* @import {CosmosOrchestrationAccount} from '../exos/cosmos-orchestration-account.js'; | ||
*/ | ||
|
||
const emptyOfferShape = harden({ | ||
// Nothing to give; the funds are deposited offline | ||
give: {}, | ||
want: {}, // UNTIL https://github.com/Agoric/agoric-sdk/issues/2230 | ||
exit: M.any(), | ||
}); | ||
|
||
/** | ||
* Orchestration contract to be wrapped by withOrchestration for Zoe. | ||
* | ||
* @param {ZCF} zcf | ||
* @param {{ | ||
* agoricNames: Remote<NameHub>; | ||
* localchain: Remote<LocalChain>; | ||
* orchestrationService: Remote<CosmosInterchainService>; | ||
* storageNode: Remote<StorageNode>; | ||
* marshaller: Marshaller; | ||
* timerService: Remote<TimerService>; | ||
* }} privateArgs | ||
* @param {Zone} zone | ||
* @param {OrchestrationTools} tools | ||
*/ | ||
const contract = async ( | ||
zcf, | ||
privateArgs, | ||
zone, | ||
{ orchestrateAll, vowTools }, | ||
) => { | ||
const ExtraInvitationMakerInterface = M.interface('', { | ||
DepositAndDelegate: M.call(M.array()).returns(VowShape), | ||
UndelegateAndTransfer: M.call(M.array()).returns(VowShape), | ||
}); | ||
/** @type {any} XXX async membrane */ | ||
const makeExtraInvitationMaker = zone.exoClass( | ||
'ContinuingInvitationExampleInvitationMakers', | ||
ExtraInvitationMakerInterface, | ||
/** @param {GuestInterface<CosmosOrchestrationAccount>} account */ | ||
account => { | ||
return { account }; | ||
}, | ||
{ | ||
DepositAndDelegate() { | ||
const { account } = this.state; | ||
|
||
const invP = zcf.makeInvitation( | ||
(seat, validatorAddr, amountArg) => | ||
// eslint-disable-next-line no-use-before-define -- defined by orchestrateAll, necessarily after this | ||
orchFns.depositAndDelegate(account, seat, validatorAddr, amountArg), | ||
'Deposit and delegate', | ||
undefined, | ||
{ | ||
give: { | ||
Stake: AmountShape, | ||
}, | ||
}, | ||
); | ||
|
||
return vowTools.watch(invP); | ||
}, | ||
/** | ||
* @param {Omit<Delegation, 'delegatorAddress'>[]} delegations | ||
*/ | ||
UndelegateAndTransfer(delegations) { | ||
const { account } = this.state; | ||
|
||
const invP = zcf.makeInvitation( | ||
// eslint-disable-next-line no-use-before-define -- defined by orchestrateAll, necessarily after this | ||
() => orchFns.undelegateAndTransfer(account, delegations), | ||
'Undelegate and transfer', | ||
undefined, | ||
emptyOfferShape, | ||
); | ||
|
||
return vowTools.watch(invP); | ||
}, | ||
}, | ||
); | ||
|
||
/** @type {any} XXX async membrane */ | ||
const makeCombineInvitationMakers = prepareCombineInvitationMakers( | ||
zone, | ||
CosmosOrchestrationInvitationMakersInterface, | ||
ExtraInvitationMakerInterface, | ||
); | ||
|
||
const orchFns = orchestrateAll(flows, { | ||
makeCombineInvitationMakers, | ||
makeExtraInvitationMaker, | ||
flows, | ||
zcf, | ||
}); | ||
|
||
const publicFacet = zone.exo('publicFacet', undefined, { | ||
makeAccount() { | ||
return zcf.makeInvitation( | ||
orchFns.makeAccount, | ||
'Make an ICA account', | ||
undefined, | ||
emptyOfferShape, | ||
); | ||
}, | ||
}); | ||
|
||
return harden({ publicFacet }); | ||
}; | ||
|
||
export const start = withOrchestration(contract); | ||
harden(start); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
/** | ||
* @import {GuestInterface} from '@agoric/async-flow'; | ||
* @import {Orchestrator, OrchestrationFlow, OrchestrationAccount, OrchestrationAccountI, StakingAccountActions, AmountArg, CosmosValidatorAddress} from '../types.js' | ||
* @import {ContinuingOfferResult, InvitationMakers} from '@agoric/smart-wallet/src/types.js'; | ||
* @import {MakeCombineInvitationMakers} from '../exos/combine-invitation-makers.js'; | ||
* @import {Delegation} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js'; | ||
* @import {CosmosOrchestrationAccount} from '../exos/cosmos-orchestration-account.js'; | ||
*/ | ||
|
||
/** | ||
* @satisfies {OrchestrationFlow} | ||
* @param {Orchestrator} orch | ||
* @param {{ | ||
* makeCombineInvitationMakers: MakeCombineInvitationMakers; | ||
* makeExtraInvitationMaker: (account: any) => InvitationMakers; | ||
* }} ctx | ||
* @param {ZCFSeat} _seat | ||
* @param {{ chainName: string }} offerArgs | ||
* @returns {Promise<ContinuingOfferResult>} | ||
*/ | ||
export const makeAccount = async (orch, ctx, _seat, { chainName }) => { | ||
const chain = await orch.getChain(chainName); | ||
const account = await chain.makeAccount(); | ||
|
||
const extraMakers = ctx.makeExtraInvitationMaker(account); | ||
|
||
/** @type {ContinuingOfferResult} */ | ||
const result = await account.asContinuingOffer(); | ||
|
||
return { | ||
...result, | ||
invitationMakers: ctx.makeCombineInvitationMakers( | ||
extraMakers, | ||
result.invitationMakers, | ||
), | ||
}; | ||
}; | ||
harden(makeAccount); | ||
|
||
/** | ||
* @satisfies {OrchestrationFlow} | ||
* @param {Orchestrator} orch | ||
* @param {object} ctx | ||
* @param {GuestInterface<CosmosOrchestrationAccount>} account | ||
* @param {ZCFSeat} seat | ||
* @param {CosmosValidatorAddress} validator | ||
* @param {AmountArg} amount | ||
* @returns {Promise<string>} | ||
*/ | ||
export const depositAndDelegate = async ( | ||
orch, | ||
ctx, | ||
account, | ||
seat, | ||
validator, | ||
amount, | ||
) => { | ||
console.log('depositAndDelegate', account, seat, validator, amount); | ||
// TODO deposit the amount | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ticket # would be nice, but thanks for including this! |
||
await account.delegate(validator, amount); | ||
return 'guest depositAndDelegate complete'; | ||
}; | ||
harden(depositAndDelegate); | ||
|
||
/** | ||
* @satisfies {OrchestrationFlow} | ||
* @param {Orchestrator} orch | ||
* @param {object} ctx | ||
* @param {GuestInterface<CosmosOrchestrationAccount>} account | ||
* @param {Omit<Delegation, 'delegatorAddress'>[]} delegations | ||
* @returns {Promise<string>} | ||
*/ | ||
export const undelegateAndTransfer = async ( | ||
orch, | ||
ctx, | ||
account, | ||
delegations, | ||
) => { | ||
await account.undelegate(delegations); | ||
// TODO transfer something | ||
return 'guest undelegateAndTransfer complete'; | ||
}; | ||
harden(undelegateAndTransfer); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { M } from '@endo/patterns'; | ||
import { | ||
prepareGuardedAttenuator, | ||
makeSyncMethodCallback, | ||
} from '@agoric/internal/src/callback.js'; | ||
import { getMethodNames } from '@agoric/internal'; | ||
|
||
/** | ||
* @import {InvitationMakers} from '@agoric/smart-wallet/src/types.js'; | ||
* @import {Zone} from '@agoric/zone'; | ||
*/ | ||
|
||
// TODO use a helper from Endo https://github.com/endojs/endo/issues/2448 | ||
/** | ||
* Takes two or more InvitationMaker exos and combines them into a new one. | ||
* | ||
* @param {Zone} zone | ||
* @param {import('@endo/patterns').InterfaceGuard[]} interfaceGuards | ||
*/ | ||
export const prepareCombineInvitationMakers = (zone, ...interfaceGuards) => { | ||
const methodGuards = interfaceGuards.map(ig => ig.payload.methodGuards); | ||
const CombinedInterfaceGuard = M.interface( | ||
'CombinedInvitationMakers interface', | ||
Object.assign({}, ...methodGuards), | ||
); | ||
|
||
const mixin = prepareGuardedAttenuator(zone, CombinedInterfaceGuard, { | ||
tag: 'CombinedInvitationMakers', | ||
}); | ||
|
||
/** | ||
* @template {InvitationMakers[]} IM | ||
* @param {IM} invitationMakers | ||
* @returns {IM[number]} | ||
*/ | ||
const combineInvitationMakers = (...invitationMakers) => { | ||
const overrides = {}; | ||
for (const invMakers of invitationMakers) { | ||
// remove '__getInterfaceGuard__', '__getMethodNames__' | ||
const names = getMethodNames(invMakers).filter(n => !n.startsWith('__')); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nb: should we move this to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no opinion here. WDYT @mhofman ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
for (const key of names) { | ||
overrides[key] = makeSyncMethodCallback(invMakers, key); | ||
} | ||
} | ||
return mixin({ | ||
overrides, | ||
}); | ||
}; | ||
|
||
return combineInvitationMakers; | ||
}; | ||
|
||
/** @typedef {ReturnType<typeof prepareCombineInvitationMakers>} MakeCombineInvitationMakers */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking on this more, I don't think we need
vowTools.watch
here. We can expect the promise for an Invitation to resolve promptly - it's just the offer handler that needs to return a vow.If we feel we should return a vow here, we should also make this change in the orchAccount exos. Currently, we're always returning a promise.
The only instance coming to mind where it'd be helpful to return a vow is if a developer wanted to call an
invitationMaker
from inside a flow - it'd need to be a vow to cross the membrane. I'm not sure how common this will be - we don't have any examples that need this currently.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's what motivated the vow: an earlier version of this branch was calling between guest fns. But definitely not needed now. Thanks for pointing it out.
I believe "promptly" means in "in the same run".
zcf.makeInvitation
calls out to another vat so I don't think it can be in the same run.agoric-sdk/packages/zoe/src/contractFacet/zcfZygote.js
Lines 314 to 316 in daff9eb