Skip to content

Commit

Permalink
test: bootstrap test for portfolio holder
Browse files Browse the repository at this point in the history
- adds portofolio holder to basic-flows.contract.js and tests wallet offers in bootstrap environment
  • Loading branch information
0xpatrickdev committed Jul 17, 2024
1 parent f123030 commit e1cc43a
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 17 deletions.
136 changes: 131 additions & 5 deletions packages/boot/test/bootstrapTests/orchestration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import {

const test: TestFn<WalletFactoryTestContext> = anyTest;

const validatorAddress: CosmosValidatorAddress = {
value: 'cosmosvaloper1test',
chainId: 'gaiatest',
encoding: 'bech32',
};

test.before(async t => {
t.context = await makeWalletFactoryContext(
t,
Expand Down Expand Up @@ -163,11 +169,6 @@ test.serial('stakeAtom - smart wallet', async t => {

const { ATOM } = agoricNamesRemotes.brand;
ATOM || Fail`ATOM missing from agoricNames`;
const validatorAddress: CosmosValidatorAddress = {
value: 'cosmosvaloper1test',
chainId: 'gaiatest',
encoding: 'bech32',
};

await t.notThrowsAsync(
wd.executeOffer({
Expand Down Expand Up @@ -320,3 +321,128 @@ test.serial('auto-stake-it - proposal', async t => {
),
);
});

test.serial('basic-flows - portfolio holder', async t => {
const { buildProposal, evalProposal, readLatest, agoricNamesRemotes } =
t.context;

await evalProposal(
buildProposal('@agoric/builders/scripts/orchestration/init-basic-flows.js'),
);

const wd =
await t.context.walletFactoryDriver.provideSmartWallet('agoric1test2');

// create a cosmos orchestration account
await wd.executeOffer({
id: 'request-portfolio-acct',
invitationSpec: {
source: 'agoricContract',
instancePath: ['basicFlows'],
callPipe: [['makePortfolioAccountInvitation']],
},
offerArgs: {
chainNames: ['agoric', 'cosmoshub', 'osmosis'],
},
proposal: {},
});
t.like(wd.getCurrentWalletRecord(), {
offerToPublicSubscriberPaths: [
[
'request-portfolio-acct',
{
agoric: 'published.basicFlows.agoric1mockVlocalchainAddress',
cosmoshub: 'published.basicFlows.cosmos1test',
// XXX support multiple chain addresses in ibc mocks
osmosis: 'published.basicFlows.cosmos1test',
},
],
],
});
t.like(wd.getLatestUpdateRecord(), {
status: { id: 'request-portfolio-acct', numWantsSatisfied: 1 },
});
// XXX this overrides a previous account, since mocks only provide one address
t.is(readLatest('published.basicFlows.cosmos1test'), '');
// XXX this overrides a previous account, since mocks only provide one address
t.is(readLatest('published.basicFlows.agoric1mockVlocalchainAddress'), '');

const { ATOM, BLD } = agoricNamesRemotes.brand;
ATOM || Fail`ATOM missing from agoricNames`;
BLD || Fail`BLD missing from agoricNames`;

await t.notThrowsAsync(
wd.executeOffer({
id: 'delegate-cosmoshub',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-portfolio-acct',
invitationMakerName: 'MakeInvitation',
invitationArgs: [
'cosmoshub',
'Delegate',
[validatorAddress, { brand: ATOM, value: 10n }],
],
},
proposal: {},
}),
);
t.like(wd.getLatestUpdateRecord(), {
status: { id: 'delegate-cosmoshub', numWantsSatisfied: 1 },
});

await t.notThrowsAsync(
wd.executeOffer({
id: 'delegate-agoric',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-portfolio-acct',
invitationMakerName: 'MakeInvitation',
invitationArgs: [
'agoric',
'Delegate',
// XXX use ChainAddress for LocalOrchAccount
['agoric1validator1', { brand: BLD, value: 10n }],
],
},
proposal: {},
}),
);
t.like(wd.getLatestUpdateRecord(), {
status: { id: 'delegate-agoric', numWantsSatisfied: 1 },
});

await t.throwsAsync(
wd.executeOffer({
id: 'delegate-2-cosmoshub',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-portfolio-acct',
invitationMakerName: 'MakeInvitation',
invitationArgs: [
'cosmoshub',
'Delegate',
[validatorAddress, { brand: ATOM, value: 504n }],
],
},
proposal: {},
}),
);

await t.throwsAsync(
wd.executeOffer({
id: 'delegate-2-agoric',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-portfolio-acct',
invitationMakerName: 'MakeInvitation',
invitationArgs: [
'agoric',
'Delegate',
['agoric1validator1', { brand: BLD, value: 504n }],
],
},
proposal: {},
}),
);
});
86 changes: 74 additions & 12 deletions packages/orchestration/src/examples/basic-flows.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
*/
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { M, mustMatch } from '@endo/patterns';
import { provideOrchestration } from '../utils/start-helper.js';
import { withOrchestration } from '../utils/start-helper.js';
import { preparePortfolioHolder } from '../exos/portfolio-holder-kit.js';

/**
* @import {Baggage} from '@agoric/vat-data';
* @import {Orchestrator} from '@agoric/orchestration';
* @import {Vow, VowTools} from '@agoric/vow';
* @import {Zone} from '@agoric/zone';
* @import {OrchestrationAccount, Orchestrator} from '@agoric/orchestration';
* @import {ResolvedPublicTopic} from '@agoric/zoe/src/contractSupport/topics.js';
* @import {OrchestrationPowers} from '../utils/start-helper.js';
* @import {MakePortfolioHolder} from '../exos/portfolio-holder-kit.js';
* @import {OrchestrationTools} from '../utils/start-helper.js';
*/

/**
Expand All @@ -30,19 +33,63 @@ const makeOrchAccountHandler = async (orch, _ctx, seat, { chainName }) => {
return cosmosAccount.asContinuingOffer();
};

/**
* Create accounts on multiple chains and return them in a single continuing
* offer with invitations makers for Delegate, WithdrawRewards, Transfer, etc.
* Calls to the underlying invitationMakers are proxied through the
* `MakeInvitation` invitation maker.
*
* @param {Orchestrator} orch
* @param {MakePortfolioHolder} makePortfolioHolder
* @param {ZCFSeat} seat
* @param {{ chainNames: string[] }} offerArgs
*/
const makePortfolioAcctHandler = async (
orch,
makePortfolioHolder,
seat,
{ chainNames },
) => {
seat.exit(); // no funds exchanged
mustMatch(chainNames, M.arrayOf(M.string()));
const allChains = await Promise.all(chainNames.map(n => orch.getChain(n)));
const allAccounts = await Promise.all(allChains.map(c => c.makeAccount()));

const accountEntries = harden(
/** @type {[string, OrchestrationAccount<any>][]} */ (
chainNames.map((chainName, index) => [chainName, allAccounts[index]])
),
);
const publicTopicEntries = harden(
/** @type {[string, ResolvedPublicTopic<unknown>][]} */ (
await Promise.all(
accountEntries.map(async ([name, account]) => {
const { account: topicRecord } = await account.getPublicTopics();
return [name, topicRecord];
}),
)
),
);
const portfolioHolder = makePortfolioHolder(
accountEntries,
publicTopicEntries,
);

return portfolioHolder.asContinuingOffer();
};

/**
* @param {ZCF} zcf
* @param {OrchestrationPowers & {
* marshaller: Marshaller;
* }} privateArgs
* @param {Baggage} baggage
* }} _privateArgs
* @param {Zone} zone
* @param {OrchestrationTools} tools
*/
export const start = async (zcf, privateArgs, baggage) => {
const { orchestrate, zone } = provideOrchestration(
zcf,
baggage,
privateArgs,
privateArgs.marshaller,
const contract = async (zcf, _privateArgs, zone, { orchestrate, vowTools }) => {
const makePortfolioHolder = preparePortfolioHolder(
zone.subZone('portfolio'),
vowTools,
);

const makeOrchAccount = orchestrate(
Expand All @@ -51,10 +98,17 @@ export const start = async (zcf, privateArgs, baggage) => {
makeOrchAccountHandler,
);

const makePortfolioAccount = orchestrate(
'makePortfolioAccount',
makePortfolioHolder,
makePortfolioAcctHandler,
);

const publicFacet = zone.exo(
'Basic Flows Public Facet',
M.interface('Basic Flows PF', {
makeOrchAccountInvitation: M.callWhen().returns(InvitationShape),
makePortfolioAccountInvitation: M.callWhen().returns(InvitationShape),
}),
{
makeOrchAccountInvitation() {
Expand All @@ -63,10 +117,18 @@ export const start = async (zcf, privateArgs, baggage) => {
'Make an Orchestration Account',
);
},
makePortfolioAccountInvitation() {
return zcf.makeInvitation(
makePortfolioAccount,
'Make an Orchestration Account',
);
},
},
);

return { publicFacet };
};

export const start = withOrchestration(contract);

/** @typedef {typeof start} BasicFlowsSF */

0 comments on commit e1cc43a

Please sign in to comment.