Skip to content

Commit

Permalink
feat(zoe): first transferable
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Nov 7, 2023
1 parent 143a08c commit 70ed294
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 0 deletions.
94 changes: 94 additions & 0 deletions packages/zoe/src/contractSupport/transferableKit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { M } from '@endo/patterns';
import { prepareExo } from '@agoric/vat-data';
import { OfferHandlerI } from '../typeGuards.js';

/** @typedef {import('@agoric/vat-data').Baggage} Baggage */

const { apply } = Reflect;

const TransferProposalShape = harden({
give: {},
want: {},
exit: {
onDemand: {},
},
});

const defaultGetSelfFromThis = {
getSelfFromThis() {
const { self } = this;
return self;
},
}.getSelfFromThis;

export const makeTransferablesKit = (
zcf,
baggage,
detailsShape,
makeUseObject,
getSelfFromThis = defaultGetSelfFromThis,
) => {
const UseObjectMethodGuards = harden({
incr: M.call().returns(M.bigint()),
getCustomDetails: M.call().returns(detailsShape),
makeTransferInvitation: M.call().returns(M.promise()),
});

let revokeTransferHandler;

const transferHandler = prepareExo(
baggage,
'TransferHandler',
OfferHandlerI,
{
handle(seat) {
// @ts-expect-error Usual self-typing problem
const { self } = this;
// TODO implement *Seat.getDetails()
const { customDetails } = seat.getDetails();
seat.exit();
revokeTransferHandler(self);
return makeUseObject(customDetails);
},
},
{
receiveRevoker(revoke) {
revokeTransferHandler = revoke;
},
},
);

let revokeUseObject;

const useObjectMethods = harden({
makeTransferInvitation() {
const self = apply(getSelfFromThis, this, []);
const invitation = zcf.makeInvitation(
// eslint-disable-next-line no-use-before-define
transferHandler,
'transfer',
self.getCustomDetails(),
TransferProposalShape,
);
revokeUseObject(self);
return invitation;
},
});

const useObjectOptions = harden({
receiveRevoker(revoke) {
revokeUseObject = revoke;
},
});

return harden({
// note: includes getCustomDetails
UseObjectMethodGuards,
// note: does not include getCustomDetails,
// so getCustomDetails is effectively an abstract method that must be
// concretely implemented.
useObjectMethods,
useObjectOptions,
});
};
harden(makeTransferablesKit);
89 changes: 89 additions & 0 deletions packages/zoe/src/contracts/transferable-counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { M } from '@endo/patterns';
import { prepareExo, prepareExoClass } from '@agoric/vat-data';
import { makeTransferablesKit } from '../contractSupport/transferableKit.js';

/** @typedef {import('@agoric/vat-data').Baggage} Baggage */

const CounterDetailsShape = harden({
count: M.bigint(),
});

/**
* @param {ZCF} zcf
* @param {{ count: bigint}} privateArgs
* @param {Baggage} instanceBaggage
*/
export const start = async (zcf, privateArgs, instanceBaggage) => {
const { count: startCount = 0n } = privateArgs;
assert.typeof(startCount, 'bigint');

// for use by upgraded versions.
const firstTime = !instanceBaggage.has('count');
if (firstTime) {
instanceBaggage.init('count', startCount);
}

const makeForwardUseCounter = customDetails =>
// eslint-disable-next-line no-use-before-define
makeUseCounter(customDetails);

const { UseObjectMethodGuards, useObjectMethods, useObjectOptions } =
makeTransferablesKit(
zcf,
instanceBaggage,
CounterDetailsShape,
makeForwardUseCounter,
);

const UseCounterI = M.interface('UseCounter', {
...UseObjectMethodGuards,
incr: M.call().returns(M.bigint()),
});

const makeUseCounter = prepareExoClass(
instanceBaggage,
'UseCounter',
UseCounterI,
customDetails => {
const { count } = customDetails;
assert(count === instanceBaggage.get('count'));
return harden({});
},
{
...useObjectMethods,

incr() {
const count = instanceBaggage.get('count') + 1n;
instanceBaggage.set('count', count);
return count;
},

// note: abstract method must be concretely implemented
getCustomDetails() {
return harden({
count: instanceBaggage.get('count'),
});
},
},

{
...useObjectOptions,
},
);

const ViewCounterI = M.interface('ViewCounter', {
view: M.call().returns(M.bigint()),
});

const viewCounter = prepareExo(instanceBaggage, 'ViewCounter', ViewCounterI, {
view() {
return instanceBaggage.get('count');
},
});

return harden({
creatorFacet: makeUseCounter(startCount),
publicFacet: viewCounter,
});
};
harden(start);

0 comments on commit 70ed294

Please sign in to comment.