rain.flow

Docs at https://rainprotocol.github.io/rain.flow

Current version

V3

V3 compatible contracts are still found in the monorepo at

https://github.com/rainprotocol/rain-protocol

The contracts in this repo are targetting V4 (see below).

Unstable versions

V4

The main changes in V4 replace previewFlow with stackToFlow which is a more generalised version of the same basic idea. By allowing any stack to be passed to the flow contract, the reader can simulate flows based on differen callers, state, time, etc.

V4 also targets a newer interpreter interface than V3, notably the native parsing functionality that works off a single bytes for the Rain bytecode rather than the bytes[] that older interpreters expected.

Deprecated versions

V2

V2 was deprecated to remove native flows entirely from the V3+ interface.

This is because flow implementations are likely to want to implement Multicall like functionality, such as that provided by Open Zeppelin.

Multicall based on delegate call preserves the msg.sender which is great for allowing EOA wallets like Metamask to flow under their own identity, but is a critical security issue when reading msg.value (e.g. to allow native flows).

https://samczsun.com/two-rights-might-make-a-wrong/

V1

V1 was deprecated because the SignedContext struct that it relies on was deprecated upstream. SignedContext was replaced with SignedContextV1 which was a minor change, reordering fields only for more efficient processing.

Contents

BadMinStackLength

Git Source

Thrown when the min outputs for a flow is fewer than the sentinels. This is always an implementation bug as the min outputs and sentinel count should both be compile time constants.

error BadMinStackLength(uint256 flowMinOutputs);

Parameters

NameTypeDescription
flowMinOutputsuint256The min outputs for the flow.

FlowCommon

Git Source

Inherits: ERC721Holder, ERC1155Holder, Multicall, ReentrancyGuard, IInterpreterCallerV2, DeployerDiscoverableMetaV2

Common functionality for flows. Largely handles the evaluable registration and dispatch. Also implementes the necessary interfaces for a smart contract to receive ERC721 and ERC1155 tokens. Flow contracts are expected to be deployed via. a proxy/factory as clones of an implementation contract. This makes flows cheap to deploy and every flow contract can be initialized with a different set of flows. This gives strong guarantees that the flow contract is only capable of evaluating registered flows, and that individual flow contracts cannot collide state with each other, given a correctly implemented interpreter store. Combining proxies with rainlang gives us a very powerful and flexible system for composing flows without significant gas overhead. Typically a flow contract deployment will cost well under 1M gas, which is very cheap for bespoke logic, without significant runtime overheads. This allows for new UX patterns where users can cheaply create many different tools such as NFT mints, auctions, escrows, etc. and aim to horizontally scale rather than design monolithic protocols. This does NOT implement the preview and flow logic directly because each flow implementation has different requirements for the mint and burn logic of the flow tokens. In the future, this may be refactored so that a single flow contract can handle all flows. FlowCommon is Multicall so it is NOT compatible with receiving ETH. This is because Multicall uses delegatecall in a loop which reuses msg.value for each loop iteration, effectively "double spending" the ETH it receives. This is a known issue with Multicall so in the future, we may refactor FlowCommon to not use Multicall and instead implement flow batching directly in the flow contracts.

State Variables

registeredFlows

This mapping tracks all flows that are registered at initialization. This is used to ensure that only registered flows are evaluated. Inheriting contracts MUST check this mapping before evaluating a flow, else anons can deploy their own evaluable and drain the contract. isRegistered will be set to FLOW_IS_REGISTERED for each registered flow.

mapping(bytes32 evaluableHash => uint256 isRegistered) internal registeredFlows;

Functions

constructor

Forwards config to DeployerDiscoverableMetaV2 and disables initializers. The initializers are disabled because inheriting contracts are expected to implement some kind of initialization logic that is compatible with cloning via. proxy/factory. Disabling initializers in the implementation contract forces that the only way to initialize the contract is via. a proxy, which should also strongly encourage patterns that atomically clone and initialize via. some factory.

constructor(bytes32 metaHash, DeployerDiscoverableMetaV2ConstructionConfig memory config)
    DeployerDiscoverableMetaV2(metaHash, config);

Parameters

NameTypeDescription
metaHashbytes32As per DeployerDiscoverableMetaV2.
configDeployerDiscoverableMetaV2ConstructionConfigAs per DeployerDiscoverableMetaV2.

flowCommonInit

Common initialization logic for inheriting contracts. This MUST be called by inheriting contracts in their initialization logic (and only).

function flowCommonInit(EvaluableConfigV2[] memory evaluableConfigs, uint256 flowMinOutputs)
    internal
    onlyInitializing;

Parameters

NameTypeDescription
evaluableConfigsEvaluableConfigV2[]The evaluable configs to register at initialization. Each of these represents a flow that defines valid token movements at runtime for the inheriting contract.
flowMinOutputsuint256The minimum number of outputs for each flow. All flows share the same minimum number of outputs for simplicity.

_flowStack

Standard evaluation logic for flows. This includes critical guards to ensure that only registered flows are evaluated. This is the only function that inheriting contracts should call to evaluate flows. The start and end pointers to the stack are returned so that inheriting contracts can easily scan the stack for sentinels, which is the expected pattern to determine what token moments are required.

function _flowStack(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    internal
    returns (Pointer, Pointer, uint256[] memory);

Parameters

NameTypeDescription
evaluableEvaluableThe evaluable to evaluate.
callerContextuint256[]The caller context to evaluate the evaluable with.
signedContextsSignedContextV1[]The signed contexts to evaluate the evaluable with.

Returns

NameTypeDescription
<none>PointerThe bottom of the stack after evaluation.
<none>PointerThe top of the stack after evaluation.
<none>uint256[]The key-value pairs that were emitted during evaluation.

Events

FlowInitialized

This event is emitted when a flow is registered at initialization.

event FlowInitialized(address sender, Evaluable evaluable);

Parameters

NameTypeDescription
senderaddressThe address that registered the flow.
evaluableEvaluableThe evaluable of the flow that was registered. The hash of this evaluable is used as the key in registeredFlows so users MUST provide the same evaluable when they evaluate the flow.

Constants

Git Source

FLOW_ENTRYPOINT

The entrypoint for a flow is always 0 because each flow has its own evaluable with its own entrypoint. Running multiple flows involves evaluating several expressions in sequence.

SourceIndex constant FLOW_ENTRYPOINT = SourceIndex.wrap(0);

FLOW_MAX_OUTPUTS

There is no maximum number of outputs for a flow. Pragmatically gas will limit the number of outputs well before this limit is reached.

uint16 constant FLOW_MAX_OUTPUTS = type(uint16).max;

FLOW_IS_REGISTERED

Any non-zero value indicates that the flow is registered.

uint256 constant FLOW_IS_REGISTERED = 1;

FLOW_IS_NOT_REGISTERED

Zero indicates that the flow is not registered.

uint256 constant FLOW_IS_NOT_REGISTERED = 0;

Contents

Contents

Flow

Git Source

Inherits: ICloneableV2, IFlowV4, FlowCommon

Functions

constructor

Forwards to FlowCommon constructor.

constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config);

initialize

Overloaded typed initialize function MUST revert with this error. As per ICloneableV2 interface.

function initialize(EvaluableConfigV2[] memory) external pure;

initialize

function initialize(bytes calldata data) external initializer returns (bytes32);

