Skip to content

Commit

Permalink
chore: cleanups and refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris-Hibbert committed Jun 30, 2022
1 parent 57970ce commit 4d5a57e
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 48 deletions.
3 changes: 0 additions & 3 deletions packages/vat-data/src/kind-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ export const ProvideFar = (baggage, kindName, methods, options = undefined) => {
options,
);

// CTH: I think this should associate a new makeSingleton (with new behavior)
// with the existing service.

return provide(baggage, `the_${kindName}`, () => makeSingleton());
};
// @ts-expect-error TODO statically recognize harden
Expand Down
5 changes: 2 additions & 3 deletions packages/zoe/src/contractFacet/vatRoot.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@ export function buildRootObject(powers, vatParameters, baggage) {
contractBundleCap,
baggage,
);
// snapshot zygote here //////////////////

return Far('executeContract', {
return Far('contractRunner', {
// initialize instance-specific state of the contract (1st time)
startContract: (
zoeInstanceAdmin, // in 1st message post-clone
Expand All @@ -64,6 +63,7 @@ export function buildRootObject(powers, vatParameters, baggage) {
// upgrade might supplement this
) => {
assert(!didStart);
// bikeshed: the outer and inner messages shouldn't both be startContract
/** @type {ZCFZygote} */
return E(zcfZygote).startContract(
zoeInstanceAdmin,
Expand All @@ -87,7 +87,6 @@ export function buildRootObject(powers, vatParameters, baggage) {
zoeInstanceAdmin,
instanceRecordFromZoe,
issuerStorageFromZoe,
baggage,
privateArgs,
);
},
Expand Down
81 changes: 42 additions & 39 deletions packages/zoe/src/contractFacet/zcfZygote.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const { details: X, makeAssert } = assert;
* @param {ERef<ZoeService>} zoeService
* @param {Issuer} invitationIssuer
* @param {TestJigSetter} testJigSetter
* @param contractBundleCap
* @param {BundleCap} contractBundleCap
* @param {import('@agoric/vat-data').Baggage} zcfBaggage
* @returns {Promise<ZCFZygote>}
*/
Expand Down Expand Up @@ -273,20 +273,43 @@ export const makeZCFZygote = async (
}
return evalContractBundle(bundle);
};
// evaluate the contract (either the first version, or an upgrade)
// evaluate the contract (either the first version, or an upgrade). start is
// from a non-upgradeable contract, setupInstallation for upgradeable ones.
const { setupInstallation, start } = await evaluateContract();

const contractBaggage = provideDurableMapStore(zcfBaggage, 'contractBaggage');

// setup instance for either first or later upgradeable contract
const doSetupInstance = async setupInstance => {
// maybe do some defineDurableKinds here
// const { zcfThing1, zcfThing2 } = provideZcfThings(baggage, perInstanceZoeStuff);
// const zcfThing1 = makeZcfThing1(perInstanceZoeStuff);
// const zcfThing2 = makeZcfThing2(perInstanceZoeStuff);

return setupInstance();
};

// start a non-upgradeable contract
const startClassicContract = privateArgs => {
/** @type {Promise<ExecuteClassicContractResult>} */
const result = E.when(
start(zcf, privateArgs),
({
creatorFacet = Far('emptyCreatorFacet', {}),
publicFacet = Far('emptyPublicFacet', {}),
creatorInvitation = undefined,
}) => {
return harden({
creatorFacet,
publicFacet,
creatorInvitation,
handleOfferObj,
});
},
);
handlePWarning(result);
return result;
};

/**
* A zygote is a pre-image of a vat that can quickly be instantiated because
* the code has already been evaluated. SwingSet doesn't support zygotes yet.
Expand Down Expand Up @@ -316,58 +339,38 @@ export const makeZCFZygote = async (

// If it's a non-upgradeable contract, start it now.
if (!setupInstallation) {
// fall back to old non-upgradable scheme
// Next, execute the contract code, passing in zcf
/** @type {Promise<ExecuteContractResult>} */
const result = E.when(
start(zcf, privateArgs),
({
creatorFacet = Far('emptyCreatorFacet', {}),
publicFacet = Far('emptyPublicFacet', {}),
creatorInvitation = undefined,
}) => {
return harden({
creatorFacet,
publicFacet,
creatorInvitation,
handleOfferObj,
});
},
);
handlePWarning(result);
return result;
return startClassicContract(privateArgs);
}

// setupInstallation is a fn returned by the contract that sets up
// installation-specific state, and returns setupInstance. setupInstance
// does instane-specific setup and returns makeInstanceKit.
E.when(
// installation-specific state and defines kinds, then returns
// setupInstance. setupInstance does instance-specific setup and returns
// makeInstanceKit.
/** @type {Promise<ExecuteUpgradeableContractResult>} */
const result = E.when(
setupInstallation(contractBaggage, zcf, privateArgs),
async setupInstance => {
// allow the contract (any version) to set up its installation Kinds
// const zcfHandle1 = provideHandle(baggage, 'zcfHandle1', 'zcfIface1');
// const zcfHandle2 = provideHandle(baggage, 'zcfHandle2', 'zcfIface2');
// const makeZcfThing1 = defineDurableKind(zcfHandle1, undefined, {}); // makeZcfSeat
// const makeZcfThing2 = defineDurableKind(zcfHandle2, undefined, {});

// the version-1 *instance* will see this zcRoot.start() get
// invoked (in a clone of the original vat) exactly once per
// instance

// now that our clone is differentiated, we can do
// instance-specific setup
// instance-specific setup and get back the contract runner
const makeInstanceKit = await doSetupInstance(setupInstance);
// and we can invoke makeInstanceKit() for the first and only time
const { publicFacet, privateFacet } = makeInstanceKit();
return harden({ publicFacet, privateFacet });

// snapshot zygote here //////////////////

// and invoke makeInstanceKit() for the first and only time
const { publicFacet, creatorFacet } = makeInstanceKit();
return harden({ publicFacet, creatorFacet, handleOfferObj });
},
);

const zcfRoot = Far('zcfRoot', { start2 });
return zcfRoot;
return result;
},
// @ts-expect-error TODO: add to types
restartContract: async () => {
// code like startContract
// add code here like startContract

// For version-2 or later, we know we've already been started, so
// allow the contract to set up its instance Kinds
Expand Down
133 changes: 133 additions & 0 deletions packages/zoe/src/contracts/coveredCallV2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// @ts-check

import { assert } from '@agoric/assert';
import { M, fit } from '@agoric/store';
import '../../exported.js';

// Eventually will be importable from '@agoric/zoe-contract-support'
import { provideDurableMapStore } from '@agoric/vat-data/src';
import { swapExact } from '../contractSupport/index.js';
import { isAfterDeadlineExitRule } from '../typeGuards.js';

/**
* A call option is the right (but not the obligation) to buy digital
* assets at a pre-determined price, called the strike price. This
* call option is "covered," meaning that the owner of the digital
* assets must put the assets in escrow. This guarantees that the
* assets can be transferred without relying on the owner of the
* digital assets to keep their promise.
*
* https://agoric.com/documentation/zoe/guide/contracts/covered-call.html
*
* The call option has an expiration date, when the opportunity is
* cancelled. The owner of the digital assets cannot remove the assets
* from escrow before the expiration date.
*
* The `creatorInvitation` of this contract is an invitation to escrow
* the underlying assets. The proposal to escrow assets can have any
* `give` and `want` with any keywords. Any number of assets of
* different brands can be escrowed under different keywords. The
* proposal must have an exit record with the key "afterDeadline":
* {
* give: { ... },
* want: { ... },
* exit: {
* afterDeadline: { deadline: time, timer: myTimer }
* },
* }
*
* This deadline serves as the expiration date for the covered call
* option. After this deadline, if the option has not been exercised,
* the underlying assets are automatically paid out to the creator of
* the contract as a refund.
*
* After the owner of the digital assets escrows the assets in the
* initial offer, they receive a seat. The payout for this seat will
* either be a refund of the underlying assets (as mentioned above) or
* payments in the amount of the strike price. Zoe's enforcement of
* offer safety guarantees that the payout is either a refund or
* payments in the amount of the strike price, regardless of whether
* this contract is buggy.
*
* The offerResult of this initial seat resolves to the call option
* itself: an inspectable invitation to buy the underlying assets. The
* call option invitation has this additional information in the
* value: {expirationDate, timeAuthority, underlyingAssets,
* strikePrice }
*
* The invitation itself can be traded as a valuable digital asset: a
* covered call option.
*
* The recipient of a covered call option (buying it on some other
* exchange or through some other trading contract) can exercise the
* option before the deadline by using the option as an invitation to
* this contract, paying the strike price and receiving the underlying
* assets. The recipient of a covered call option can use whatever
* keywords they wish, as long as they give the strike price as
* specified in the invitation value, and want the underlying assets
* exactly.
*
* @param installationBaggage
* @param {ZCF} zcf
*/
const setupInstallation = (installationBaggage, zcf) => {
const sellSeatExpiredMsg = `The covered call option is expired.`;

const instanceBaggage = provideDurableMapStore(
installationBaggage,
'instance',
);

/** @type {OfferHandler} */
const makeOption = sellSeat => {
fit(sellSeat.getProposal(), M.split({ exit: { afterDeadline: M.any() } }));
const sellSeatExitRule = sellSeat.getProposal().exit;
assert(
isAfterDeadlineExitRule(sellSeatExitRule),
`the seller must have an afterDeadline exitRule, but instead had ${sellSeatExitRule}`,
);

const exerciseOption = buySeat => {
assert(!sellSeat.hasExited(), sellSeatExpiredMsg);
try {
swapExact(zcf, sellSeat, buySeat);
} catch (err) {
console.log(
'Swap failed. Please make sure your offer has the same underlyingAssets and strikePrice as specified in the invitation details. The keywords should not matter.',
);
throw err;
}
zcf.shutdown('Swap completed.');
return `The option was exercised. Please collect the assets in your payout.`;
};

const customProps = harden({
expirationDate: sellSeatExitRule.afterDeadline.deadline,
timeAuthority: sellSeatExitRule.afterDeadline.timer,
underlyingAssets: sellSeat.getProposal().give,
strikePrice: sellSeat.getProposal().want,
});
return zcf.makeInvitation(exerciseOption, 'exerciseOption', customProps);
};

const setupInstance = () => {
// define instance kinds

const makeInstanceKit = () => {
// the creatorFacet could be made durable for this demonstration, but if
// it's not initiated before upgrade, there's no state to lose.
const creatorFacet = {
makeInvitation: () => zcf.makeInvitation(makeOption, 'makeCallOption'),
};
return harden({ creatorFacet });
};
return harden({ makeInstanceKit });
};

// redefine kinds.

return harden({ setupInstance });
};

harden(setupInstallation);
export { setupInstallation };
17 changes: 15 additions & 2 deletions packages/zoe/src/internal-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,27 @@
/**
* @typedef {object} ZCFRoot
* @property {StartContract} startContract
*
* @typedef {object} ExecuteContractResult
*/

/**
* @typedef {object} ExecuteClassicContractResult
* @property {object} creatorFacet
* @property {Promise<Invitation>} creatorInvitation
* @property {object} publicFacet
* @property {HandleOfferObj} handleOfferObj
*/

/**
* @typedef {object} ExecuteUpgradeableContractResult
* @property {object} creatorFacet
* @property {object} publicFacet
* @property {HandleOfferObj} handleOfferObj
*/

/**
* @typedef {ExecuteClassicContractResult|ExecuteUpgradeableContractResult} ExecuteContractResult
*/

/**
* @callback StartContract
* @param {ERef<ZoeInstanceAdmin>} zoeInstanceAdmin
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15296,7 +15296,7 @@ rollup@^2.43.1, rollup@^2.58.0:
optionalDependencies:
fsevents "~2.3.2"

"rollup@github:endojs/endo#rollup-2.7.1-patch-1":
rollup@endojs/endo#rollup-2.7.1-patch-1:
version "2.70.1-endo.1"
resolved "https://codeload.github.com/endojs/endo/tar.gz/54060e784a4dbe77b6692f17344f4d84a198530d"
optionalDependencies:
Expand Down

0 comments on commit 4d5a57e

Please sign in to comment.