-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
379 additions
and
0 deletions.
There are no files selected for viewing
118 changes: 118 additions & 0 deletions
118
packages/orchestration/src/examples/continuing-invitation.contract.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/** | ||
* @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 { VowShape } from '@agoric/vow'; | ||
import { M } from '@endo/patterns'; | ||
import { prepareCombineInvitationMakers } from '../exos/combine-invitation-makers.js'; | ||
import { withOrchestration } from '../utils/start-helper.js'; | ||
import { CosmosOrchestrationInvitationMakersInterface } from '../exos/cosmos-orchestration-account.js'; | ||
import * as flows from './continuing-invitation.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('', { | ||
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 }; | ||
}, | ||
{ | ||
/** | ||
* @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); |
58 changes: 58 additions & 0 deletions
58
packages/orchestration/src/examples/continuing-invitation.flows.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* @import {GuestInterface} from '@agoric/async-flow'; | ||
* @import {Orchestrator, OrchestrationFlow, OrchestrationAccount, OrchestrationAccountI, StakingAccountActions} 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 {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); |
110 changes: 110 additions & 0 deletions
110
packages/orchestration/test/examples/continuing-invitation.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; | ||
|
||
import { inspectMapStore } from '@agoric/internal/src/testing-utils.js'; | ||
import { setUpZoeForTest } from '@agoric/zoe/tools/setup-zoe.js'; | ||
import { E } from '@endo/far'; | ||
import path from 'path'; | ||
import { protoMsgMocks, UNBOND_PERIOD_SECONDS } from '../ibc-mocks.js'; | ||
import { commonSetup } from '../supports.js'; | ||
|
||
const dirname = path.dirname(new URL(import.meta.url).pathname); | ||
|
||
const contractFile = `${dirname}/../../src/examples/continuing-invitation.contract.js`; | ||
type StartFn = | ||
typeof import('@agoric/orchestration/src/examples/continuing-invitation.contract.js').start; | ||
|
||
test('start', async t => { | ||
const { | ||
bootstrap: { timer, vowTools: vt }, | ||
brands: { ist }, | ||
mocks: { ibcBridge }, | ||
commonPrivateArgs, | ||
} = await commonSetup(t); | ||
|
||
let contractBaggage; | ||
const { zoe, bundleAndInstall } = await setUpZoeForTest({ | ||
setJig: ({ baggage }) => { | ||
contractBaggage = baggage; | ||
}, | ||
}); | ||
const installation: Installation<StartFn> = | ||
await bundleAndInstall(contractFile); | ||
|
||
const { publicFacet } = await E(zoe).startInstance( | ||
installation, | ||
{ Stable: ist.issuer }, | ||
{}, | ||
commonPrivateArgs, | ||
); | ||
|
||
const inv = E(publicFacet).makeAccount(); | ||
|
||
t.is( | ||
(await E(zoe).getInvitationDetails(inv)).description, | ||
'Make an ICA account', | ||
); | ||
|
||
const userSeat = await E(zoe).offer( | ||
inv, | ||
{}, | ||
{}, | ||
{ | ||
chainName: 'osmosis', | ||
}, | ||
); | ||
|
||
const result = await vt.when(E(userSeat).getOfferResult()); | ||
t.like(result, { | ||
publicSubscribers: { | ||
account: { | ||
description: 'Staking Account holder status', | ||
storagePath: 'mockChainStorageRoot.cosmos1test', | ||
}, | ||
}, | ||
}); | ||
|
||
// Here the account would get funded through Cosmos native operations. | ||
|
||
// Delegate the funds like so, but don't bother executing the offer | ||
// because the balances aren't tracked. | ||
const delegateInv = await E(result.invitationMakers).Delegate( | ||
{ value: '10', encoding: 'bech32', chainId: 'osmosis' }, | ||
{ | ||
denom: 'osmo', | ||
value: 10n, | ||
}, | ||
); | ||
t.like(await E(zoe).getInvitationDetails(delegateInv), { | ||
description: 'Delegate', | ||
}); | ||
|
||
// Undelegate the funds using the guest flow | ||
ibcBridge.addMockAck( | ||
// observed in console | ||
'eyJ0eXBlIjoxLCJkYXRhIjoiQ2xnS0pTOWpiM050YjNNdWMzUmhhMmx1Wnk1Mk1XSmxkR0V4TGsxeloxVnVaR1ZzWldkaGRHVVNMd29MWTI5emJXOXpNWFJsYzNRU0VtTnZjMjF2YzNaaGJHOXdaWEl4ZEdWemRCb01DZ1YxYjNOdGJ4SURNVEF3IiwibWVtbyI6IiJ9', | ||
protoMsgMocks.undelegate.ack, | ||
); | ||
const undelegateInvVow = await E( | ||
result.invitationMakers, | ||
).UndelegateAndTransfer([ | ||
{ validatorAddress: 'cosmosvaloper1test', shares: '100' }, | ||
]); | ||
const undelegateInv = await vt.when(undelegateInvVow); | ||
t.like(await E(zoe).getInvitationDetails(undelegateInv), { | ||
description: 'Undelegate and transfer', | ||
}); | ||
|
||
const undelegateUserSeat = await E(zoe).offer(undelegateInv); | ||
|
||
// Wait for the unbonding period | ||
timer.advanceBy(UNBOND_PERIOD_SECONDS * 1000n); | ||
|
||
const undelegateResult = await vt.when( | ||
E(undelegateUserSeat).getOfferResult(), | ||
); | ||
t.is(undelegateResult, 'guest undelegateAndTransfer complete'); | ||
|
||
// snapshot the resulting contract baggage | ||
const tree = inspectMapStore(contractBaggage); | ||
t.snapshot(tree, 'contract baggage after start'); | ||
}); |
93 changes: 93 additions & 0 deletions
93
packages/orchestration/test/examples/snapshots/continuing-invitation.test.ts.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# Snapshot report for `test/examples/continuing-invitation.test.ts` | ||
|
||
The actual snapshot is saved in `continuing-invitation.test.ts.snap`. | ||
|
||
Generated by [AVA](https://avajs.dev). | ||
|
||
## start | ||
|
||
> contract baggage after start | ||
{ | ||
'Durable Publish Kit_kindHandle': 'Alleged: kind', | ||
Recorder_kindHandle: 'Alleged: kind', | ||
asyncFlow: { | ||
AdminAsyncFlow_kindHandle: 'Alleged: kind', | ||
AdminAsyncFlow_singleton: 'Alleged: AdminAsyncFlow', | ||
Bijection_kindHandle: 'Alleged: kind', | ||
FunctionUnwrapper_kindHandle: 'Alleged: kind', | ||
FunctionUnwrapper_singleton: 'Alleged: FunctionUnwrapper', | ||
LogStore_kindHandle: 'Alleged: kind', | ||
StateUnwrapper_kindHandle: 'Alleged: kind', | ||
asyncFuncEagerWakers: [], | ||
asyncFuncFailures: {}, | ||
flowForOutcomeVow: {}, | ||
unwrapMap: 'Alleged: weakMapStore', | ||
}, | ||
contract: { | ||
CombinedInvitationMakers_kindHandle: 'Alleged: kind', | ||
ContinuingInvitationExampleInvitationMakers_kindHandle: 'Alleged: kind', | ||
orchestration: { | ||
makeAccount: { | ||
asyncFlow_kindHandle: 'Alleged: kind', | ||
endowments: { | ||
0: { | ||
flows: { | ||
makeAccount_kindHandle: 'Alleged: kind', | ||
makeAccount_singleton: 'Alleged: makeAccount', | ||
undelegateAndTransfer_kindHandle: 'Alleged: kind', | ||
undelegateAndTransfer_singleton: 'Alleged: undelegateAndTransfer', | ||
}, | ||
makeCombineInvitationMakers_kindHandle: 'Alleged: kind', | ||
makeCombineInvitationMakers_singleton: 'Alleged: makeCombineInvitationMakers', | ||
makeExtraInvitationMaker_kindHandle: 'Alleged: kind', | ||
makeExtraInvitationMaker_singleton: 'Alleged: makeExtraInvitationMaker', | ||
}, | ||
}, | ||
}, | ||
undelegateAndTransfer: { | ||
asyncFlow_kindHandle: 'Alleged: kind', | ||
endowments: { | ||
0: { | ||
flows: { | ||
makeAccount_kindHandle: 'Alleged: kind', | ||
makeAccount_singleton: 'Alleged: makeAccount', | ||
undelegateAndTransfer_kindHandle: 'Alleged: kind', | ||
undelegateAndTransfer_singleton: 'Alleged: undelegateAndTransfer', | ||
}, | ||
makeCombineInvitationMakers_kindHandle: 'Alleged: kind', | ||
makeCombineInvitationMakers_singleton: 'Alleged: makeCombineInvitationMakers', | ||
makeExtraInvitationMaker_kindHandle: 'Alleged: kind', | ||
makeExtraInvitationMaker_singleton: 'Alleged: makeExtraInvitationMaker', | ||
}, | ||
}, | ||
}, | ||
}, | ||
publicFacet_kindHandle: 'Alleged: kind', | ||
publicFacet_singleton: 'Alleged: publicFacet', | ||
}, | ||
orchestration: { | ||
'Cosmos Orchestration Account Holder_kindHandle': 'Alleged: kind', | ||
'Local Orchestration Account Kit_kindHandle': 'Alleged: kind', | ||
LocalChainFacade_kindHandle: 'Alleged: kind', | ||
Orchestrator_kindHandle: 'Alleged: kind', | ||
RemoteChainFacade_kindHandle: 'Alleged: kind', | ||
chainName: { | ||
osmosis: 'Alleged: RemoteChainFacade public', | ||
}, | ||
ibcTools: { | ||
IBCTransferSenderKit_kindHandle: 'Alleged: kind', | ||
ibcResultWatcher_kindHandle: 'Alleged: kind', | ||
ibcResultWatcher_singleton: 'Alleged: ibcResultWatcher', | ||
}, | ||
packetTools: { | ||
PacketToolsKit_kindHandle: 'Alleged: kind', | ||
}, | ||
}, | ||
vows: { | ||
PromiseWatcher_kindHandle: 'Alleged: kind', | ||
VowInternalsKit_kindHandle: 'Alleged: kind', | ||
WatchUtils_kindHandle: 'Alleged: kind', | ||
}, | ||
zoe: {}, | ||
} |
Binary file added
BIN
+1.29 KB
packages/orchestration/test/examples/snapshots/continuing-invitation.test.ts.snap
Binary file not shown.