stackToFlow

Given a stack of values, convert it to a flow transfer. MUST NOT modify state but MAY revert if the stack is malformed. The intended workflow is that the interpreter contract is called to produce a stack then the stack is converted to a flow transfer struct, to allow the caller to preview a flow before actually executing it. By accepting a stack as input, the caller can preview any possible flow, not just ones that have been registered with the flow contract, and can preview flows that may not even be possible to execute due to the state of the tokens, or access gating that would exclude the caller, etc.

function stackToFlow(uint256[] memory stack) external pure virtual override returns (FlowTransferV1 memory);

Parameters

NameTypeDescription
stackuint256[]The stack of values to convert to a flow transfer.

Returns

NameTypeDescription
<none>FlowTransferV1flowTransfer The resulting flow transfer.

flow

Given an evaluable, caller context, and signed contexts, evaluate the evaluable and return the resulting flow transfer. MUST process the flow transfer atomically, either all of it succeeds or none of it succeeds. MUST revert if the evaluable is not registered with the flow contract. MUST revert if the evaluable reverts. MUST revert if the evaluable returns a stack that is malformed. MUST revert if the evaluable returns a stack that contains a token transfer that is not allowed by the flow contract (e.g. if a token is being moved from an address that is not the caller or the flow contract).

function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    external
    virtual
    nonReentrant
    returns (FlowTransferV1 memory);

Parameters

NameTypeDescription
evaluableEvaluableThe evaluable to evaluate.
callerContextuint256[]The caller context to pass to the evaluable.
signedContextsSignedContextV1[]The signed contexts to pass to the evaluable.

Returns

NameTypeDescription
<none>FlowTransferV1flowTransfer The resulting flow transfer.

Constants

Git Source

CALLER_META_HASH

The hash of the meta data expected to be passed to FlowCommon's constructor.

bytes32 constant CALLER_META_HASH = bytes32(0x95de68a447a477b8fab10701f1265b3e85a98b24710b3e40e6a96aa6d76263bc);

Contents

FlowERC1155

Git Source

Inherits: ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155

State Variables

sEvalHandleTransfer

True if the evaluable needs to be called on every transfer.

bool private sEvalHandleTransfer;

sEvaluable

The Evaluable that handles transfers.

Evaluable internal sEvaluable;

Functions

constructor

Forwards the FlowCommon constructor.

constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config);

initialize

Overloaded typed initialize function MUST revert with this error. As per ICloneableV2 interface.

function initialize(FlowERC1155ConfigV2 memory) external pure;

initialize

function initialize(bytes calldata data) external initializer returns (bytes32);

supportsInterface

Needed here to fix Open Zeppelin implementing supportsInterface on multiple base contracts.

function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155, ERC1155Receiver) returns (bool);

_afterTokenTransfer

function _afterTokenTransfer(
    address operator,
    address from,
    address to,
    uint256[] memory ids,
    uint256[] memory amounts,
    bytes memory data
) internal virtual override;

stackToFlow

As per IFlowV4 but returns a FlowERC1155IOV1 instead of a FlowTransferV1.

function stackToFlow(uint256[] memory stack) external pure override returns (FlowERC1155IOV1 memory flowERC1155IO);

Parameters

NameTypeDescription
stackuint256[]The stack to convert to a FlowERC1155IOV1.

Returns

NameTypeDescription
flowERC1155IOFlowERC1155IOV1The FlowERC1155IOV1 representation of the stack.

flow

As per IFlowV4 but returns a FlowERC1155IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC1155 accordingly.

function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    external
    virtual
    returns (FlowERC1155IOV1 memory);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
<none>FlowERC1155IOV1flowERC1155IO The FlowERC1155IOV1 representing all token mint/burns and transfers that occurred during the flow.

_stackToFlow

Wraps the standard LibFlow.stackToFlow function with the addition of consuming the mint/burn sentinels from the stack and returning them in the FlowERC1155IOV1.

function _stackToFlow(Pointer stackBottom, Pointer stackTop) internal pure returns (FlowERC1155IOV1 memory);

Parameters

NameTypeDescription
stackBottomPointerThe bottom of the stack.
stackTopPointerThe top of the stack.

Returns

NameTypeDescription
<none>FlowERC1155IOV1flowERC1155IO The FlowERC1155IOV1 representation of the stack.

_flow

Wraps the standard LibFlow.flow function to handle minting and burning of the flow contract itself. This involves consuming the mint/burn sentinels from the stack and minting/burning the tokens accordingly, then calling LibFlow.flow to handle the rest of the flow.

function _flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    internal
    virtual
    nonReentrant
    returns (FlowERC1155IOV1 memory);

Constants

Git Source

CALLER_META_HASH

The hash of the meta data expected by the FlowCommon constructor.

bytes32 constant CALLER_META_HASH = bytes32(0x7ea70f837234357ec1bb5b777e04453ebaf3ca778a98805c4bb20a738d559a21);

Contents

FlowERC20

Git Source

Inherits: ICloneableV2, IFlowERC20V4, FlowCommon, ERC20

State Variables

sEvalHandleTransfer

True if we need to eval handleTransfer on every transfer. For many tokens this will be false, so we don't want to invoke the external interpreter call just to cause a noop.

bool private sEvalHandleTransfer;

sEvaluable

The evaluable that will be used to evaluate handleTransfer on every transfer. This is only set if sEvalHandleTransfer is true.

Evaluable internal sEvaluable;

Functions

constructor

Forwards the FlowCommon constructor arguments to the FlowCommon.

constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config);

initialize

Overloaded typed initialize function MUST revert with this error. As per ICloneableV2 interface.

function initialize(FlowERC20ConfigV2 memory) external pure;

initialize

function initialize(bytes calldata data) external initializer returns (bytes32);

stackToFlow

As per IFlowV4 but returns a FlowERC20IOV1 instead of a FlowTransferV1.

function stackToFlow(uint256[] memory stack) external pure virtual override returns (FlowERC20IOV1 memory);

Parameters

NameTypeDescription
stackuint256[]The stack to convert to a FlowERC20IOV1.

Returns

NameTypeDescription
<none>FlowERC20IOV1flowERC20IO The FlowERC20IOV1 representation of the stack.

flow

As per IFlowV4 but returns a FlowERC20IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC20 accordingly.

function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    external
    virtual
    returns (FlowERC20IOV1 memory);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
<none>FlowERC20IOV1flowERC20IO The FlowERC20IOV1 representing all token mint/burns and transfers that occurred during the flow.

_afterTokenTransfer

Exposes the Open Zeppelin _afterTokenTransfer hook as an evaluable entrypoint so that the deployer of the token can use it to implement custom transfer logic. The stack is ignored, so if the expression author wants to prevent some kind of transfer, they can just revert within the expression evaluation.

function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override;

_stackToFlow

Wraps the standard LibFlow.stackToFlow with the additional logic to convert the stack to a FlowERC20IOV1 struct. This involves consuming the mints and burns from the stack as additional sentinel separated tuples. The mints are consumed first, then the burns, then the remaining stack is converted to a flow as normal.

function _stackToFlow(Pointer stackBottom, Pointer stackTop) internal pure virtual returns (FlowERC20IOV1 memory);

Parameters

NameTypeDescription
stackBottomPointerThe bottom of the stack.
stackTopPointerThe top of the stack.

Returns

