From 4983f3a228d1381263ceef30ce78a0db015b0dc1 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 24 Sep 2024 09:28:47 -0700 Subject: [PATCH 1/3] BREAKING CHANGE: remove SharedStateRecord --- packages/async-flow/index.js | 1 - packages/async-flow/src/endowments.js | 32 --------------------------- 2 files changed, 33 deletions(-) diff --git a/packages/async-flow/index.js b/packages/async-flow/index.js index a795feb1424..cd3db12eb51 100644 --- a/packages/async-flow/index.js +++ b/packages/async-flow/index.js @@ -1,3 +1,2 @@ export * from './src/async-flow.js'; export * from './src/types-index.js'; -export { makeSharedStateRecord } from './src/endowments.js'; diff --git a/packages/async-flow/src/endowments.js b/packages/async-flow/src/endowments.js index 1aba97efaaf..909fb06389f 100644 --- a/packages/async-flow/src/endowments.js +++ b/packages/async-flow/src/endowments.js @@ -53,38 +53,6 @@ export const forwardingMethods = rem => { return fromEntries(keys.map(makeMethodEntry)); }; -/** - * Given a possibly mutable (and therefore unhardened) record, return a - * corresponding state record that acts identically for normal - * gets and sets, but is implemented using accessors, so it will be recognized - * as a state record. - * - * @template { string | number | symbol } K - * @template {Record} R - * @param {R} dataRecord - * @returns {R} - */ -export const makeSharedStateRecord = dataRecord => - harden( - create( - objectPrototype, - fromEntries( - ownKeys(dataRecord).flatMap(key => - entries( - getOwnPropertyDescriptors({ - get [key]() { - return dataRecord[key]; - }, - set [key](newValue) { - dataRecord[key] = newValue; - }, - }), - ), - ), - ), - ), - ); - /** * @param {Zone} outerZone * @param {PreparationOptions} [outerOptions] From d97c7b0dfda06c719bbc5414471668a185bd5672 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Tue, 24 Sep 2024 09:51:13 -0700 Subject: [PATCH 2/3] chore: sharedLocalAccount pattern --- .../src/examples/send-anywhere.contract.js | 25 +++++--- .../src/examples/send-anywhere.flows.js | 23 +++---- .../src/examples/shared.flows.js | 21 +++++++ .../examples/staking-combinations.contract.js | 28 +++++---- .../examples/staking-combinations.flows.js | 21 ++++--- .../snapshots/send-anywhere.test.ts.md | 26 ++++++-- .../snapshots/send-anywhere.test.ts.snap | Bin 1340 -> 1535 bytes .../snapshots/staking-combinations.test.ts.md | 10 ++-- .../staking-combinations.test.ts.snap | Bin 2573 -> 2584 bytes .../test/fixtures/zoe-tools.contract.js | 24 +++++--- .../test/fixtures/zoe-tools.flows.js | 56 +++++++++--------- 11 files changed, 146 insertions(+), 88 deletions(-) create mode 100644 packages/orchestration/src/examples/shared.flows.js diff --git a/packages/orchestration/src/examples/send-anywhere.contract.js b/packages/orchestration/src/examples/send-anywhere.contract.js index f555817d7dd..3cfab6ec922 100644 --- a/packages/orchestration/src/examples/send-anywhere.contract.js +++ b/packages/orchestration/src/examples/send-anywhere.contract.js @@ -1,5 +1,3 @@ -import { makeSharedStateRecord } from '@agoric/async-flow'; - import { InvitationShape } from '@agoric/zoe/src/typeGuards.js'; import { E } from '@endo/far'; import { M } from '@endo/patterns'; @@ -7,6 +5,7 @@ import { prepareChainHubAdmin } from '../exos/chain-hub-admin.js'; import { AnyNatAmountShape } from '../typeGuards.js'; import { withOrchestration } from '../utils/start-helper.js'; import * as flows from './send-anywhere.flows.js'; +import * as sharedFlows from './shared.flows.js'; /** * @import {Vow} from '@agoric/vow'; @@ -38,12 +37,6 @@ export const contract = async ( zone, { chainHub, orchestrateAll, vowTools, zoeTools }, ) => { - const contractState = makeSharedStateRecord( - /** @type {{ account: OrchestrationAccount | undefined }} */ { - localAccount: undefined, - }, - ); - const creatorFacet = prepareChainHubAdmin(zone, chainHub); // UNTIL https://github.com/Agoric/agoric-sdk/issues/9066 @@ -51,10 +44,24 @@ export const contract = async ( /** @type {(msg: string) => Vow} */ const log = msg => vowTools.watch(E(logNode).setValue(msg)); + const { makeLocalAccount } = orchestrateAll(sharedFlows, {}); + /** + * Setup a shared local account for use in async-flow functions. Typically, + * exo initState functions need to resolve synchronously, but `makeOnce` + * allows us to provide a Promise. When using this inside a flow, we must + * await it to ensure the account is available for use. + * + * @type {any} sharedLocalAccountP expects a Promise but this is a vow + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccountP = zone.makeOnce('localAccount', () => + makeLocalAccount(), + ); + // orchestrate uses the names on orchestrationFns to do a "prepare" of the associated behavior const orchFns = orchestrateAll(flows, { - contractState, log, + sharedLocalAccountP, zoeTools, }); diff --git a/packages/orchestration/src/examples/send-anywhere.flows.js b/packages/orchestration/src/examples/send-anywhere.flows.js index 74ab64b5f03..6022fcb40f1 100644 --- a/packages/orchestration/src/examples/send-anywhere.flows.js +++ b/packages/orchestration/src/examples/send-anywhere.flows.js @@ -5,8 +5,9 @@ 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, LocalAccountMethods, OrchestrationAccountI, OrchestrationFlow} from '../types.js'; + * @import {Orchestrator, OrchestrationFlow, LocalAccountMethods} from '../types.js'; */ const { entries } = Object; @@ -18,7 +19,7 @@ const { entries } = Object; * @satisfies {OrchestrationFlow} * @param {Orchestrator} orch * @param {object} ctx - * @param {{ localAccount?: OrchestrationAccountI & LocalAccountMethods }} ctx.contractState + * @param {Promise>} ctx.sharedLocalAccountP * @param {GuestInterface} ctx.zoeTools * @param {GuestOf<(msg: string) => Vow>} ctx.log * @param {ZCFSeat} seat @@ -26,7 +27,7 @@ const { entries } = Object; */ export const sendIt = async ( orch, - { contractState, log, zoeTools: { localTransfer, withdrawToSeat } }, + { sharedLocalAccountP, log, zoeTools: { localTransfer, withdrawToSeat } }, seat, offerArgs, ) => { @@ -44,23 +45,23 @@ export const sendIt = async ( `${amt.brand} not registered in vbank`, ); - // FIXME racy - if (!contractState.localAccount) { - contractState.localAccount = await agoric.makeAccount(); - } - const chain = await orch.getChain(chainName); const info = await chain.getChainInfo(); const { chainId } = info; assert(typeof chainId === 'string', 'bad chainId'); void log(`got info for chain: ${chainName} ${chainId}`); - await localTransfer(seat, contractState.localAccount, give); + /** + * @type {any} XXX methods returning vows + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccount = await sharedLocalAccountP; + await localTransfer(seat, sharedLocalAccount, give); void log(`completed transfer to localAccount`); try { - await contractState.localAccount.transfer( + await sharedLocalAccount.transfer( { value: destAddr, encoding: 'bech32', @@ -70,7 +71,7 @@ export const sendIt = async ( ); void log(`completed transfer to ${destAddr}`); } catch (e) { - await withdrawToSeat(contractState.localAccount, seat, give); + await withdrawToSeat(sharedLocalAccount, seat, give); const errorMsg = `IBC Transfer failed ${q(e)}`; void log(`ERROR: ${errorMsg}`); seat.exit(errorMsg); diff --git a/packages/orchestration/src/examples/shared.flows.js b/packages/orchestration/src/examples/shared.flows.js new file mode 100644 index 00000000000..80d2e8d25fd --- /dev/null +++ b/packages/orchestration/src/examples/shared.flows.js @@ -0,0 +1,21 @@ +/** + * @file Flows shared by multiple examples + * + * A module with flows can be used be reused across multiple contracts. They are + * bound to a particular contract's context via orchestrateAll. See + * ./send-anywhere.contract.js for example usage. + */ +/** + * @import {Orchestrator, OrchestrationFlow, LocalAccountMethods} from '../types.js'; + */ + +/** + * @satisfies {OrchestrationFlow} + * @param {Orchestrator} orch + * @returns {Promise} + */ +export const makeLocalAccount = async orch => { + const agoricChain = await orch.getChain('agoric'); + return agoricChain.makeAccount(); +}; +harden(makeLocalAccount); diff --git a/packages/orchestration/src/examples/staking-combinations.contract.js b/packages/orchestration/src/examples/staking-combinations.contract.js index 2158a4560bb..0f233c85fdf 100644 --- a/packages/orchestration/src/examples/staking-combinations.contract.js +++ b/packages/orchestration/src/examples/staking-combinations.contract.js @@ -5,7 +5,6 @@ * The primary offer result is a power for invitation makers that can perform * actions with an ICA account. */ -import { makeSharedStateRecord } from '@agoric/async-flow'; import { AmountShape } from '@agoric/ertp'; import { M } from '@endo/patterns'; import { prepareCombineInvitationMakers } from '../exos/combine-invitation-makers.js'; @@ -13,6 +12,7 @@ import { CosmosOrchestrationInvitationMakersI } from '../exos/cosmos-orchestrati import { ChainAddressShape, DelegationShape } from '../typeGuards.js'; import { withOrchestration } from '../utils/start-helper.js'; import * as flows from './staking-combinations.flows.js'; +import * as sharedFlows from './shared.flows.js'; import { prepareChainHubAdmin } from '../exos/chain-hub-admin.js'; /** @@ -46,16 +46,6 @@ const contract = async ( zone, { orchestrateAll, zoeTools, chainHub }, ) => { - const contractState = makeSharedStateRecord( - /** - * @type {{ - * account: (OrchestrationAccount & LocalAccountMethods) | undefined; - * }} - */ { - localAccount: undefined, - }, - ); - const StakingCombinationsInvitationMakersI = M.interface( 'StakingCombinationsInvitationMakersI', { @@ -128,8 +118,22 @@ const contract = async ( StakingCombinationsInvitationMakersI, ); + const { makeLocalAccount } = orchestrateAll(sharedFlows, {}); + /** + * Setup a shared local account for use in async-flow functions. Typically, + * exo initState functions need to resolve synchronously, but `makeOnce` + * allows us to provide a Promise. When using this inside a flow, we must + * await it to ensure the account is available for use. + * + * @type {any} sharedLocalAccountP expects a Promise but this is a vow + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccountP = zone.makeOnce('localAccount', () => + makeLocalAccount(), + ); + const orchFns = orchestrateAll(flows, { - contractState, + sharedLocalAccountP, makeCombineInvitationMakers, makeExtraInvitationMaker, flows, diff --git a/packages/orchestration/src/examples/staking-combinations.flows.js b/packages/orchestration/src/examples/staking-combinations.flows.js index 9bfaba08d54..23b1583ce58 100644 --- a/packages/orchestration/src/examples/staking-combinations.flows.js +++ b/packages/orchestration/src/examples/staking-combinations.flows.js @@ -2,6 +2,7 @@ * @import {GuestInterface} from '@agoric/async-flow'; * @import {Orchestrator, OrchestrationFlow, AmountArg, CosmosValidatorAddress, ChainAddress, LocalAccountMethods, OrchestrationAccountI} from '../types.js' * @import {ContinuingOfferResult, InvitationMakers} from '@agoric/smart-wallet/src/types.js'; + * @import {LocalOrchestrationAccountKit} from '../exos/local-orchestration-account.js'; * @import {MakeCombineInvitationMakers} from '../exos/combine-invitation-makers.js'; * @import {CosmosOrchestrationAccount} from '../exos/cosmos-orchestration-account.js'; * @import {ResolvedContinuingOfferResult, ZoeTools} from '../utils/zoe-tools.js'; @@ -47,7 +48,7 @@ harden(makeAccount); * @satisfies {OrchestrationFlow} * @param {Orchestrator} orch * @param {object} ctx - * @param {{ localAccount?: OrchestrationAccountI & LocalAccountMethods }} ctx.contractState + * @param {Promise>} ctx.sharedLocalAccountP * @param {GuestInterface} ctx.zoeTools * @param {GuestInterface} account * @param {ZCFSeat} seat @@ -56,7 +57,7 @@ harden(makeAccount); */ export const depositAndDelegate = async ( orch, - { contractState, zoeTools }, + { sharedLocalAccountP, zoeTools }, account, seat, validator, @@ -64,18 +65,20 @@ export const depositAndDelegate = async ( await null; trace('depositAndDelegate', account, seat, validator); mustMatch(validator, ChainAddressShape); - if (!contractState.localAccount) { - const agoricChain = await orch.getChain('agoric'); - contractState.localAccount = await agoricChain.makeAccount(); - } + const { give } = seat.getProposal(); - await zoeTools.localTransfer(seat, contractState.localAccount, give); + /** + * @type {any} XXX methods returning vows + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccount = await sharedLocalAccountP; + await zoeTools.localTransfer(seat, sharedLocalAccount, give); const address = account.getAddress(); try { - await contractState.localAccount.transfer(address, give.Stake); + await sharedLocalAccount.transfer(address, give.Stake); } catch (cause) { - await zoeTools.withdrawToSeat(contractState.localAccount, seat, give); + await zoeTools.withdrawToSeat(sharedLocalAccount, seat, give); const errMsg = makeError(`ibc transfer failed ${q(cause)}`); seat.exit(errMsg); throw errMsg; diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md index 38ac9729bdd..d168ee29da0 100644 --- a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.md @@ -19,16 +19,30 @@ Generated by [AVA](https://avajs.dev). FunctionUnwrapper_singleton: 'Alleged: FunctionUnwrapper', LogStore_kindHandle: 'Alleged: kind', StateUnwrapper_kindHandle: 'Alleged: kind', - asyncFuncEagerWakers: [], + asyncFuncEagerWakers: [ + Object @Alleged: asyncFlow flow {}, + ], asyncFuncFailures: {}, - flowForOutcomeVow: {}, + flowForOutcomeVow: { + 'Alleged: VowInternalsKit vowV0': 'Alleged: asyncFlow flow', + }, unwrapMap: 'Alleged: weakMapStore', }, chainHub: { ChainHub_kindHandle: 'Alleged: kind', ChainHub_singleton: 'Alleged: ChainHub', brandDenom: {}, - chainInfos: {}, + chainInfos: { + agoric: { + chainId: 'agoric-3', + icqEnabled: false, + stakingTokens: [ + { + denom: 'ubld', + }, + ], + }, + }, connectionInfos: {}, denom: {}, lookupChainInfo_kindHandle: 'Alleged: kind', @@ -40,13 +54,15 @@ Generated by [AVA](https://avajs.dev). 'ChainHub Admin_singleton': 'Alleged: ChainHub Admin', 'Send PF_kindHandle': 'Alleged: kind', 'Send PF_singleton': 'Alleged: Send PF', + localAccount: 'Vow', orchestration: { + makeLocalAccount: { + asyncFlow_kindHandle: 'Alleged: kind', + }, sendIt: { asyncFlow_kindHandle: 'Alleged: kind', endowments: { 0: { - contractState_kindHandle: 'Alleged: kind', - contractState_singleton: 'Alleged: contractState', log_kindHandle: 'Alleged: kind', log_singleton: 'Alleged: log', zoeTools: { diff --git a/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap b/packages/orchestration/test/examples/snapshots/send-anywhere.test.ts.snap index 897c3c4e64b0a400c6639ac4ed092169d96d0e52..70f6bb08d283bef5fbba6dc9f212d271119b6f16 100644 GIT binary patch literal 1535 zcmVSWAc;MHv2S-ZMKr(=)qC_7$Cj2#SV)A)*Mf*<{V)u37dK zB!^JbT|3)OPu1F8lgS1GhJfG;3?9wJh1`4`BB(b#`8a4q(2EzN2!bf0pt&S6J+JQS z-DP|CG&|q-e^viq|Nqxt^Uhk;7DnB@vc;q&ZC0z&T8-A2<#M{=t_$g2*)mNk>GQ+S z#F0k(k7P(@2EZJEHvxPJ;1>XQ0gMvhaRLmJREea{R9|3*B&7dQ*L4sLBn?u;Fo}(j zI7qTY?mO9ZXw_zwg=W>Z-1W+{mONj#xOtj#(`G%IHf7ta#>^)xZA{S^USx)FOy=yt zut3N>0eWzjx|`gXu|>;+uKS>ju}&?1x>@y*p96^C(xHa*ko#f-L=%p&&RpqGX$kJZ z)CVUnh?d)qV*Yvz*opx^#ej$6fDs37#DTx#z#|Euo&c^TfDaSE4+$lzv0zj)Bv(iR zBS~OB3A~a7ZX|)vlECdGa3={okpix!6q&Rtll~$Fe4PTcG_aBe-be$Vr-47yifK_b z%@#7iNCsHX03T<7I~m}37Py!N-pT^+Wfh5oq1C_20=L?<9AM;ttsHPW2OJ(!Ovgi6 zHKb(3*p%6@_*Cy~qbAg-V(wbJW-}>xk8r4O9b_sxBPXo(E-k^M?qG7J$vwjr-g4;1 z#-4IJpfC|u2)Qxd7YLEn5}OmXB`F*hbsJ+#l1diEivl6DLxY+b=~QTw8>eWEIcI5| zIj+azK31vEnxU56bXbS-x}UOUi`I;A&NQVV8f-OmTG^(z%Xzxd`{qD@Yb{3W^K`=t zftPVvH9*tdZV@#=<-TF)cuIY{X@4ua>2oSqbtpGaGA#B;E0Chva?hg>(Mi3d{wk&xnvHfw!4o^IpD3k zA}!n9+aZ1Di}$$WcTBl)G9>m_9{4xkooSL&jzc#|y!WYCEVhd)Fz+PQQ7J|=Agw85 zQA(i&(M(8WMFY;MBH3L2c4S2esh>eC^ zd#wOmQ)y$nt~n7zb07p{h>%+a@8zR;{|BEg{WMUrt#U!MR*#1JfB&sfS@SZ!qgf#2 zUSW_uDOwYbWq4@JLyr^(sNSG;Hs|Zx9%IGFNV$xgv(mGVtGDLtiRopBa(9h6?K^=v z`{|-q)mUV1)0X=!&X7Q;Q;hZPRqnPYF}5)1aC`GeIsC-V$JEQfmx{p4Md1A+@J&%k ztz0Mr|0n|gsI*e3K#u6ZF&*H#LM?~hIO<`hJL8?%zW_Rk^XOv=FJ~Dqp5pCx)S|uVEoxEgPHWqQN-f_vu|>^uK3?`s6_*6-4h|WTJYEK#Dg&3w zz^7&4URiPQK*+&_Ixc%dXRirG9>9XuA!iQyobeIWcG0rry6Mo?vRGm?aswPy%y*gi lgG^@ADzh(6_I^I9bboTI&^5`NiYuuj{|B$6SvZ#y008qX?)Lxy literal 1340 zcmV-C1;hG5RzV^Vn|0F|LX`w~cbLq9<$|yr~vs zCF{PH#UG0Z00000000A>SWRpjMHK$V_S$R5UfWIDrfCWmKN3O$AtY3RxTGmoBT^cY zv=s+b#=8@H$$G|`9oLi#>H!HP5J(7#L#fmXRUA0Lg&QY&p;FZYXF&W^zyTo+ToHJk z-R#a%E1R6GeBb-#&HI`6>}y*cPuSc3&;>{v(s|b`!z>6i|?Go^HNi&<#&B{NO zfZs~MvnKG43EVb;N*P!!1Kl!kxvWVvb&1l|GVsZN^k2)s@d|LU0(@Bk{;FuE2U8U_ zqgBI^C8zK5r7*8qi)mgn_g&ugm=ZiB9FDA$OigF*ggf@q6+Ej4i|ru~4A1hBq=Uhp z>N=rtG_8=j#zG{JBJ1t7D!Lm=NS4)&BO8h;mgN=}A;)HR`(bBr~ zQkuJjJ28UcOf(a|E_gz04+qPkZq~a#6tnl2xU(ENkyYvb9i_xA_8?LNG4u)zgFETv z%x{HeKIN@QGW9yxVBBe~wX>ez4DC_}HzKngN|59~X6`+zY9OX2|M;@-`@(OXi8EVE zwk?KSwN43dZzDIM`RJ;!sdo>_ea!x&fnE^ZHnpjf`H?!j>x#&Ho5gk37m8&&-XdhK z8e2hvR(*l?SpYp5gG{$$Zwlc}T9n3+nE|!8nc4wsAVtlyV?mv*GbJyL*Q@qVM|bJXLltd*v*g6QdQbnB>&+_faVagLBU+*Yo$5q_<-6UXbyN zX_Lg9tSH8kl=KqGho9>8dW41ROF`RwlBh8iElowusc3mBTG98h+>I*mSykJ;s#8S{ z=pu!iRp3??sMmnCnkF`zI`c{mxUSRYrk;5ywGEOWbwAd2Q&;pEcUDw@=Jipsa$X?vdzEV?yMK;7iO87I4=B9;^dy z9r(Bo+^K7N^^~5z8q9}Z@`5Ec^HfP%r}B9yGENXR70VOdtS8sTHmUQfk%=d|LF9g~ z$L7rIg6)hFlNgH);DHA4N&~pj0PZ%l;5AagE9k-V##1@$tjxhnT2BdHf~fbv$W=W@ y($S{aU^H`A&uivWcDp2#d9=g4*A~OXwswA4Y0)jkWXo4ns(%4Pr7(1T5dZ*4PmdG; diff --git a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md index 8d65dc66fcb..2c914a1bdf9 100644 --- a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md +++ b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.md @@ -105,13 +105,12 @@ Generated by [AVA](https://avajs.dev). 'ChainHub Admin_singleton': 'Alleged: ChainHub Admin', CombinedInvitationMakers_kindHandle: 'Alleged: kind', StakingCombinationsInvitationMakers_kindHandle: 'Alleged: kind', + localAccount: 'Vow', orchestration: { depositAndDelegate: { asyncFlow_kindHandle: 'Alleged: kind', endowments: { 0: { - contractState_kindHandle: 'Alleged: kind', - contractState_singleton: 'Alleged: contractState', flows: { depositAndDelegate_kindHandle: 'Alleged: kind', depositAndDelegate_singleton: 'Alleged: depositAndDelegate', @@ -137,8 +136,6 @@ Generated by [AVA](https://avajs.dev). asyncFlow_kindHandle: 'Alleged: kind', endowments: { 0: { - contractState_kindHandle: 'Alleged: kind', - contractState_singleton: 'Alleged: contractState', flows: { depositAndDelegate_kindHandle: 'Alleged: kind', depositAndDelegate_singleton: 'Alleged: depositAndDelegate', @@ -160,12 +157,13 @@ Generated by [AVA](https://avajs.dev). }, }, }, + makeLocalAccount: { + asyncFlow_kindHandle: 'Alleged: kind', + }, undelegateAndTransfer: { asyncFlow_kindHandle: 'Alleged: kind', endowments: { 0: { - contractState_kindHandle: 'Alleged: kind', - contractState_singleton: 'Alleged: contractState', flows: { depositAndDelegate_kindHandle: 'Alleged: kind', depositAndDelegate_singleton: 'Alleged: depositAndDelegate', diff --git a/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.snap b/packages/orchestration/test/examples/snapshots/staking-combinations.test.ts.snap index 45b56186c154fcc15ba34f43428c868dcaab9d2b..9af887fc4b1a83295df6ff912adff80d08bbb317 100644 GIT binary patch literal 2584 zcmV+z3g`7fRzVAIEe2B7oxnz760N0Pg_!4}b&*9OQrw&fm`Y zPiF5YqQP8CP0RFshlv;01W7qs z$Xb@ST^3a_G(j{h%ZR6938|X6Ms!0H3`tciv*a*)&#RldIf|`Mdw}a6;4dCvx(Qfs z0-kFEK5PPJynyZnzUu{k<^}%frAF1`9#x)e>GT2JKH!WGc+3Yp=L25x0q^<%&JWD{ zfy;i1#!u5U-|_>$^#kz$@Rb1YYykKm01WWJJP$m~Qxr*t;sy`A%mc}0KxziAHv@le z2F6=}!!5v9TPTV>G)3TI3vlTU{G}Ek76g`pz~e#SW{`5-$7ENK%8cZEZbMS$%X8dl zVkIbdT~bzMVyH?P&}DnOkrYqtkYq+FsY;^}bT1YZ%khk|sR{XfT{-SzNV5z~H&QkM z1E!bSadmacP&LwM7|A6=Fi0c42y@&A!OGf+6<2CO5swI~M7u{=Cz@`dI$JBKb{n-Q zNOD0VMaYa3vLdUSi>h|AV2J7lIm^sebHR%8gpe=)61PL!B*OX$A#V+Vm2u7V04HA-QA9dC9Q$krP&~HyY61B|})3l+_}iMWlaR&Q@6!1$kZ+)q-M_ ze{4Ei+CBf-y7f-zqUp?=o4vRwh@|1oKJN5o203QCI$%|&d+&m(Z>W0aq%+P;scV^| zs$5?^;o_V)ZVt~DlshPF(iWeUE{lRFW@4E!hzycP!j76!r*$|c1FF&hCs7Xcokp`W3l z-k(Q+7b5`Q1{`an;FC{;)q#pTjOVP6l0G?I z?!cX6*Ru|AB~Gtp`@O>!&5^vSOUAre9?4`?Fp5&-jXDHpLs%yzowkho9L6@&c-*n? zS%|X^M7*HnO4Rd8Zdnr)eZ{Pti`{n(c4^egUo-aD(@J03zv$A`9+mPh{Wb%m);2{5 zCfHp!9Xqs84s?kqIil@kdy=x^X`&ZoW2dUe5HJ#)wEbyp)nrbds%#zg^@!)Zm!iPe zqrme~;8v7cUd_y+{d*L6FA8KjfcXvz-b%y$_jLd(9l&=wfFE}NZ*>6wrrp{Yw^R(s z#DJw3@IVasUJUq2jB@K>^y-Zma61NgbTN8$e;mlif$KCh%|L${2YwR= z{GAju!)Vn&Coo3Ch8f0eC$QEDT%(~QjBwF894MY^Y_;Oc!b0I%B=LbGin+ZNp`JEt z1;5bgs8;o2t-P$R6GgA9wV+GA>S^%p+70@cwJu9UG0sVLlaZ-|lZLfeHzdWFJYcc% zT@}_=OP`3A7c}DnMdzvTv{nv%lXp`vpKhMht#4lQzc9z$^o7Jd*@oN&c zk22c0F9pn|fE5}VV_JGL1^h4t{5l1A(!gvQSWW{M)4-Ey$~|7QVoX#m3Cps6BMrPv z6LvG||3(_PO~d*b*gw+1zi8N~8`feaR*>Ubx~(?|`}=h9dzHl5R)FUA)5;O5=*qe3 zYI9&_uIdYhxt2{asca49UnXbiQ(8&mPMI3FK)VIYZZlJ~W7O$*O4SUqQ+3Z3#FF%P1p z+u@IUkvm+Ai#dc1*;F;$=zDAvdgMVv6DVM#p$=?G#KhU}inE$ev7jAr+(JI264ymz znB!Ikc4y+#nTf00%*5-vGw~_SL}ak)Oz>Rb&>(Ph5V$-Dyg3MT4FUHK0au17rR-%c zBi&#sP60xH1m>X&e}xpj^k9)*hY!9-RPwF#!bj0E>GlmkDO$ zV4gA9tI39+F{)71c*dx97wUeQbKHbB{08Clt?@GFx@>hZWU5}IHvA@BorQ_0*CyHe z{+7NRNLUY`JB_T%?af5gX*;`s^d0A>WUS>hVRKnsB0|H{l%U*eJXE`pyE(j@!@D`W uo5Q;~{J+g%A`4j}KiFOVE7VL@SX~uXNk&*Ph?cSbiub?YD=mrKD*ymW#?%%7 literal 2573 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 { - const contractState = makeSharedStateRecord( - /** @type {{ account: OrchestrationAccount | undefined }} */ { - localAccount: undefined, - }, - ); - const creatorFacet = prepareChainHubAdmin(zone, chainHub); + const { makeLocalAccount } = orchestrateAll(sharedFlows, {}); + /** + * Setup a shared local account for use in async-flow functions. Typically, + * exo initState functions need to resolve synchronously, but `makeOnce` + * allows us to provide a Promise. When using this inside a flow, we must + * await it to ensure the account is available for use. + * + * @type {any} sharedLocalAccountP expects a Promise but this is a vow + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccountP = zone.makeOnce('localAccount', () => + makeLocalAccount(), + ); + const orchFns = orchestrateAll(flows, { - contractState, + sharedLocalAccountP, zoeTools, }); diff --git a/packages/orchestration/test/fixtures/zoe-tools.flows.js b/packages/orchestration/test/fixtures/zoe-tools.flows.js index 15338b63d2a..4994984bafc 100644 --- a/packages/orchestration/test/fixtures/zoe-tools.flows.js +++ b/packages/orchestration/test/fixtures/zoe-tools.flows.js @@ -11,6 +11,8 @@ const { values } = Object; /** * @import {GuestInterface} from '@agoric/async-flow'; + * @import {AtomicProvider} from '@agoric/store/src/stores/store-utils.js'; + * @import {LocalOrchestrationAccountKit} from '../../src/exos/local-orchestration-account.js'; * @import {Orchestrator, LocalAccountMethods, OrchestrationAccountI, OrchestrationFlow, ChainAddress} from '@agoric/orchestration'; * @import {ZoeTools} from '../../src/utils/zoe-tools.js'; */ @@ -23,14 +25,14 @@ const { values } = Object; * @satisfies {OrchestrationFlow} * @param {Orchestrator} orch * @param {object} ctx - * @param {{ localAccount?: OrchestrationAccountI & LocalAccountMethods }} ctx.contractState + * @param {Promise>} ctx.sharedLocalAccountP * @param {GuestInterface} ctx.zoeTools * @param {ZCFSeat} seat * @param {{ destAddr: ChainAddress }} offerArgs */ export const depositSend = async ( orch, - { contractState, zoeTools: { localTransfer, withdrawToSeat } }, + { sharedLocalAccountP, zoeTools: { localTransfer, withdrawToSeat } }, seat, offerArgs, ) => { @@ -40,18 +42,17 @@ export const depositSend = async ( const { give } = seat.getProposal(); - await null; - if (!contractState.localAccount) { - const agoricChain = await orch.getChain('agoric'); - contractState.localAccount = await agoricChain.makeAccount(); - } - - await localTransfer(seat, contractState.localAccount, give); + /** + * @type {any} XXX methods returning vows + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccount = await sharedLocalAccountP; + await localTransfer(seat, sharedLocalAccount, give); try { - await contractState.localAccount.sendAll(destAddr, values(give)); + await sharedLocalAccount.sendAll(destAddr, values(give)); } catch (error) { - await withdrawToSeat(contractState.localAccount, seat, give); + await withdrawToSeat(sharedLocalAccount, seat, give); const errMsg = makeError(`SendAll failed ${q(error)}`); seat.exit(errMsg); throw errMsg; @@ -67,25 +68,24 @@ harden(depositSend); * @satisfies {OrchestrationFlow} * @param {Orchestrator} orch * @param {object} ctx - * @param {{ localAccount?: OrchestrationAccountI & LocalAccountMethods }} ctx.contractState + * @param {Promise>} ctx.sharedLocalAccountP * @param {GuestInterface} ctx.zoeTools * @param {ZCFSeat} seat */ export const deposit = async ( orch, - { contractState, zoeTools: { localTransfer } }, + { sharedLocalAccountP, zoeTools: { localTransfer } }, seat, ) => { const { give } = seat.getProposal(); - await null; - if (!contractState.localAccount) { - const agoricChain = await orch.getChain('agoric'); - contractState.localAccount = await agoricChain.makeAccount(); - } - + /** + * @type {any} XXX methods returning vows + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccount = await sharedLocalAccountP; try { - await localTransfer(seat, contractState.localAccount, give); + await localTransfer(seat, sharedLocalAccount, give); } catch (e) { seat.exit(e); throw e; @@ -100,25 +100,25 @@ harden(deposit); * @satisfies {OrchestrationFlow} * @param {Orchestrator} orch * @param {object} ctx - * @param {{ localAccount?: OrchestrationAccountI & LocalAccountMethods }} ctx.contractState + * @param {Promise>} ctx.sharedLocalAccountP * @param {GuestInterface} ctx.zoeTools * @param {ZCFSeat} seat */ export const withdraw = async ( orch, - { contractState, zoeTools: { withdrawToSeat } }, + { sharedLocalAccountP, zoeTools: { withdrawToSeat } }, seat, ) => { const { want } = seat.getProposal(); - await null; - if (!contractState.localAccount) { - const agoricChain = await orch.getChain('agoric'); - contractState.localAccount = await agoricChain.makeAccount(); - } + /** + * @type {any} XXX methods returning vows + * https://github.com/Agoric/agoric-sdk/issues/9822 + */ + const sharedLocalAccount = await sharedLocalAccountP; try { - await withdrawToSeat(contractState.localAccount, seat, want); + await withdrawToSeat(sharedLocalAccount, seat, want); } catch (e) { seat.exit(e); throw e; From 31e82eb982f437181c6c455e59c120b6e78c7601 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Wed, 2 Oct 2024 08:52:13 -0700 Subject: [PATCH 3/3] chore(types): LOA implements LocalAccountMethods --- packages/orchestration/src/examples/stake-bld.contract.js | 2 ++ .../orchestration/src/exos/local-orchestration-account.js | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/orchestration/src/examples/stake-bld.contract.js b/packages/orchestration/src/examples/stake-bld.contract.js index f51136e2629..db657e1235c 100644 --- a/packages/orchestration/src/examples/stake-bld.contract.js +++ b/packages/orchestration/src/examples/stake-bld.contract.js @@ -111,6 +111,8 @@ export const start = async (zcf, privateArgs, baggage) => { const { give } = seat.getProposal(); trace('makeStakeBldInvitation', give); const { holder } = await makeLocalAccountKit(); + /** @type {Record>} */ + // @ts-expect-error XXX PaymentPKeywordRecord throught deeplyFulfilled will be a PaymnentKeywordRecord const { In } = await deeplyFulfilled( withdrawFromSeat(zcf, seat, give), ); diff --git a/packages/orchestration/src/exos/local-orchestration-account.js b/packages/orchestration/src/exos/local-orchestration-account.js index cb75695dc35..bc5e1f2a48a 100644 --- a/packages/orchestration/src/exos/local-orchestration-account.js +++ b/packages/orchestration/src/exos/local-orchestration-account.js @@ -28,7 +28,7 @@ import { coerceCoin, coerceDenomAmount } from '../utils/amounts.js'; /** * @import {HostOf} from '@agoric/async-flow'; * @import {LocalChain, LocalChainAccount} from '@agoric/vats/src/localchain.js'; - * @import {AmountArg, ChainAddress, DenomAmount, IBCMsgTransferOptions, IBCConnectionInfo, OrchestrationAccountI} from '@agoric/orchestration'; + * @import {AmountArg, ChainAddress, DenomAmount, IBCMsgTransferOptions, IBCConnectionInfo, OrchestrationAccountI, LocalAccountMethods} from '@agoric/orchestration'; * @import {RecorderKit, MakeRecorderKit} from '@agoric/zoe/src/contractSupport/recorder.js'. * @import {Zone} from '@agoric/zone'; * @import {Remote} from '@agoric/internal'; @@ -613,14 +613,14 @@ export const prepareLocalOrchestrationAccountKit = ( * updater will get a special notification that the account is being * transferred. */ - /** @type {HostOf} */ + /** @type {HostOf} */ deposit(payment) { return watch( E(this.state.account).deposit(payment), this.facets.returnVoidWatcher, ); }, - /** @type {HostOf} */ + /** @type {HostOf} */ withdraw(amount) { return watch(E(this.state.account).withdraw(amount)); }, @@ -733,7 +733,7 @@ export const prepareLocalOrchestrationAccountKit = ( matchFirstPacket(patternV) { return watch(E(this.state.packetTools).matchFirstPacket(patternV)); }, - /** @type {HostOf} */ + /** @type {HostOf} */ monitorTransfers(tap) { return watch(E(this.state.packetTools).monitorTransfers(tap)); },