@agoric/zoe
Version:
Zoe: the Smart Contract Framework for Offer Enforcement
104 lines (97 loc) • 3.13 kB
JavaScript
import { M } from '@endo/patterns';
import { prepareRevocableMakerKit } from '@agoric/base-zone/zone-helpers.js';
import { OfferHandlerI } from '../typeGuards.js';
const TransferProposalShape = M.splitRecord({
give: {},
want: {},
exit: {
onDemand: null,
},
});
/**
* @typedef {object} OwnableOptions
* @property {string} [uInterfaceName]
* The `interfaceName` of the underlying interface guard.
* Defaults to the `uKindName`.
*/
/**
* Prepare a kind that wraps an 'ownable' object with a `makeTransferInvitation`
* ability and delegates to the underlying object methods specified in an
* allowlist of method names.
*
* @template {(string | symbol)[]} MN Method names
* @param {import('@agoric/base-zone').Zone} zone
* @param {ZCF['makeInvitation']} makeInvitation
* A function with the same behavior as `zcf.makeInvitation`.
* A contract will normally just extract it from its own zcf using the
* argument expression
* ```js
* (...args) => zcf.makeInvitation(...args)
* ```
* See ownable-counter.js for the canonical example.
* @param {string} uKindName
* The `kindName` of the underlying exo class
* @param {MN} uMethodNames
* The method names of the underlying exo class that should be represented
* by transparently-forwarding methods of the wrapping ownable object.
* @param {OwnableOptions} [options]
* @returns {<U>(underlying: U) => Partial<U> & {makeTransferInvitation: () => Invitation<U>}}
*/
export const prepareOwnable = (
zone,
makeInvitation,
uKindName,
uMethodNames,
options = {},
) => {
const { uInterfaceName = uKindName } = options;
const { revoke, makeRevocable } = prepareRevocableMakerKit(
zone,
uKindName,
uMethodNames,
{
uInterfaceName,
extraMethodGuards: {
makeTransferInvitation: M.call().returns(M.promise()),
},
extraMethods: {
makeTransferInvitation() {
const { underlying } = this.state;
const { revocable } = this.facets;
const customDetails = underlying.getInvitationCustomDetails();
const transferHandler = makeTransferHandler(underlying);
const invitation = makeInvitation(
transferHandler,
'transfer',
customDetails,
TransferProposalShape,
);
revoke(revocable);
return invitation;
},
},
},
);
const makeTransferHandler = zone.exoClass(
'TransferHandler',
OfferHandlerI,
underlying => ({
underlying,
}),
{
handle(seat) {
const { underlying } = this.state;
const revocable = makeRevocable(underlying);
seat.exit();
return revocable;
},
},
);
const makeOwnable = underlying => makeRevocable(underlying);
// Using at-ts-ignore rather than at-ts-expect-error because the vscode
// ts server says it is an error but `yarn lint` says it is not.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore Return type is more precise than revocable supports
return harden(makeOwnable);
};
harden(prepareOwnable);