NameTypeDescription
<none>FlowERC20IOV1flowERC20IO The resulting FlowERC20IOV1 struct.

_flow

Wraps the standard LibFlow.flow with the additional logic to handle the mints and burns from the FlowERC20IOV1 struct. The mints are processed first, then the burns, then the remaining flow is processed as normal.

function _flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    internal
    virtual
    nonReentrant
    returns (FlowERC20IOV1 memory);

Constants

Git Source

CALLER_META_HASH

The hash of the meta data expected to be passed to FlowCommon's constructor.

bytes32 constant CALLER_META_HASH = bytes32(0xff0499e4ee7171a54d176cfe13165a7ea512d146dbd99d42b3d3ec9963025acf);

Contents

FlowERC721

Git Source

Inherits: ICloneableV2, IFlowERC721V4, FlowCommon, ERC721

State Variables

sEvalHandleTransfer

True if we need to eval handleTransfer on every transfer. For many tokens this will be false, so we don't want to invoke the external interpreter call just to cause a noop.

bool private sEvalHandleTransfer;

sEvalTokenURI

True if we need to eval tokenURI to build the token URI. For many tokens this will be false, so we don't want to invoke the external interpreter call just to cause a noop.

bool private sEvalTokenURI;

sEvaluable

The evaluable that contains the entrypoints for handleTransfer and tokenURI. This is only set if sEvalHandleTransfer or sEvalTokenURI is true.

Evaluable internal sEvaluable;

sBaseURI

The base URI for all token URIs. This is set during initialization and cannot be changed. The token URI evaluable can be used for dynamic token URIs from the base URI.

string private sBaseURI;

Functions

constructor

Forwards the FlowCommon constructor arguments to the FlowCommon.

constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config);

supportsInterface

Needed here to fix Open Zeppelin implementing supportsInterface on multiple base contracts.

function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC1155Receiver) returns (bool);

initialize

Overloaded typed initialize function MUST revert with this error. As per ICloneableV2 interface.

function initialize(FlowERC721ConfigV2 memory) external pure;

initialize

function initialize(bytes calldata data) external initializer returns (bytes32);

_baseURI

Overrides the Open Zeppelin _baseURI hook to return the base URI set during initialization.

function _baseURI() internal view virtual override returns (string memory);

tokenURI

Overrides the Open Zeppelin tokenURI function to return the token URI calculated by the token URI evaluable. Currently the token URI evaluable can only return a single token ID value, and the token URI is built from that according to default Open Zeppelin logic. If the token URI evaluable is not set, then the default Open Zeppelin logic is used with the token ID passed in.

function tokenURI(uint256 tokenId) public view virtual override returns (string memory);

stackToFlow

As per IFlowV4 but returns a FlowERC721IOV1 instead of a FlowTransferV1.

function stackToFlow(uint256[] memory stack) external pure virtual override returns (FlowERC721IOV1 memory);

flow

As per IFlowV4 but returns a FlowERC721IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC721 accordingly.

function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    external
    virtual
    returns (FlowERC721IOV1 memory);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
<none>FlowERC721IOV1flowERC721IO The FlowERC721IOV1 representing all token mint/burns and transfers that occurred during the flow.

_afterTokenTransfer

Exposes the Open Zeppelin _afterTokenTransfer hook as an evaluable entrypoint so that the deployer of the token can use it to implement custom transfer logic. The stack is ignored, so if the expression author wants to prevent some kind of transfer, they can just revert within the expression evaluation.

function _afterTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal virtual override;

_stackToFlow

Wraps the standard LibFlow.stackToFlow with the additional logic to convert the stack to a FlowERC721IOV1 struct. This involves consuming the mints and burns from the stack as additional sentinel separated tuples. The mints are consumed first, then the burns, then the remaining stack is converted to a flow as normal.

function _stackToFlow(Pointer stackBottom, Pointer stackTop) internal pure returns (FlowERC721IOV1 memory);

Parameters

NameTypeDescription
stackBottomPointerThe bottom of the stack.
stackTopPointerThe top of the stack.

Returns

NameTypeDescription
<none>FlowERC721IOV1flowERC721IO The FlowERC721IOV1 representation of the stack.

_flow

Wraps the standard LibFlow.flow with the additional logic to handle self minting and burning. This involves consuming the mints and burns from the stack as additional sentinel separated tuples. The mints are consumed first, then the burns, then the remaining stack is converted to a flow as normal.

function _flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts)
    internal
    virtual
    nonReentrant
    returns (FlowERC721IOV1 memory);

Constants

Git Source

CALLER_META_HASH

The hash of the meta data expected to be passed to FlowCommon's constructor.

bytes32 constant CALLER_META_HASH = bytes32(0xf0003e81ff90467c9933f3ac68db3ca49df8b30ab83a0b88e1ed8381ed28fdd6);

Contents

Contents

Contents

FlowERC1155Config

Git Source

struct FlowERC1155Config {
    string uri;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

ERC1155SupplyChange

Git Source

struct ERC1155SupplyChange {
    address account;
    uint256 id;
    uint256 amount;
}

FlowERC1155IO

Git Source

struct FlowERC1155IO {
    ERC1155SupplyChange[] mints;
    ERC1155SupplyChange[] burns;
    FlowTransfer flow;
}

IFlowERC1155V1

Git Source

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContext[] calldata signedContexts
) external view returns (FlowERC1155IO calldata);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContext[] calldata signedContexts)
    external
    payable
    returns (FlowERC1155IO calldata);

Events

Initialize

event Initialize(address sender, FlowERC1155Config config);

ERC20SupplyChange

Git Source

struct ERC20SupplyChange {
    address account;
    uint256 amount;
}

FlowERC20IO

Git Source

struct FlowERC20IO {
    ERC20SupplyChange[] mints;
    ERC20SupplyChange[] burns;
    FlowTransfer flow;
}

FlowERC20Config

Git Source

Constructor config.

struct FlowERC20Config {
    string name;
    string symbol;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
namestring
symbolstring
evaluableConfigEvaluableConfig
flowConfigEvaluableConfig[]

IFlowERC20V1

Git Source

Mints itself according to some predefined schedule. The schedule is expressed as an expression and the claim function is world-callable. Intended behaviour is to avoid sybils infinitely minting by putting the claim functionality behind a TierV2 contract. The flow contract itself implements ReadOnlyTier and every time a claim is processed it logs the block number of the claim against every tier claimed. So the block numbers in the tier report for FlowERC20 are the last time that tier was claimed against this contract. The simplest way to make use of this information is to take the max block for the underlying tier and the last claim and then diff it against the current block number. See test/Claim/FlowERC20.sol.ts for examples, including providing staggered rewards where more tokens are minted for higher tier accounts.

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContext[] calldata signedContexts
) external view returns (FlowERC20IO calldata);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContext[] calldata signedContexts)
    external
    payable
    returns (FlowERC20IO calldata);

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC20Config config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC20ConfigAll initialized config.

FlowERC721Config

Git Source

Constructor config.

