@agoric/zoe
Version:
Zoe: the Smart Contract Framework for Offer Enforcement
308 lines (305 loc) • 11.3 kB
text/typescript
import type {
AnyAmount,
AssetKind,
DisplayInfo,
Issuer,
NatValue,
Payment,
} from '@agoric/ertp';
import type { Subscriber } from '@agoric/notifier';
import type { ERef, EReturn } from '@endo/eventual-send';
import type { Bundle, BundleID } from '@agoric/swingset-vat';
import type { ContractStartFunction, StartParams } from './utils.js';
import type {
Keyword,
InvitationHandle,
BrandKeywordRecord,
Handle,
IssuerKeywordRecord,
} from '../types.js';
import type { Allocation } from '../types-index.js';
/**
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/is-any.d.ts}
*/
type IsAny<T> = 0 extends 1 & NoInfer<T> ? true : false;
/**
* Zoe provides a framework for deploying and working with smart
* contracts. It is accessed as a long-lived and well-trusted service
* that enforces offer safety for the contracts that use it. Zoe has a
* single `invitationIssuer` for the entirety of its lifetime. By
* having a reference to Zoe, a user can get the `invitationIssuer`
* and thus validate any `invitation` they receive from someone else.
*
* Zoe has two different facets: the public Zoe service and the
* contract facet (ZCF). Each contract instance has a copy of ZCF
* within its vat. The contract and ZCF never have direct access to
* the users' payments or the Zoe purses.
*/
export type ZoeService = {
/**
* Zoe has a single `invitationIssuer` for the entirety of its
* lifetime. By having a reference to Zoe, a user can get the
* `invitationIssuer` and thus validate any `invitation` they receive
* from someone else. The mint associated with the invitationIssuer
* creates the ERTP payments that represent the right to interact with
* a smart contract in particular ways.
*/
getInvitationIssuer: GetInvitationIssuer;
install: InstallBundle;
installBundleID: InstallBundleID;
startInstance: import('./utils.js').StartInstance;
offer: Offer;
getPublicFacet: <I extends Instance>(
instance: ERef<I>,
) => Promise<
IsAny<I> extends true
? any
: I extends import('./utils.js').Instance<
infer SF extends ContractStartFunction
>
? IsAny<SF> extends true
? unknown
: EReturn<SF>['publicFacet']
: never
>;
getIssuers: GetIssuers;
getBrands: GetBrands;
getTerms: <I extends ERef<Instance>>(
instance: I,
) => IsAny<I> extends true
? Promise<any>
: I extends ERef<
import('./utils.js').Instance<infer SF extends ContractStartFunction>
>
? IsAny<SF> extends true
? Promise<unknown>
: Promise<StartParams<SF>['terms']>
: never;
getOfferFilter: (instance: ERef<Instance>) => Promise<string[]>;
getInstallationForInstance: GetInstallationForInstance;
getInstance: GetInstance;
getInstallation: GetInstallation;
/**
* Return an object with the instance, installation, description, invitation
* handle, and any custom properties specific to the contract.
*/
getInvitationDetails: GetInvitationDetails;
getFeeIssuer: GetFeeIssuer;
getConfiguration: GetConfiguration;
getBundleIDFromInstallation: GetBundleIDFromInstallation;
/**
* Return the pattern (if any) associated with the invitationHandle that a
* proposal is required to match to be accepted by zoe.offer().
*/
getProposalShapeForInvitation: (
invitationHandle: InvitationHandle,
) => import('@endo/patterns').Pattern | undefined;
};
type GetInvitationIssuer = () => Promise<Issuer<'set', InvitationDetails>>;
type GetFeeIssuer = () => Promise<Issuer<'nat'>>;
type GetConfiguration = () => {
feeIssuerConfig: FeeIssuerConfig;
};
export type GetIssuers = (
instance: import('./utils.js').Instance<any>,
) => Promise<IssuerKeywordRecord>;
export type GetBrands = (
instance: import('./utils.js').Instance<any>,
) => Promise<BrandKeywordRecord>;
type GetInstallationForInstance = (
instance: import('./utils.js').Instance<any>,
) => Promise<Installation>;
export type GetInstance = (
invitation: ERef<import('../types-index.js').Invitation>,
) => Promise<import('./utils.js').Instance<any>>;
export type GetInstallation = (
invitation: ERef<import('../types-index.js').Invitation>,
) => Promise<Installation>;
export type GetInvitationDetails = (
invitation: ERef<import('../types-index.js').Invitation<any, any>>,
) => Promise<InvitationDetails>;
/**
* Create an installation by safely evaluating the code and
* registering it with Zoe. Returns an installation.
*/
export type InstallBundle = (
bundle: Bundle | SourceBundle,
bundleLabel?: string | undefined,
) => Promise<Installation>;
/**
* Create an installation from a Bundle ID. Returns an installation.
*/
export type InstallBundleID = (
bundleID: BundleID,
bundleLabel?: string | undefined,
) => Promise<Installation>;
/**
* Verify that an alleged Installation is real, and return the Bundle ID it
* will use for contract code.
*/
export type GetBundleIDFromInstallation = (
allegedInstallation: ERef<Installation>,
) => Promise<string>;
/**
* To redeem an invitation, the user normally provides a proposal (their
* rules for the offer) as well as payments to be escrowed by Zoe. If
* either the proposal or payments would be empty, indicate this by
* omitting that argument or passing undefined, rather than passing an
* empty record.
*
* The proposal has three parts: `want` and `give` are used by Zoe to
* enforce offer safety, and `exit` is used to specify the particular
* payout-liveness policy that Zoe can guarantee. `want` and `give`
* are objects with keywords as keys and amounts as values.
* `paymentKeywordRecord` is a record with keywords as keys, and the
* values are the actual payments to be escrowed. A payment is
* expected for every rule under `give`.
*/
export type Offer = <Result, Args = undefined>(
invitation: ERef<import('../types-index.js').Invitation<Result, Args>>,
proposal?: Proposal,
paymentKeywordRecord?: PaymentPKeywordRecord,
offerArgs?: Args,
) => Promise<UserSeat<Result>>;
/**
* Zoe uses seats to access or manipulate offers. They let contracts and users
* interact with them. Zoe has two kinds of seats. ZCFSeats are used within
* contracts and with zcf methods. UserSeats represent offers external to Zoe
* and the contract. The party who exercises an invitation and sends the offer()
* message to Zoe gets a UserSeat that can check payouts' status or retrieve the
* result of processing the offer in the contract. This varies, but examples are
* a string and an invitation for another seat.
*
* Also, a UserSeat can be handed to an agent outside Zoe and the contract,
* letting them query or monitor the current state, access the payouts and
* result, and, if it's allowed for this seat, call tryExit().
*
* Since anyone can attempt to exit the seat if they have a reference to it, you
* should only share a UserSeat with trusted parties.
*
* UserSeat includes queries for the associated offer's current state and an
* operation to request that the offer exit, as follows:
*/
export type UserSeat<OR = unknown> = {
getProposal: () => Promise<ProposalRecord>;
/**
* returns a promise for a KeywordPaymentRecord containing all the payouts from
* this seat. The promise will resolve after the seat has exited.
*/
getPayouts: () => Promise<PaymentPKeywordRecord>;
/**
* returns a promise for the Payment corresponding to the indicated keyword.
* The promise will resolve after the seat has exited. If there is no payment
* corresponding to the keyword, an error will be thrown. (It used to return
* undefined.)
*/
getPayout: (keyword: Keyword) => Promise<Payment<any, any>>;
getOfferResult: () => Promise<OR>;
/**
* Note: Only works if the seat's `proposal` has an `OnDemand` `exit` clause. Zoe's
* offer-safety guarantee applies no matter how a seat's interaction with a
* contract ends. Under normal circumstances, the participant might be able to
* call `tryExit()`, or the contract might do something explicitly. On exiting,
* the seat holder gets its current `allocation` and the `seat` can no longer
* interact with the contract.
*/
tryExit?: (() => void) | undefined;
/**
* Returns true if the seat has exited, false if it is still active.
*/
hasExited: () => Promise<boolean>;
/**
* returns 1 if the proposal's
* want clause was satisfied by the final allocation, otherwise 0. This is
* numeric to support a planned enhancement called "multiples" which will allow
* the return value to be any non-negative number. The promise will resolve
* after the seat has exited.
*/
numWantsSatisfied: () => Promise<0 | 1>;
/**
* return a promise for the final allocation. The promise will resolve after the
* seat has exited.
*/
getFinalAllocation: () => Promise<Allocation>;
/**
* returns a subscriber that
* will be notified when the seat has exited or failed.
*/
getExitSubscriber: () => Subscriber<import('../types-index.js').Completion>;
};
export type Proposal = Partial<ProposalRecord>;
export type ProposalRecord = {
give: AmountKeywordRecord;
want: AmountKeywordRecord;
exit: ExitRule;
};
/**
* The keys are keywords, and the values are amounts. For example:
* { Asset: AmountMath.make(assetBrand, 5n), Price:
* AmountMath.make(priceBrand, 9n) }
*/
export type AmountKeywordRecord = Record<Keyword, AnyAmount>;
export type Waker = {
wake: () => void;
};
export type OnDemandExitRule = {
onDemand: null;
};
export type WaivedExitRule = {
waived: null;
};
export type AfterDeadlineExitRule = {
afterDeadline: {
timer: import('@agoric/time').TimerService;
deadline: import('@agoric/time').Timestamp;
};
};
/**
* The possible keys are 'waived', 'onDemand', and 'afterDeadline'.
* `timer` and `deadline` only are used for the `afterDeadline` key.
* The possible records are:
* `{ waived: null }`
* `{ onDemand: null }`
* `{ afterDeadline: { timer :Timer<Deadline>, deadline :Deadline } }`
*/
export type ExitRule =
| OnDemandExitRule
| WaivedExitRule
| AfterDeadlineExitRule;
export type Instance<SF = any> = import('./utils.js').Instance<SF>;
export type ZCFSpec =
| {
bundleCap: import('@agoric/swingset-vat').BundleCap;
}
| {
name: string;
}
| {
/** Bundle ID */
id: string;
};
/**
* Opaque type for a JSONable source bundle
*/
export type SourceBundle = Record<string, any>;
export type PaymentPKeywordRecord = Record<Keyword, ERef<Payment<any>>>;
export type PaymentKeywordRecord = Record<Keyword, Payment<any>>;
export type InvitationDetails = {
installation: Installation;
instance: import('./utils.js').Instance<any>;
handle: InvitationHandle;
description: string;
customDetails?: Record<string, any> | undefined;
};
export type Installation<SF = any> = import('./utils.js').Installation<SF>;
export type InstallationStart<I extends Installation> =
import('./utils.js').InstallationStart<I>;
export type FeeIssuerConfig = {
name: string;
assetKind: AssetKind;
displayInfo: DisplayInfo;
};
export type ZoeFeesConfig = {
getPublicFacetFee: NatValue;
};
export type FeeMintAccess = Handle<'feeMintAccess'>;