From db525f85a72c578bffcd055c151743fa8176dcd2 Mon Sep 17 00:00:00 2001 From: Michael FIG Date: Tue, 29 Sep 2020 13:07:50 -0600 Subject: [PATCH] feat: cleanups and fixes to the wallet --- .../lib/ag-solo/html/wallet-bridge.html | 31 +++-- .../dapp-svelte-wallet/api/src/lib-wallet.js | 113 +++++++++++------- .../dapp-svelte-wallet/api/src/observable.js | 2 +- packages/dapp-svelte-wallet/api/src/wallet.js | 89 +++++++++++--- .../dapp-svelte-wallet/ui/src/Amount.svelte | 4 +- .../ui/src/Transaction.svelte | 6 +- 6 files changed, 168 insertions(+), 77 deletions(-) diff --git a/packages/cosmic-swingset/lib/ag-solo/html/wallet-bridge.html b/packages/cosmic-swingset/lib/ag-solo/html/wallet-bridge.html index 0ac3769b7c2..a27bb986702 100644 --- a/packages/cosmic-swingset/lib/ag-solo/html/wallet-bridge.html +++ b/packages/cosmic-swingset/lib/ag-solo/html/wallet-bridge.html @@ -41,8 +41,16 @@ diff --git a/packages/dapp-svelte-wallet/api/src/lib-wallet.js b/packages/dapp-svelte-wallet/api/src/lib-wallet.js index 1175724158d..8af102d79ba 100644 --- a/packages/dapp-svelte-wallet/api/src/lib-wallet.js +++ b/packages/dapp-svelte-wallet/api/src/lib-wallet.js @@ -76,7 +76,6 @@ export async function makeWallet({ const idToCompiledOfferP = new Map(); const idToComplete = new Map(); const idToSeat = new Map(); - const idToOutcome = new Map(); // Client-side representation of the purses inbox; /** @type {Map} */ @@ -182,7 +181,8 @@ export async function makeWallet({ throw e; } }, - receive(payment) { + async receive(paymentP) { + const payment = await paymentP; return E(purse).deposit(payment); }, deposit(payment, amount = undefined) { @@ -246,11 +246,11 @@ export async function makeWallet({ async function updateInboxState(id, offer) { // Only sent the uncompiled offer to the client. - const { - instanceHandleBoardId, - installationHandleBoardId, - proposalTemplate, - } = offer; + const { instance, installation, proposalTemplate, ...rest } = offer; + if (!instance || !installation) { + return; + } + delete rest.actions; // We could get the instanceHandle and installationHandle from the // board and store them to prevent having to make this call each // time, but if we want the offers to be able to sent to the @@ -258,19 +258,16 @@ export async function makeWallet({ // installationHandle in these offer objects because the handles // are presences and we don't wish to send presences to the // frontend. - const instanceHandle = await E(board).getValue(instanceHandleBoardId); - const installationHandle = await E(board).getValue( - installationHandleBoardId, - ); - const instance = display(instanceHandle); - const installation = display(installationHandle); + const instanceDisplay = display(instance); + const installationDisplay = display(installation); const alreadyDisplayed = inboxState.has(id) && inboxState.get(id).proposalForDisplay; const offerForDisplay = { - ...offer, - instancePetname: instance.petname, - installationPetname: installation.petname, + ...rest, + proposalTemplate, + instancePetname: instanceDisplay.petname, + installationPetname: installationDisplay.petname, proposalForDisplay: displayProposal(alreadyDisplayed || proposalTemplate), }; @@ -442,8 +439,10 @@ export async function makeWallet({ const addIssuer = async (petnameForBrand, issuerP, makePurse = false) => { const { brand, issuer } = await brandTable.initIssuer(issuerP); - const issuerBoardId = await E(board).getId(issuer); - issuerToBoardId.init(issuer, issuerBoardId); + if (!issuerToBoardId.has(issuer)) { + const issuerBoardId = await E(board).getId(issuer); + issuerToBoardId.init(issuer, issuerBoardId); + } const addBrandPetname = () => { let p; const already = brandMapping.valToPetname.has(brand); @@ -486,8 +485,8 @@ export async function makeWallet({ depositFacet = actions; } else { depositFacet = harden({ - receive(payment) { - return E(actions).receive(payment); + receive(paymentP) { + return E(actions).receive(paymentP); }, }); } @@ -620,6 +619,12 @@ export async function makeWallet({ inviteHandleBoardId, // Keep for backward-compatibility. invitationHandleBoardId = inviteHandleBoardId, } = offer; + + assert.typeof( + invitationHandleBoardId, + 'string', + details`invitationHandleBoardId must be a string`, + ); const { proposal, purseKeywordRecord } = compileProposal( offer.proposalTemplate, ); @@ -643,8 +648,17 @@ export async function makeWallet({ harden([inviteValueElems.find(matchInvite)]), ); const inviteP = E(zoeInvitePurse).withdraw(inviteAmount); + const { installation, instance } = await E(zoe).getInvitationDetails( + inviteP, + ); - return { proposal, inviteP, purseKeywordRecord }; + return { + proposal, + inviteP, + purseKeywordRecord, + installation, + instance, + }; }; /** @type {Store} */ @@ -747,20 +761,8 @@ export async function makeWallet({ async function addOffer(rawOffer, requestContext = {}) { const dappOrigin = requestContext.dappOrigin || requestContext.origin || 'unknown'; - const { - id: rawId, - instanceHandleBoardId, - installationHandleBoardId, - } = rawOffer; + const { id: rawId } = rawOffer; const id = `${dappOrigin}#${rawId}`; - assert( - typeof instanceHandleBoardId === 'string', - details`instanceHandleBoardId must be a string`, - ); - assert( - typeof installationHandleBoardId === 'string', - details`installationHandleBoardId must be a string`, - ); const offer = harden({ ...rawOffer, id, @@ -771,15 +773,35 @@ export async function makeWallet({ updateInboxState(id, offer); // Compile the offer - idToCompiledOfferP.set(id, await compileOffer(offer)); + const compiledOfferP = compileOffer(offer); + idToCompiledOfferP.set(id, compiledOfferP); // Our inbox state may have an enriched offer. updateInboxState(id, idToOffer.get(id)); + const { installation, instance } = await compiledOfferP; + + if (idToOffer.has(id)) { + idToOffer.set( + id, + harden({ + ...idToOffer.get(id), + installation, + instance, + }), + ); + updateInboxState(id, idToOffer.get(id)); + } return id; } function consummated(offer) { - return offer.status !== undefined; + if (offer.status !== undefined) { + return true; + } + if (offer.actions) { + E(offer.actions).handled(offer); + } + return false; } function declineOffer(id) { @@ -824,7 +846,7 @@ export async function makeWallet({ return undefined; } - /** @type {{ outcome?: any, depositedP?: Promise }} */ + /** @type {{ outcome?: any, depositedP?: Promise, outcomeDetails?: any }} */ let ret = {}; let alreadyResolved = false; const rejected = e => { @@ -870,9 +892,15 @@ export async function makeWallet({ // it could be an object. We don't do anything currently if it // is an object, but we will store it here for future use. const outcome = await E(seat).getOfferResult(); - idToOutcome.set(id, outcome); + if (offer.actions) { + E(offer.actions).result(offer, outcome); + } - ret = { outcome, depositedP }; + ret = { + outcome, + depositedP, + outcomeDetails: offer.outcomeDetails, + }; // Update status, drop the proposal depositedP @@ -922,11 +950,12 @@ export async function makeWallet({ }; /** - * @param {Payment} payment + * @param {ERef} paymentP * @param {Purse | Petname=} depositTo */ - const addPayment = async (payment, depositTo = undefined) => { - // We don't even create the record until we get an alleged brand. + const addPayment = async (paymentP, depositTo = undefined) => { + // We don't even create the record until we resolve the payment. + const payment = await paymentP; const brand = await E(payment).getAllegedBrand(); const depositedPK = makePromiseKit(); diff --git a/packages/dapp-svelte-wallet/api/src/observable.js b/packages/dapp-svelte-wallet/api/src/observable.js index 6f6152dd5c6..e49ed9ea633 100644 --- a/packages/dapp-svelte-wallet/api/src/observable.js +++ b/packages/dapp-svelte-wallet/api/src/observable.js @@ -12,7 +12,7 @@ export default function makeObservablePurse(E, purse, onFulfilled) { .then(depositOnlyFacet => { return { receive(...args) { - E(depositOnlyFacet) + return E(depositOnlyFacet) .receive(...args) .then(result => { onFulfilled(); diff --git a/packages/dapp-svelte-wallet/api/src/wallet.js b/packages/dapp-svelte-wallet/api/src/wallet.js index 16e4907f538..8da2fb47fca 100644 --- a/packages/dapp-svelte-wallet/api/src/wallet.js +++ b/packages/dapp-svelte-wallet/api/src/wallet.js @@ -99,9 +99,41 @@ export function buildRootObject(_vatPowers) { }; } case 'walletAddOffer': { + let handled = false; + const actions = harden({ + result(offer, outcome) { + E(http).send( + { + type: 'walletOfferResult', + data: { + id: offer.id, + outcomeDetails: offer.outcomeDetails, + outcome: `${outcome}`, + }, + }, + [meta.channelHandle], + ); + }, + handled(offer) { + if (handled) { + return; + } + handled = true; + E(http).send( + { + type: 'walletOfferHandled', + data: offer.id, + }, + [meta.channelHandle], + ); + }, + }); return { type: 'walletOfferAdded', - data: await wallet.addOffer(data, { ...meta, dappOrigin }), + data: await wallet.addOffer( + { ...data, actions }, + { ...meta, dappOrigin }, + ), }; } case 'walletDeclineOffer': { @@ -217,16 +249,22 @@ export function buildRootObject(_vatPowers) { dappOrigin, ); - const notYetEnabled = () => - E(otherSide) - .needDappApproval(dappOrigin, suggestedDappPetname) - .catch(_ => {}); - const approve = () => - wallet.waitForDappApproval( + const approve = async () => { + let needApproval = false; + await wallet.waitForDappApproval( suggestedDappPetname, dappOrigin, - notYetEnabled, + () => { + needApproval = true; + E(otherSide) + .needDappApproval(dappOrigin, suggestedDappPetname) + .catch(_ => {}); + }, ); + if (needApproval) { + E(otherSide).dappApproved(dappOrigin); + } + }; return harden({ async getPurseNotifier() { @@ -297,27 +335,42 @@ export function buildRootObject(_vatPowers) { const { type, dappOrigin = meta.origin, - suggestedDappPetname = obj.dappOrigin || meta.origin, + suggestedDappPetname = (meta.query && + meta.query.suggestedDappPetname) || + obj.dappOrigin || + meta.origin, } = obj; // When we haven't been enabled, tell our caller. - const notYetEnabled = () => + let needApproval = false; + await wallet.waitForDappApproval( + suggestedDappPetname, + dappOrigin, + () => { + needApproval = true; + E(http).send( + { + type: 'walletNeedDappApproval', + data: { + dappOrigin, + suggestedDappPetname, + }, + }, + [meta.channelHandle], + ); + }, + ); + if (needApproval) { E(http).send( { - type: 'walletNeedDappApproval', + type: 'walletHaveDappApproval', data: { dappOrigin, - suggestedDappPetname, }, }, [meta.channelHandle], ); - - await wallet.waitForDappApproval( - suggestedDappPetname, - dappOrigin, - notYetEnabled, - ); + } switch (type) { case 'walletGetPurses': diff --git a/packages/dapp-svelte-wallet/ui/src/Amount.svelte b/packages/dapp-svelte-wallet/ui/src/Amount.svelte index 1c3117df08f..95cc31e2328 100644 --- a/packages/dapp-svelte-wallet/ui/src/Amount.svelte +++ b/packages/dapp-svelte-wallet/ui/src/Amount.svelte @@ -27,9 +27,9 @@ {#if brand.petname === 'zoe invite'} - {#each value as { instanceHandle: { petname }, inviteDesc }} + {#each value as { instance: { petname }, description }}
instance:
-
inviteDesc: {inviteDesc}
+
description: {description}
{/each} {:else} {value.map(v => JSON.stringify(v)).join(', ')} diff --git a/packages/dapp-svelte-wallet/ui/src/Transaction.svelte b/packages/dapp-svelte-wallet/ui/src/Transaction.svelte index 2d2bc98e4aa..15b993e6caf 100644 --- a/packages/dapp-svelte-wallet/ui/src/Transaction.svelte +++ b/packages/dapp-svelte-wallet/ui/src/Transaction.svelte @@ -17,7 +17,11 @@ if (!obj) { return; } - let { outcome } = obj; + let { outcomeDetails, outcome } = obj; + if (outcomeDetails) { + // They have opted-in to handling the outcome. + return; + } if (typeof outcome !== 'string') { outcome = 'Offer was accepted.'; }