struct FlowERC721Config {
    string name;
    string symbol;
    string baseURI;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
namestring
symbolstring
baseURIstring
evaluableConfigEvaluableConfig
flowConfigEvaluableConfig[]

ERC721SupplyChange

Git Source

struct ERC721SupplyChange {
    address account;
    uint256 id;
}

FlowERC721IO

Git Source

struct FlowERC721IO {
    ERC721SupplyChange[] mints;
    ERC721SupplyChange[] burns;
    FlowTransfer flow;
}

IFlowERC721V1

Git Source

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContext[] calldata signedContexts
) external view returns (FlowERC721IO calldata);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContext[] calldata signedContexts)
    external
    payable
    returns (FlowERC721IO calldata);

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC721Config config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC721ConfigAll initialized config.

FlowConfig

Git Source

struct FlowConfig {
    EvaluableConfig dummyConfig;
    EvaluableConfig[] config;
}

NativeTransfer

Git Source

struct NativeTransfer {
    address from;
    address to;
    uint256 amount;
}

ERC20Transfer

Git Source

struct ERC20Transfer {
    address token;
    address from;
    address to;
    uint256 amount;
}

ERC721Transfer

Git Source

struct ERC721Transfer {
    address token;
    address from;
    address to;
    uint256 id;
}

ERC1155Transfer

Git Source

struct ERC1155Transfer {
    address token;
    address from;
    address to;
    uint256 id;
    uint256 amount;
}

FlowTransfer

Git Source

struct FlowTransfer {
    NativeTransfer[] native;
    ERC20Transfer[] erc20;
    ERC721Transfer[] erc721;
    ERC1155Transfer[] erc1155;
}

IFlowV1

Git Source

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContext[] calldata signedContexts
) external view returns (FlowTransfer calldata flowTransfer);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContext[] calldata signedContexts)
    external
    payable
    returns (FlowTransfer calldata flowTransfer);

Events

Initialize

event Initialize(address sender, FlowConfig config);

Contents

FlowERC1155Config

Git Source

struct FlowERC1155Config {
    string uri;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

ERC1155SupplyChange

Git Source

struct ERC1155SupplyChange {
    address account;
    uint256 id;
    uint256 amount;
}

FlowERC1155IO

Git Source

struct FlowERC1155IO {
    ERC1155SupplyChange[] mints;
    ERC1155SupplyChange[] burns;
    FlowTransfer flow;
}

IFlowERC1155V2

Git Source

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowERC1155IO calldata);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    payable
    returns (FlowERC1155IO calldata);

Events

Initialize

event Initialize(address sender, FlowERC1155Config config);

ERC20SupplyChange

Git Source

struct ERC20SupplyChange {
    address account;
    uint256 amount;
}

FlowERC20IO

Git Source

struct FlowERC20IO {
    ERC20SupplyChange[] mints;
    ERC20SupplyChange[] burns;
    FlowTransfer flow;
}

FlowERC20Config

Git Source

Constructor config.

struct FlowERC20Config {
    string name;
    string symbol;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
namestring
symbolstring
evaluableConfigEvaluableConfig
flowConfigEvaluableConfig[]

IFlowERC20V2

Git Source

Mints itself according to some predefined schedule. The schedule is expressed as an expression and the claim function is world-callable. Intended behaviour is to avoid sybils infinitely minting by putting the claim functionality behind a TierV2 contract. The flow contract itself implements ReadOnlyTier and every time a claim is processed it logs the block number of the claim against every tier claimed. So the block numbers in the tier report for FlowERC20 are the last time that tier was claimed against this contract. The simplest way to make use of this information is to take the max block for the underlying tier and the last claim and then diff it against the current block number. See test/Claim/FlowERC20.sol.ts for examples, including providing staggered rewards where more tokens are minted for higher tier accounts.

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowERC20IO calldata);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    payable
    returns (FlowERC20IO calldata);

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC20Config config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC20ConfigAll initialized config.

FlowERC721Config

Git Source

Constructor config.

struct FlowERC721Config {
    string name;
    string symbol;
    string baseURI;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
namestring
symbolstring
baseURIstring
evaluableConfigEvaluableConfig
flowConfigEvaluableConfig[]

ERC721SupplyChange

Git Source

struct ERC721SupplyChange {
    address account;
    uint256 id;
}

FlowERC721IO

Git Source

struct FlowERC721IO {
    ERC721SupplyChange[] mints;
    ERC721SupplyChange[] burns;
    FlowTransfer flow;
}

IFlowERC721V2

Git Source

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowERC721IO calldata);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    payable
    returns (FlowERC721IO calldata);

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC721Config config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC721ConfigAll initialized config.

FlowConfig

Git Source

struct FlowConfig {
    EvaluableConfig dummyConfig;
    EvaluableConfig[] config;
}

NativeTransfer

Git Source

struct NativeTransfer {
    address from;
    address to;
    uint256 amount;
}

ERC20Transfer

Git Source

struct ERC20Transfer {
    address token;
    address from;
    address to;
    uint256 amount;
}

ERC721Transfer

Git Source

struct ERC721Transfer {
    address token;
    address from;
    address to;
    uint256 id;
}

ERC1155Transfer

Git Source

struct ERC1155Transfer {
    address token;
    address from;
    address to;
    uint256 id;
    uint256 amount;
}

FlowTransfer

Git Source

struct FlowTransfer {
    NativeTransfer[] native;
    ERC20Transfer[] erc20;
    ERC721Transfer[] erc721;
    ERC1155Transfer[] erc1155;
}

IFlowV2

Git Source

Functions

previewFlow

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowTransfer calldata flowTransfer);

flow

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    payable
    returns (FlowTransfer calldata flowTransfer);

Events

Initialize

event Initialize(address sender, FlowConfig config);

Contents

FlowERC1155ConfigV2

Git Source

Initialization config.

struct FlowERC1155ConfigV2 {
    string uri;
    EvaluableConfigV2 evaluableConfig;
    EvaluableConfigV2[] flowConfig;
}

Properties

NameTypeDescription
uristringAs per Open Zeppelin ERC1155Upgradeable.
evaluableConfigEvaluableConfigV2The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers.
flowConfigEvaluableConfigV2[]Initialization config for the Evaluables that define the flow behaviours outside self mints/burns.

IFlowERC1155V4

Git Source

Functions

stackToFlow

As per IFlowV4 but returns a FlowERC1155IOV1 instead of a FlowTransferV1.

function stackToFlow(uint256[] memory stack) external pure returns (FlowERC1155IOV1 memory flowERC1155IO);

Parameters

NameTypeDescription
stackuint256[]The stack to convert to a FlowERC1155IOV1.

Returns

NameTypeDescription
flowERC1155IOFlowERC1155IOV1The FlowERC1155IOV1 representation of the stack.

flow

As per IFlowV4 but returns a FlowERC1155IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC1155 accordingly.

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowERC1155IOV1 calldata);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
<none>FlowERC1155IOV1flowERC1155IO The FlowERC1155IOV1 representing all token mint/burns and transfers that occurred during the flow.

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC1155ConfigV2 config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC1155ConfigV2All initialized config.

FlowERC20ConfigV2

Git Source

Initialization config.

struct FlowERC20ConfigV2 {
    string name;
    string symbol;
    EvaluableConfigV2 evaluableConfig;
    EvaluableConfigV2[] flowConfig;
}

Properties

NameTypeDescription
namestringAs per Open Zeppelin ERC20Upgradeable.
symbolstringAs per Open Zeppelin ERC20Upgradeable.
evaluableConfigEvaluableConfigV2The EvaluableConfigV2 to use to build the evaluable that can be used to evaluate handleTransfer.
flowConfigEvaluableConfigV2[]The EvaluableConfigV2[] to use to build the evaluables for all the flows, including self minting and burning.

IFlowERC20V4

Git Source

Functions

stackToFlow

As per IFlowV4 but returns a FlowERC20IOV1 instead of a FlowTransferV1.

function stackToFlow(uint256[] memory stack) external pure returns (FlowERC20IOV1 memory flowERC20IO);

Parameters

NameTypeDescription
stackuint256[]The stack to convert to a FlowERC20IOV1.

Returns

NameTypeDescription
flowERC20IOFlowERC20IOV1The FlowERC20IOV1 representation of the stack.

flow

As per IFlowV4 but returns a FlowERC20IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC20 accordingly.

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowERC20IOV1 calldata flowERC20IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
flowERC20IOFlowERC20IOV1The FlowERC20IOV1 representing all token mint/burns and transfers that occurred during the flow.

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC20ConfigV2 config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC20ConfigV2All initialized config.

BurnerNotOwner

Git Source

Thrown when burner of tokens is not the owner of tokens.

error BurnerNotOwner();

FlowERC721ConfigV2

Git Source

Initialization config.

struct FlowERC721ConfigV2 {
    string name;
    string symbol;
    string baseURI;
    EvaluableConfigV2 evaluableConfig;
    EvaluableConfigV2[] flowConfig;
}

Properties

NameTypeDescription
namestringAs per Open Zeppelin ERC721Upgradeable.
symbolstringAs per Open Zeppelin ERC721Upgradeable.
baseURIstringAs per Open Zeppelin ERC721Upgradeable.
evaluableConfigEvaluableConfigV2The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers and build token IDs for the token URI.
flowConfigEvaluableConfigV2[]Initialization config for the Evaluables that define the flow behaviours outside self mints/burns.

IFlowERC721V4

Git Source

Functions

stackToFlow

As per IFlowV4 but returns a FlowERC721IOV1 instead of a FlowTransferV1.

function stackToFlow(uint256[] memory stack) external pure returns (FlowERC721IOV1 memory flowERC721IO);

flow

As per IFlowV4 but returns a FlowERC721IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC721 accordingly.

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowERC721IOV1 calldata flowERC721IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
flowERC721IOFlowERC721IOV1The FlowERC721IOV1 representing all token mint/burns and transfers that occurred during the flow.

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC721ConfigV2 config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC721ConfigV2All initialized config.

IFlowV4

Git Source

Interface for a flow contract that does NOT require native minting or burning of itself as a token. This is the base case that all other flow interfaces model themselves after, with the addition of token minting and burning. Current functionality only allows for moving third party tokens between accounts. Token standards ERC20, ERC721, and ERC1155 are supported. The basic lifecycle of a flow is:

  • Flow is deployed as a reference implementation to be cloned, with its initializers disabled on construction.
  • Flow is cloned and initialized with an abi encoded list of evaluable configs that define every possible movement of tokens that can occur due to the clone. The EOA that deployed the clone DOES NOT have any special privileges over the clone, although they could grant themselves privileges by flowing tokens to themselves or similar within the evaluables. Ideally the EOA doesn't introduce "admin" features as it would be a security risk to themselves and others. In the case that they do, all priviledges will be visible in the rainlang code of the evaluable, there's no hidden functionality that can be introduced to the clone bytecode.
  • Anyone can call flow on the clone, passing in one of the evaluables set during initialization. If the evaluable passed by the caller does not match an initialized evaluable, the flow MUST revert with UnregisteredFlow. The entirety of the resulting stack from the evaluation defines all the token movements that MUST occur as a result of the flow. ANY failures during the flow MUST revert the entire flow, leaving the state of the tokens unchanged. The structure of the stack can be thought of as a simple list of transfers. All the erc20 tokens are moved first, then the erc721 tokens, then the erc1155 tokens. Each token type is separated in the stack by a sentinel value. The sentinel is a constant, RAIN_FLOW_SENTINEL, that is guaranteed to not collide with any token amounts or addresses. The sentinel is also guaranteed to not collide with any other sentinels from other contexts, to the extent that we can guarantee that with raw cryptographic collision resistance. This sentinel can be thought of as similar to the null terminator in a c string, it's a value that is guaranteed to not be a valid value for the type of data it's separating. The main benefit in this context, for rainlang authors, is that they can always use the same constant value in all their rainlang code to separate the different token types, rather than needing to manually calculate the length of the tuples they're wanting to flow over in each token type (which would be very error prone). Currently every token transfer type MUST be present in every flow stack, which is awkward as it means that if you want to flow erc20 tokens, you MUST also flow erc721 and erc1155 tokens, even if you don't want to. This is a limitation of the current implementation, and will be fixed in a future version. Each individual token transfer is simply a list of values, where the values are specific to the token type.
  • erc20 transfers are a list of 4 values:
  • address of the token contract
  • address of the token sender
  • address of the token recipient
  • amount of tokens to transfer
  • erc721 transfers are a list of 4 values:
  • address of the token contract
  • address of the token sender
  • address of the token recipient
  • token id to transfer
  • erc1155 transfers are a list of 5 values:
  • address of the token contract
  • address of the token sender
  • address of the token recipient
  • token id to transfer
  • amount of tokens to transfer The final stack is processed from the bottom up, so the first token transfer in the stack is the last one to be processed. For example, a rainlang expression that transfers 1e18 erc20 token 0xf00baa from the flow contract to the address 0xdeadbeef, and 1 erc721 token address 0x1234 and id 5678 from the address 0xdeadbeef to the flow contract, would result in the following rainlang/stack:
* sentinel is always the same. */
sentinel: 0xfea74d0c9bf4a3c28f0dd0674db22a3d7f8bf259c56af19f4ac1e735b156974f,
* erc1155 transfers are first, just a sentinel as there's nothing to do */
_: sentinel,
* erc721 transfers are next, with the token id as the last value */
_: 0x1234 0xdeadbeef context<0 1>() 5678,
* erc20 transfers are last, with the amount as the last value */
_: 0xf00baa context<0 1>() 0xdeadbeef 1e18;

Note that for all token transfers the sender of the tokens MUST be either the flow contract itself, or the caller of the flow contract. This is to prevent the flow contract from being able to transfer tokens from arbitrary addresses without their consent. Even if some address has approved the flow contract to transfer tokens on their behalf, the flow contract MUST NOT transfer tokens from that address unless the caller of the flow contract is that address. Note that native gas movements are not supported in this version of the flow contract. This is because the current reference implementation uses Multicall to batch together multiple calls to the flow contract, and this involves a loop over a delegate call, which is not safe to do with native gas movements. This will be fixed in a future version of the interface where batching is handled by the flow contract itself, rather than relying on Multicall.

Functions

stackToFlow

Given a stack of values, convert it to a flow transfer. MUST NOT modify state but MAY revert if the stack is malformed. The intended workflow is that the interpreter contract is called to produce a stack then the stack is converted to a flow transfer struct, to allow the caller to preview a flow before actually executing it. By accepting a stack as input, the caller can preview any possible flow, not just ones that have been registered with the flow contract, and can preview flows that may not even be possible to execute due to the state of the tokens, or access gating that would exclude the caller, etc.

function stackToFlow(uint256[] memory stack) external pure returns (FlowTransferV1 calldata flowTransfer);

