-
Notifications
You must be signed in to change notification settings - Fork 207
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
57970ce
commit 4d5a57e
Showing
6 changed files
with
193 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters