Skip to content

Commit

Permalink
feat(zoe): first ownable
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Nov 9, 2023
1 parent 7573b2e commit 79d68dd
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
94 changes: 94 additions & 0 deletions packages/zoe/src/contractSupport/ownableKit.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 makeOwnableKit = (
zcf,
baggage,
detailsShape,
makeOwnableObject,
getSelfFromThis = defaultGetSelfFromThis,
) => {
const OwnableObjectMethodGuards = 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 makeOwnableObject(customDetails);
},
},
{
receiveRevoker(revoke) {
revokeTransferHandler = revoke;
},
},
);

let revokeOwnableObject;

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

const ownableObjectOptions = harden({
receiveRevoker(revoke) {
revokeOwnableObject = revoke;
},
});

return harden({
// note: includes getCustomDetails
OwnableObjectMethodGuards,
// note: does not include getCustomDetails,
// so getCustomDetails is effectively an abstract method that must be
// concretely implemented.
ownableObjectMethods,
ownableObjectOptions,
});
};
harden(makeOwnableKit);
92 changes: 92 additions & 0 deletions packages/zoe/src/contracts/ownable-counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { M } from '@endo/patterns';
import { prepareExo, prepareExoClass } from '@agoric/vat-data';
import { makeOwnableKit } from '../contractSupport/ownableKit.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 makeForwardOwnableCounter = customDetails =>
// eslint-disable-next-line no-use-before-define
makeOwnableCounter(customDetails);

const {
OwnableObjectMethodGuards,
ownableObjectMethods,
ownableObjectOptions,
} = makeOwnableKit(
zcf,
instanceBaggage,
CounterDetailsShape,
makeForwardOwnableCounter,
);

const OwnableCounterI = M.interface('OwnableCounter', {
...OwnableObjectMethodGuards,
incr: M.call().returns(M.bigint()),
});

const makeOwnableCounter = prepareExoClass(
instanceBaggage,
'OwnableCounter',
OwnableCounterI,
customDetails => {
const { count } = customDetails;
assert(count === instanceBaggage.get('count'));
return harden({});
},
{
...ownableObjectMethods,

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'),
});
},
},

{
...ownableObjectOptions,
},
);

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: makeOwnableCounter(startCount),
publicFacet: viewCounter,
});
};
harden(start);

0 comments on commit 79d68dd

Please sign in to comment.