Parameters

NameTypeDescription
stackuint256[]The stack of values to convert to a flow transfer.

Returns

NameTypeDescription
flowTransferFlowTransferV1The resulting flow transfer.

flow

Given an evaluable, caller context, and signed contexts, evaluate the evaluable and return the resulting flow transfer. MUST process the flow transfer atomically, either all of it succeeds or none of it succeeds. MUST revert if the evaluable is not registered with the flow contract. MUST revert if the evaluable reverts. MUST revert if the evaluable returns a stack that is malformed. MUST revert if the evaluable returns a stack that contains a token transfer that is not allowed by the flow contract (e.g. if a token is being moved from an address that is not the caller or the flow contract).

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowTransferV1 calldata flowTransfer);

Parameters

NameTypeDescription
evaluableEvaluableThe evaluable to evaluate.
callerContextuint256[]The caller context to pass to the evaluable.
signedContextsSignedContextV1[]The signed contexts to pass to the evaluable.

Returns

NameTypeDescription
flowTransferFlowTransferV1The resulting flow transfer.

Events

Initialize

MUST be emitted when the flow contract is initialized.

event Initialize(address sender, EvaluableConfigV2[] config);

Parameters

NameTypeDescription
senderaddressThe EOA that deployed the flow contract.
configEvaluableConfigV2[]The list of evaluable configs that define the flows.

FlowERC1155Config

Git Source

Initializer config.

struct FlowERC1155Config {
    string uri;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
uristringAs per Open Zeppelin ERC1155Upgradeable.
evaluableConfigEvaluableConfigThe EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers.
flowConfigEvaluableConfig[]Constructor config for the Evaluables that define the flow behaviours including self mints/burns.

ERC1155SupplyChange

Git Source

Represents a single mint or burn of a single ERC1155 token. Whether this is a mint or burn must be implied by the context.

struct ERC1155SupplyChange {
    address account;
    uint256 id;
    uint256 amount;
}

Properties

NameTypeDescription
accountaddressThe address the token is being minted/burned to/from.
iduint256The id of the token being minted/burned.
amountuint256The amount of the token being minted/burned.

FlowERC1155IOV1

Git Source

Represents a set of ERC1155 transfers, including self mints/burns.

struct FlowERC1155IOV1 {
    ERC1155SupplyChange[] mints;
    ERC1155SupplyChange[] burns;
    FlowTransferV1 flow;
}

Properties

NameTypeDescription
mintsERC1155SupplyChange[]The mints that occurred.
burnsERC1155SupplyChange[]The burns that occurred.
flowFlowTransferV1The transfers that occured.

IFlowERC1155V3

Git Source

Functions

previewFlow

As per IFlowV3 but returns a FlowERC1155IOV1 instead of a FlowTransferV1.

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowERC1155IOV1 calldata flowERC1155IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable that is flowing.
callerContextuint256[]The context of the caller.
signedContextsSignedContextV1[]The signed contexts of the caller.

Returns

NameTypeDescription
flowERC1155IOFlowERC1155IOV1The FlowERC1155IOV1 that occurred.

flow

As per IFlowV3 but returns a FlowERC1155IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC1155 accordingly.

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowERC1155IOV1 calldata flowERC1155IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable that is flowing.
callerContextuint256[]The context of the caller.
signedContextsSignedContextV1[]The signed contexts of the caller.

Returns

NameTypeDescription
flowERC1155IOFlowERC1155IOV1The FlowERC1155IOV1 that occurred.

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC1155Config config);

Constants

Git Source

FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT

Entrypont of the handleTransfer evaluation.

SourceIndex constant FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT = SourceIndex.wrap(0);

FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS

Minimum number of outputs of the handleTransfer evaluation. This is 0 because the return stack is ignored.

uint256 constant FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS = 0;

FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS

Maximum number of outputs of the handleTransfer evaluation. This is 0 because the return stack is ignored.

uint16 constant FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS = 0;

FLOW_ERC1155_MIN_FLOW_SENTINELS

Minimum number of sentinels required by FlowERC1155. This is 2 more than the minimum required by FlowCommon because the mints and burns are included in the stack.

uint256 constant FLOW_ERC1155_MIN_FLOW_SENTINELS = MIN_FLOW_SENTINELS + 2;

RAIN_FLOW_ERC1155_SENTINEL

v3 of FlowERC1155 expected a sentinel different to RAIN_FLOW_SENTINEL, but this was generally more confusing than helpful.

Sentinel constant RAIN_FLOW_ERC1155_SENTINEL =
    Sentinel.wrap(uint256(keccak256(bytes("RAIN_FLOW_ERC1155_SENTINEL")) | SENTINEL_HIGH_BITS));

ERC20SupplyChange

Git Source

Represents a single mint or burn of a single ERC20 token. Whether this is a mint or burn must be implied by the context.

struct ERC20SupplyChange {
    address account;
    uint256 amount;
}

Properties

NameTypeDescription
accountaddressThe address the token is being minted/burned to/from.
amountuint256The amount of the token being minted/burned.

FlowERC20IOV1

Git Source

Represents a set of ERC20 transfers, including self mints/burns.

struct FlowERC20IOV1 {
    ERC20SupplyChange[] mints;
    ERC20SupplyChange[] burns;
    FlowTransferV1 flow;
}

Properties

NameTypeDescription
mintsERC20SupplyChange[]The mints that occurred.
burnsERC20SupplyChange[]The burns that occurred.
flowFlowTransferV1The transfers that occured.

FlowERC20Config

Git Source

Initializer config.

