diff --git a/packages/vats/src/bridge.js b/packages/vats/src/bridge.js index 8937be6376f..1b98a1452d2 100644 --- a/packages/vats/src/bridge.js +++ b/packages/vats/src/bridge.js @@ -21,7 +21,7 @@ import { Far } from '@agoric/marshal'; * @property {(srcId: string, obj: any) => Promise} fromBridge Handle an inbound message * * @typedef {Object} BridgeManager The object to manage this bridge - * @property {(dstID: string, obj: any) => void} toBridge + * @property {(dstID: string, obj: any) => any} toBridge * @property {(srcID: string, handler: BridgeHandler) => void} register * @property {(srcID: string, handler: BridgeHandler) => void} unregister */ diff --git a/packages/vats/src/vat-bank.js b/packages/vats/src/vat-bank.js index 504d0d4ac41..5477e138d74 100644 --- a/packages/vats/src/vat-bank.js +++ b/packages/vats/src/vat-bank.js @@ -15,12 +15,19 @@ import '@agoric/notifier/exported'; * @typedef {ReturnType} VirtualPurse */ +/** + * @callback BalanceUpdater + * @param {any} value + * @param {any} [nonce] + */ + /** * @param {(obj: any) => Promise} bankCall * @param {string} denom * @param {Brand} brand * @param {string} address * @param {Notifier} balanceNotifier + * @param {(obj: any) => boolean} updateBalances * @returns {VirtualPurseController} */ const makePurseController = ( @@ -29,8 +36,9 @@ const makePurseController = ( brand, address, balanceNotifier, -) => - harden({ + updateBalances, +) => { + return harden({ async *getBalances(b) { assert.equal(b, brand); let updateRecord = await balanceNotifier.getUpdateSince(); @@ -45,23 +53,26 @@ const makePurseController = ( }, async pushAmount(amt) { const value = amountMath.getValue(brand, amt); - return bankCall({ + const update = await bankCall({ type: 'VPURSE_GIVE', recipient: address, denom, amount: `${value}`, }); + updateBalances(update); }, async pullAmount(amt) { const value = amountMath.getValue(brand, amt); - return bankCall({ + const update = await bankCall({ type: 'VPURSE_GRAB', sender: address, denom, amount: `${value}`, }); + updateBalances(update); }, }); +}; /** * @typedef {Object} AssetIssuerKit @@ -93,9 +104,31 @@ export function buildRootObject(_vatPowers) { /** @type {WeakStore} */ const brandToAssetRecord = makeWeakStore('brand'); - /** @type {Store void>>} */ + /** @type {Store>} */ const denomToAddressUpdater = makeStore('denom'); + const updateBalances = obj => { + switch (obj && obj.type) { + case 'VPURSE_BALANCE_UPDATE': { + for (const update of obj.updated) { + try { + const { address, denom, amount: value } = update; + const addressToUpdater = denomToAddressUpdater.get(denom); + const updater = addressToUpdater.get(address); + + updater(value, obj.nonce); + console.error('Successful update', update); + } catch (e) { + console.error('Unregistered update', update); + } + } + return true; + } + default: + return false; + } + }; + /** * @param {import('./bridge').BridgeManager} bankBridgeMgr */ @@ -103,23 +136,8 @@ export function buildRootObject(_vatPowers) { // We need to synchronise with the remote bank. const handler = Far('bankHandler', { async fromBridge(_srcID, obj) { - switch (obj.type) { - case 'VPURSE_BALANCE_UPDATE': { - for (const update of obj.updated) { - try { - const { address, denom, amount: value } = update; - const addressToUpdater = denomToAddressUpdater.get(denom); - const updater = addressToUpdater.get(address); - - updater(value); - } catch (e) { - console.error('Unregistered update', update); - } - } - break; - } - default: - assert.fail(X`Unrecognized request ${obj.type}`); + if (!updateBalances(obj)) { + assert.fail(X`Unrecognized request ${obj && obj.type}`); } }, }); @@ -233,9 +251,19 @@ export function buildRootObject(_vatPowers) { assetRecord.denom, ); - /** @typedef {NotifierRecord} */ + /** @type {NotifierRecord} */ const { updater, notifier } = makeNotifierKit(); - const balanceUpdater = value => { + /** @type {bigint} */ + let lastBalanceUpdate = -1n; + /** @type {BalanceUpdater} */ + const balanceUpdater = (value, nonce = undefined) => { + if (nonce !== undefined) { + const thisBalanceUpdate = BigInt(nonce); + if (thisBalanceUpdate <= lastBalanceUpdate) { + return; + } + lastBalanceUpdate = thisBalanceUpdate; + } // Convert the string value to a bigint. const amt = amountMath.make(brand, BigInt(value)); updater.updateState(amt); @@ -257,6 +285,7 @@ export function buildRootObject(_vatPowers) { brand, address, notifier, + updateBalances, ); const vpurse = makeVirtualPurse(vpc, assetRecord); brandToVPurse.init(brand, vpurse);