Skip to content

Commit

Permalink
feat(ertp): purse amountStore abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Feb 5, 2024
1 parent f30b379 commit d26dce7
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 41 deletions.
33 changes: 33 additions & 0 deletions packages/ERTP/src/amountStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { AmountMath } from './amountMath.js';

/**
* @template {AssetKind} [K=AssetKind]
* @typedef {object} AmountStore
* @property {() => Amount<K>} getAmount
* @property {(delta: Amount<K>) => void} increment
* @property {(delta: Amount<K>) => boolean} decrement
*/

/**
* @template {AssetKind} [K=AssetKind]
* @param {object} state
* @param {string} key
* @returns {AmountStore<K>}
*/
export const makeAmountStore = (state, key) => {
/** @type {AmountStore<K>} */
return harden({
getAmount: () => state[key],
increment: delta => {
state[key] = AmountMath.add(state[key], delta);
},
decrement: delta => {
if (AmountMath.isGTE(state[key], delta)) {
state[key] = AmountMath.subtract(state[key], delta);
return true;
}
return false;
},
});
};
harden(makeAmountStore);
34 changes: 7 additions & 27 deletions packages/ERTP/src/paymentLedger.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,6 @@ export const preparePaymentLedger = (
}
};

/** @type {(left: Amount, right: Amount) => Amount} */
const add = (left, right) => AmountMath.add(left, right, brand);
/** @type {(left: Amount, right: Amount) => Amount} */
const subtract = (left, right) => AmountMath.subtract(left, right, brand);
/** @type {(allegedAmount: Amount) => Amount} */
const coerce = allegedAmount => AmountMath.coerce(brand, allegedAmount);
/** @type {(left: Amount, right: Amount) => boolean} */
Expand Down Expand Up @@ -239,17 +235,13 @@ export const preparePaymentLedger = (
/**
* Used by the purse code to implement purse.deposit
*
* @param {Amount} currentBalance - the current balance of the purse before a
* deposit
* @param {(newPurseBalance: Amount) => void} updatePurseBalance - commit the
* purse balance
* @param {import('./amountStore.js').AmountStore} balanceStore
* @param {Payment} srcPayment
* @param {Pattern} [optAmountShape]
* @returns {Amount}
*/
const depositInternal = (
currentBalance,
updatePurseBalance,
balanceStore,
srcPayment,
optAmountShape = undefined,
) => {
Expand All @@ -261,13 +253,12 @@ export const preparePaymentLedger = (
assertLivePayment(srcPayment);
const srcPaymentBalance = paymentLedger.get(srcPayment);
assertAmountConsistent(srcPaymentBalance, optAmountShape);
const newPurseBalance = add(srcPaymentBalance, currentBalance);
try {
// COMMIT POINT
// Move the assets in `srcPayment` into this purse, using up the
// source payment, such that total assets are conserved.
deletePayment(srcPayment);
updatePurseBalance(newPurseBalance);
balanceStore.increment(srcPaymentBalance);
} catch (err) {
shutdownLedgerWithFailure(err);
throw err;
Expand All @@ -278,30 +269,19 @@ export const preparePaymentLedger = (
/**
* Used by the purse code to implement purse.withdraw
*
* @param {Amount} currentBalance - the current balance of the purse before a
* withdrawal
* @param {(newPurseBalance: Amount) => void} updatePurseBalance - commit the
* purse balance
* @param {import('./amountStore.js').AmountStore} balanceStore
* @param {Amount} amount - the amount to be withdrawn
* @param {SetStore<Payment>} recoverySet
* @returns {Payment}
*/
const withdrawInternal = (
currentBalance,
updatePurseBalance,
amount,
recoverySet,
) => {
const withdrawInternal = (balanceStore, amount, recoverySet) => {
amount = coerce(amount);
AmountMath.isGTE(currentBalance, amount) ||
Fail`Withdrawal of ${amount} failed because the purse only contained ${currentBalance}`;
const newPurseBalance = subtract(currentBalance, amount);

const payment = makePayment();
try {
// COMMIT POINT Move the withdrawn assets from this purse into
// payment. Total assets must remain conserved.
updatePurseBalance(newPurseBalance);
balanceStore.decrement(amount) ||
Fail`Withdrawal of ${amount} failed because the purse only contained ${balanceStore.getAmount()}`;
initPayment(payment, amount, recoverySet);
} catch (err) {
shutdownLedgerWithFailure(err);
Expand Down
30 changes: 16 additions & 14 deletions packages/ERTP/src/purse.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { M } from '@agoric/store';
import { prepareExoClassKit, makeScalarBigSetStore } from '@agoric/vat-data';
import { AmountMath } from './amountMath.js';
import { makeTransientNotifierKit } from './transientNotifier.js';
import { makeAmountStore } from './amountStore.js';

// TODO `InterfaceGuard` type parameter
/** @typedef {import('@endo/patterns').InterfaceGuard} InterfaceGuard */
Expand Down Expand Up @@ -37,11 +38,6 @@ export const preparePurseKind = (
// broken across an upgrade.
const { provideNotifier, update: updateBalance } = makeTransientNotifierKit();

const updatePurseBalance = (state, newPurseBalance, purse) => {
state.currentBalance = newPurseBalance;
updateBalance(purse, purse.getCurrentAmount());
};

// - This kind is a pair of purse and depositFacet that have a 1:1
// correspondence.
// - They are virtualized together to share a single state record.
Expand Down Expand Up @@ -72,28 +68,34 @@ export const preparePurseKind = (
// PurseI does *not* delay `deposit` until `srcPayment` is fulfulled.
// See the comments on PurseI.deposit in typeGuards.js
const { state } = this;
const { purse } = this.facets;
const balanceStore = makeAmountStore(state, 'currentBalance');
// Note COMMIT POINT within deposit.
return depositInternal(
state.currentBalance,
newPurseBalance =>
updatePurseBalance(state, newPurseBalance, this.facets.purse),
const srcPaymentBalance = depositInternal(
balanceStore,
srcPayment,
optAmountShape,
);
updateBalance(purse, balanceStore.getAmount());
return srcPaymentBalance;
},
withdraw(amount) {
const { state } = this;
const { purse } = this.facets;
const balanceStore = makeAmountStore(state, 'currentBalance');
// Note COMMIT POINT within withdraw.
return withdrawInternal(
state.currentBalance,
newPurseBalance =>
updatePurseBalance(state, newPurseBalance, this.facets.purse),
const payment = withdrawInternal(
balanceStore,
amount,
state.recoverySet,
);
updateBalance(purse, balanceStore.getAmount());
return payment;
},
getCurrentAmount() {
return this.state.currentBalance;
const { state } = this;
const balanceStore = makeAmountStore(state, 'currentBalance');
return balanceStore.getAmount();
},
getCurrentAmountNotifier() {
return provideNotifier(this.facets.purse);
Expand Down

0 comments on commit d26dce7

Please sign in to comment.