struct FlowERC20Config {
    string name;
    string symbol;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
namestringAs per Open Zeppelin ERC20Upgradeable.
symbolstringAs per Open Zeppelin ERC20Upgradeable.
evaluableConfigEvaluableConfigThe EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers.
flowConfigEvaluableConfig[]Initializer config for the Evaluables that define the flow behaviours including self mints/burns.

IFlowERC20V3

Git Source

Mints itself according to some predefined schedule. The schedule is expressed as an expression and the claim function is world-callable. Intended behaviour is to avoid sybils infinitely minting by putting the claim functionality behind a TierV2 contract. The flow contract itself implements ReadOnlyTier and every time a claim is processed it logs the block number of the claim against every tier claimed. So the block numbers in the tier report for FlowERC20 are the last time that tier was claimed against this contract. The simplest way to make use of this information is to take the max block for the underlying tier and the last claim and then diff it against the current block number. See test/Claim/FlowERC20.sol.ts for examples, including providing staggered rewards where more tokens are minted for higher tier accounts.

Functions

previewFlow

As per IFlowV3 but returns a FlowERC20IOV1 instead of a FlowTransferV1.

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowERC20IOV1 calldata flowERC20IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
flowERC20IOFlowERC20IOV1The FlowERC20IOV1 that occurred.

flow

As per IFlowV3 but returns a FlowERC20IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC20 accordingly.

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowERC20IOV1 calldata flowERC20IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable to use to evaluate the flow.
callerContextuint256[]The caller context to use to evaluate the flow.
signedContextsSignedContextV1[]The signed contexts to use to evaluate the flow.

Returns

NameTypeDescription
flowERC20IOFlowERC20IOV1The FlowERC20IOV1 that occurred.

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC20Config config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC20ConfigAll initialized config.

Constants

Git Source

RAIN_FLOW_ERC20_SENTINEL

v3 of FlowERC20 expected a sentinel different to RAIN_FLOW_SENTINEL, but this was generally more confusing than helpful.

Sentinel constant RAIN_FLOW_ERC20_SENTINEL =
    Sentinel.wrap(uint256(keccak256(bytes("RAIN_FLOW_ERC20_SENTINEL")) | SENTINEL_HIGH_BITS));

FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT

Entrypont of the handleTransfer evaluation.

SourceIndex constant FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT = SourceIndex.wrap(0);

FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS

Minimum number of outputs of the handleTransfer evaluation. This is 0 because the return stack is ignored.

uint256 constant FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS = 0;

FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS

Maximum number of outputs of the handleTransfer evaluation. This is 0 because the return stack is ignored.

uint16 constant FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS = 0;

FLOW_ERC20_MIN_FLOW_SENTINELS

Minimum number of sentinels required by FlowERC20. This is 2 more than the minimum required by FlowCommon because the mints and burns are included in the stack.

uint256 constant FLOW_ERC20_MIN_FLOW_SENTINELS = MIN_FLOW_SENTINELS + 2;

FlowERC721Config

Git Source

Initializer config.

struct FlowERC721Config {
    string name;
    string symbol;
    string baseURI;
    EvaluableConfig evaluableConfig;
    EvaluableConfig[] flowConfig;
}

Properties

NameTypeDescription
namestringAs per Open Zeppelin ERC721Upgradeable.
symbolstringAs per Open Zeppelin ERC721Upgradeable.
baseURIstringAs per Open Zeppelin ERC721Upgradeable.
evaluableConfigEvaluableConfigThe EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers and token URIs. The token URI entrypoint is optional.
flowConfigEvaluableConfig[]Constructor config for the Evaluables that define the flow behaviours including self mints/burns.

ERC721SupplyChange

Git Source

Represents a single mint or burn of a single ERC721 token. Whether this is a mint or burn must be implied by the context.

struct ERC721SupplyChange {
    address account;
    uint256 id;
}

Properties

NameTypeDescription
accountaddressThe address the token is being minted/burned to/from.
iduint256The id of the token being minted/burned.

FlowERC721IOV1

Git Source

Represents a set of ERC721 transfers, including self mints/burns.

struct FlowERC721IOV1 {
    ERC721SupplyChange[] mints;
    ERC721SupplyChange[] burns;
    FlowTransferV1 flow;
}

Properties

NameTypeDescription
mintsERC721SupplyChange[]The mints that occurred.
burnsERC721SupplyChange[]The burns that occurred.
flowFlowTransferV1The transfers that occured.

IFlowERC721V3

Git Source

Functions

previewFlow

As per IFlowV3 but returns a FlowERC721IOV1 instead of a FlowTransferV1.

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowERC721IOV1 calldata flowERC721IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable that is flowing.
callerContextuint256[]The context of the caller.
signedContextsSignedContextV1[]The signed contexts of the caller.

Returns

NameTypeDescription
flowERC721IOFlowERC721IOV1The FlowERC721IOV1 that would occur if the flow was executed.

flow

As per IFlowV3 but returns a FlowERC721IOV1 instead of a FlowTransferV1 and mints/burns itself as an ERC721 accordingly.

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowERC721IOV1 calldata flowERC721IO);

Parameters

NameTypeDescription
evaluableEvaluableThe Evaluable that is flowing.
callerContextuint256[]The context of the caller.
signedContextsSignedContextV1[]The signed contexts of the caller.

Returns

NameTypeDescription
flowERC721IOFlowERC721IOV1The FlowERC721IOV1 that occurred.

Events

Initialize

Contract has initialized.

event Initialize(address sender, FlowERC721Config config);

Parameters

NameTypeDescription
senderaddressmsg.sender initializing the contract (factory).
configFlowERC721ConfigAll initialized config.

Constants

Git Source

FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT

Entrypont of the handleTransfer evaluation.

SourceIndex constant FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT = SourceIndex.wrap(0);

FLOW_ERC721_TOKEN_URI_ENTRYPOINT

Entrypont of the tokenURI evaluation.

SourceIndex constant FLOW_ERC721_TOKEN_URI_ENTRYPOINT = SourceIndex.wrap(1);

FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS

Minimum number of outputs of the handleTransfer evaluation. This is 0 because the return stack is ignored.

uint256 constant FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS = 0;

FLOW_ERC721_TOKEN_URI_MIN_OUTPUTS

Minimum number of outputs of the tokenURI evaluation. This is 1 because we can only handle a single token ID value.

uint256 constant FLOW_ERC721_TOKEN_URI_MIN_OUTPUTS = 1;

FLOW_ERC721_HANDLE_TRANSFER_MAX_OUTPUTS

Maximum number of outputs of the handleTransfer evaluation. This is 0 because the return stack is ignored.

uint16 constant FLOW_ERC721_HANDLE_TRANSFER_MAX_OUTPUTS = 0;

FLOW_ERC721_TOKEN_URI_MAX_OUTPUTS

Maximum number of outputs of the tokenURI evaluation. This is 1 because we can only handle a single token ID value.

uint16 constant FLOW_ERC721_TOKEN_URI_MAX_OUTPUTS = 1;

RAIN_FLOW_ERC721_SENTINEL

v3 of FlowERC721 expected a sentinel different to RAIN_FLOW_SENTINEL, but this was generally more confusing than helpful.

Sentinel constant RAIN_FLOW_ERC721_SENTINEL =
    Sentinel.wrap(uint256(keccak256(bytes("RAIN_FLOW_ERC721_SENTINEL")) | SENTINEL_HIGH_BITS));

FLOW_ERC721_MIN_FLOW_SENTINELS

Minimum number of sentinels required by FlowERC721. This is 2 more than the minimum required by FlowCommon because the mints and burns are included in the stack.

uint256 constant FLOW_ERC721_MIN_FLOW_SENTINELS = MIN_FLOW_SENTINELS + 2;

UnregisteredFlow

Git Source

Thrown when the flow being evaluated is unregistered.

error UnregisteredFlow(bytes32 unregisteredHash);

Parameters

NameTypeDescription
unregisteredHashbytes32Hash of the unregistered flow.

UnsupportedNativeFlow

Git Source

Thrown for unsupported native transfers.

error UnsupportedNativeFlow();

UnsupportedERC20Flow

Git Source

Thrown for unsupported erc20 transfers.

error UnsupportedERC20Flow();

UnsupportedERC721Flow

Git Source

Thrown for unsupported erc721 transfers.

error UnsupportedERC721Flow();

UnsupportedERC1155Flow

Git Source

Thrown for unsupported erc1155 transfers.

error UnsupportedERC1155Flow();

FlowConfig

Git Source

Wraps EvaluableConfig[] to workaround a Solidity bug. https://github.com/ethereum/solidity/issues/13597

struct FlowConfig {
    EvaluableConfig dummyConfig;
    EvaluableConfig[] config;
}

Properties

NameTypeDescription
dummyConfigEvaluableConfigA dummy config to workaround a Solidity bug.
configEvaluableConfig[]The list of evaluable configs that define the flows.

ERC20Transfer

Git Source

Represents a single transfer of a single ERC20 token.

struct ERC20Transfer {
    address token;
    address from;
    address to;
    uint256 amount;
}

Properties

NameTypeDescription
tokenaddressThe address of the ERC20 token being transferred.
fromaddressThe address the token is being transferred from.
toaddressThe address the token is being transferred to.
amountuint256The amount of the token being transferred.

ERC721Transfer

Git Source

Represents a single transfer of a single ERC721 token.

struct ERC721Transfer {
    address token;
    address from;
    address to;
    uint256 id;
}

Properties

NameTypeDescription
tokenaddressThe address of the ERC721 token being transferred.
fromaddressThe address the token is being transferred from.
toaddressThe address the token is being transferred to.
iduint256The id of the token being transferred.

ERC1155Transfer

Git Source

Represents a single transfer of a single ERC1155 token.

struct ERC1155Transfer {
    address token;
    address from;
    address to;
    uint256 id;
    uint256 amount;
}

Properties

NameTypeDescription
tokenaddressThe address of the ERC1155 token being transferred.
fromaddressThe address the token is being transferred from.
toaddressThe address the token is being transferred to.
iduint256The id of the token being transferred.
amountuint256The amount of the token being transferred.

FlowTransferV1

Git Source

Represents an ordered set of transfers that will be or have been executed. Supports ERC20, ERC721, and ERC1155 transfers.

struct FlowTransferV1 {
    ERC20Transfer[] erc20;
    ERC721Transfer[] erc721;
    ERC1155Transfer[] erc1155;
}

Properties

NameTypeDescription
erc20ERC20Transfer[]An array of ERC20 transfers.
erc721ERC721Transfer[]An array of ERC721 transfers.
erc1155ERC1155Transfer[]An array of ERC1155 transfers.

IFlowV3

Git Source

Functions

previewFlow

"Dry run" of a flow, returning the resulting token transfers without actually executing them.

function previewFlow(
    Evaluable calldata evaluable,
    uint256[] calldata callerContext,
    SignedContextV1[] calldata signedContexts
) external view returns (FlowTransferV1 calldata flowTransfer);

Parameters

NameTypeDescription
evaluableEvaluableThe evaluable to evaluate.
callerContextuint256[]The caller context to use when evaluating the flow.
signedContextsSignedContextV1[]The signed contexts to use when evaluating the flow.

flow

Given an evaluable, caller context, and signed contexts, evaluate the evaluable and return the resulting flow transfer. MUST process the flow transfer atomically, either all of it succeeds or none of it succeeds. MUST revert if the evaluable is not registered with the flow contract. MUST revert if the evaluable reverts. MUST revert if the evaluable returns a stack that is malformed. MUST revert if the evaluable returns a stack that contains a token transfer that is not allowed by the flow contract (e.g. if a token is being moved from an address that is not the caller or the flow contract).

function flow(Evaluable calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
    external
    returns (FlowTransferV1 calldata flowTransfer);

Parameters

NameTypeDescription
evaluableEvaluableThe evaluable to evaluate.
callerContextuint256[]The caller context to pass to the evaluable.
signedContextsSignedContextV1[]The signed contexts to pass to the evaluable.

Returns

NameTypeDescription
flowTransferFlowTransferV1The resulting flow transfer.

Events

Initialize

MUST be emitted when the flow contract is initialized.

event Initialize(address sender, FlowConfig config);

Parameters

NameTypeDescription
senderaddressThe EOA that deployed the flow contract.
configFlowConfigThe list of evaluable configs that define the flows.

Constants

Git Source

MIN_FLOW_SENTINELS

The number of sentinels required by FlowCommon. An evaluable can never have fewer minimum outputs than required sentinels.

uint256 constant MIN_FLOW_SENTINELS = 3;

SENTINEL_HIGH_BITS

Sets the high bits of all flow sentinels to guarantee that the numeric value of the sentinel will never collide with a token amount or address. This guarantee holds as long as the token supply is less than 2^252, and the that token IDs have no specific reason to collide with the sentinel. i.e. There won't be random collisions because the space of token IDs is too large.

bytes32 constant SENTINEL_HIGH_BITS = bytes32(0xF000000000000000000000000000000000000000000000000000000000000000);

RAIN_FLOW_SENTINEL

*We want a sentinel with the following properties:

  • Won't collide with token amounts (| with very large number)
  • Won't collide with token addresses
  • Won't collide with common values like type(uint256).max and type(uint256).min
  • Won't collide with other sentinels from unrelated contexts*
Sentinel constant RAIN_FLOW_SENTINEL =
    Sentinel.wrap(uint256(keccak256(bytes("RAIN_FLOW_SENTINEL")) | SENTINEL_HIGH_BITS));

Contents

LibFlow

Git Source

Functions

stackToFlow

Converts pointers bounding an evaluated stack to a FlowTransferV1. Works by repeatedly consuming sentinel tuples from the stack, where the tuple size is 4 for ERC20, 4 for ERC721 and 5 for ERC1155. The sentinels are consumed from the stack from top to bottom, so the first sentinels consumed are the ERC20 transfers, followed by the ERC721 transfers and finally the ERC1155 transfers.

function stackToFlow(Pointer stackBottom, Pointer stackTop) internal pure returns (FlowTransferV1 memory);

Parameters

NameTypeDescription
stackBottomPointerThe bottom of the stack.
stackTopPointerThe top of the stack.

Returns

NameTypeDescription
<none>FlowTransferV1The FlowTransferV1 representing the transfers in the stack.

flowERC20

Processes the ERC20 transfers in the flow. Reverts if the from address is not either the msg.sender or the flow contract. Uses IERC20.safeTransferFrom to transfer the tokens to ensure that reverts from the token are respected.

function flowERC20(FlowTransferV1 memory flowTransfer) internal;

Parameters

NameTypeDescription
flowTransferFlowTransferV1The FlowTransferV1 to process. Tokens other than ERC20 tokens are ignored.

flowERC721

Processes the ERC721 transfers in the flow. Reverts if the from address is not either the msg.sender or the flow contract. Uses IERC721.safeTransferFrom to transfer the tokens to ensure that reverts from the token are respected.

function flowERC721(FlowTransferV1 memory flowTransfer) internal;

Parameters

NameTypeDescription
flowTransferFlowTransferV1The FlowTransferV1 to process. Tokens other than ERC721 tokens are ignored.

flowERC1155

Processes the ERC1155 transfers in the flow. Reverts if the from address is not either the msg.sender or the flow contract. Uses IERC1155.safeTransferFrom to transfer the tokens to ensure that reverts from the token are respected.

function flowERC1155(FlowTransferV1 memory flowTransfer) internal;

Parameters

NameTypeDescription
flowTransferFlowTransferV1The FlowTransferV1 to process. Tokens other than ERC1155 tokens are ignored.

flow

Processes a flow transfer. Firstly sets state for the interpreter on the interpreter store. Then processes the ERC20, ERC721 and ERC1155 transfers in the flow. Guarantees ordering of the transfers but DOES NOT prevent reentrancy attacks. This is the responsibility of the caller.

function flow(FlowTransferV1 memory flowTransfer, IInterpreterStoreV1 interpreterStore, uint256[] memory kvs)
    internal;

Parameters

NameTypeDescription
flowTransferFlowTransferV1The FlowTransferV1 to process.
interpreterStoreIInterpreterStoreV1The IInterpreterStoreV1 to set state on.
kvsuint256[]The key value pairs to set on the interpreter store.