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
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
Name | Type | Description |
---|---|---|
flowMinOutputs | uint256 | The min outputs for the flow. |
FlowCommon
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
Name | Type | Description |
---|---|---|
metaHash | bytes32 | As per DeployerDiscoverableMetaV2 . |
config | DeployerDiscoverableMetaV2ConstructionConfig | As 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
Name | Type | Description |
---|---|---|
evaluableConfigs | EvaluableConfigV2[] | The evaluable configs to register at initialization. Each of these represents a flow that defines valid token movements at runtime for the inheriting contract. |
flowMinOutputs | uint256 | The 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The evaluable to evaluate. |
callerContext | uint256[] | The caller context to evaluate the evaluable with. |
signedContexts | SignedContextV1[] | The signed contexts to evaluate the evaluable with. |
Returns
Name | Type | Description |
---|---|---|
<none> | Pointer | The bottom of the stack after evaluation. |
<none> | Pointer | The 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
Name | Type | Description |
---|---|---|
sender | address | The address that registered the flow. |
evaluable | Evaluable | The 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
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
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
Name | Type | Description |
---|---|---|
stack | uint256[] | The stack of values to convert to a flow transfer. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowTransferV1 | flowTransfer 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The evaluable to evaluate. |
callerContext | uint256[] | The caller context to pass to the evaluable. |
signedContexts | SignedContextV1[] | The signed contexts to pass to the evaluable. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowTransferV1 | flowTransfer The resulting flow transfer. |
Constants
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
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
Name | Type | Description |
---|---|---|
stack | uint256[] | The stack to convert to a FlowERC1155IOV1 . |
Returns
Name | Type | Description |
---|---|---|
flowERC1155IO | FlowERC1155IOV1 | The 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC1155IOV1 | flowERC1155IO 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
Name | Type | Description |
---|---|---|
stackBottom | Pointer | The bottom of the stack. |
stackTop | Pointer | The top of the stack. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC1155IOV1 | flowERC1155IO 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
CALLER_META_HASH
The hash of the meta data expected by the FlowCommon
constructor.
bytes32 constant CALLER_META_HASH = bytes32(0x7ea70f837234357ec1bb5b777e04453ebaf3ca778a98805c4bb20a738d559a21);
Contents
FlowERC20
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
Name | Type | Description |
---|---|---|
stack | uint256[] | The stack to convert to a FlowERC20IOV1 . |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC20IOV1 | flowERC20IO 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC20IOV1 | flowERC20IO 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
Name | Type | Description |
---|---|---|
stackBottom | Pointer | The bottom of the stack. |
stackTop | Pointer | The top of the stack. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC20IOV1 | flowERC20IO 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
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
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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC721IOV1 | flowERC721IO 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
Name | Type | Description |
---|---|---|
stackBottom | Pointer | The bottom of the stack. |
stackTop | Pointer | The top of the stack. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC721IOV1 | flowERC721IO 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
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
- deprecated
- unstable
- FlowERC1155Config
- ERC1155SupplyChange
- FlowERC1155IOV1
- IFlowERC1155V3
- IFlowERC1155V3 constants
- ERC20SupplyChange
- FlowERC20IOV1
- FlowERC20Config
- IFlowERC20V3
- IFlowERC20V3 constants
- FlowERC721Config
- ERC721SupplyChange
- FlowERC721IOV1
- IFlowERC721V3
- IFlowERC721V3 constants
- UnregisteredFlow
- UnsupportedNativeFlow
- UnsupportedERC20Flow
- UnsupportedERC721Flow
- UnsupportedERC1155Flow
- FlowConfig
- ERC20Transfer
- ERC721Transfer
- ERC1155Transfer
- FlowTransferV1
- IFlowV3
- IFlowV3 constants
Contents
Contents
- FlowERC1155Config
- ERC1155SupplyChange
- FlowERC1155IO
- IFlowERC1155V1
- ERC20SupplyChange
- FlowERC20IO
- FlowERC20Config
- IFlowERC20V1
- FlowERC721Config
- ERC721SupplyChange
- FlowERC721IO
- IFlowERC721V1
- FlowConfig
- NativeTransfer
- ERC20Transfer
- ERC721Transfer
- ERC1155Transfer
- FlowTransfer
- IFlowV1
FlowERC1155Config
struct FlowERC1155Config {
string uri;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
ERC1155SupplyChange
struct ERC1155SupplyChange {
address account;
uint256 id;
uint256 amount;
}
FlowERC1155IO
struct FlowERC1155IO {
ERC1155SupplyChange[] mints;
ERC1155SupplyChange[] burns;
FlowTransfer flow;
}
IFlowERC1155V1
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
struct ERC20SupplyChange {
address account;
uint256 amount;
}
FlowERC20IO
struct FlowERC20IO {
ERC20SupplyChange[] mints;
ERC20SupplyChange[] burns;
FlowTransfer flow;
}
FlowERC20Config
Constructor config.
struct FlowERC20Config {
string name;
string symbol;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | |
symbol | string | |
evaluableConfig | EvaluableConfig | |
flowConfig | EvaluableConfig[] |
IFlowERC20V1
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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC20Config | All initialized config. |
FlowERC721Config
Constructor config.
struct FlowERC721Config {
string name;
string symbol;
string baseURI;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | |
symbol | string | |
baseURI | string | |
evaluableConfig | EvaluableConfig | |
flowConfig | EvaluableConfig[] |
ERC721SupplyChange
struct ERC721SupplyChange {
address account;
uint256 id;
}
FlowERC721IO
struct FlowERC721IO {
ERC721SupplyChange[] mints;
ERC721SupplyChange[] burns;
FlowTransfer flow;
}
IFlowERC721V1
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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC721Config | All initialized config. |
FlowConfig
struct FlowConfig {
EvaluableConfig dummyConfig;
EvaluableConfig[] config;
}
NativeTransfer
struct NativeTransfer {
address from;
address to;
uint256 amount;
}
ERC20Transfer
struct ERC20Transfer {
address token;
address from;
address to;
uint256 amount;
}
ERC721Transfer
struct ERC721Transfer {
address token;
address from;
address to;
uint256 id;
}
ERC1155Transfer
struct ERC1155Transfer {
address token;
address from;
address to;
uint256 id;
uint256 amount;
}
FlowTransfer
struct FlowTransfer {
NativeTransfer[] native;
ERC20Transfer[] erc20;
ERC721Transfer[] erc721;
ERC1155Transfer[] erc1155;
}
IFlowV1
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
- ERC1155SupplyChange
- FlowERC1155IO
- IFlowERC1155V2
- ERC20SupplyChange
- FlowERC20IO
- FlowERC20Config
- IFlowERC20V2
- FlowERC721Config
- ERC721SupplyChange
- FlowERC721IO
- IFlowERC721V2
- FlowConfig
- NativeTransfer
- ERC20Transfer
- ERC721Transfer
- ERC1155Transfer
- FlowTransfer
- IFlowV2
FlowERC1155Config
struct FlowERC1155Config {
string uri;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
ERC1155SupplyChange
struct ERC1155SupplyChange {
address account;
uint256 id;
uint256 amount;
}
FlowERC1155IO
struct FlowERC1155IO {
ERC1155SupplyChange[] mints;
ERC1155SupplyChange[] burns;
FlowTransfer flow;
}
IFlowERC1155V2
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
struct ERC20SupplyChange {
address account;
uint256 amount;
}
FlowERC20IO
struct FlowERC20IO {
ERC20SupplyChange[] mints;
ERC20SupplyChange[] burns;
FlowTransfer flow;
}
FlowERC20Config
Constructor config.
struct FlowERC20Config {
string name;
string symbol;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | |
symbol | string | |
evaluableConfig | EvaluableConfig | |
flowConfig | EvaluableConfig[] |
IFlowERC20V2
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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC20Config | All initialized config. |
FlowERC721Config
Constructor config.
struct FlowERC721Config {
string name;
string symbol;
string baseURI;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | |
symbol | string | |
baseURI | string | |
evaluableConfig | EvaluableConfig | |
flowConfig | EvaluableConfig[] |
ERC721SupplyChange
struct ERC721SupplyChange {
address account;
uint256 id;
}
FlowERC721IO
struct FlowERC721IO {
ERC721SupplyChange[] mints;
ERC721SupplyChange[] burns;
FlowTransfer flow;
}
IFlowERC721V2
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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC721Config | All initialized config. |
FlowConfig
struct FlowConfig {
EvaluableConfig dummyConfig;
EvaluableConfig[] config;
}
NativeTransfer
struct NativeTransfer {
address from;
address to;
uint256 amount;
}
ERC20Transfer
struct ERC20Transfer {
address token;
address from;
address to;
uint256 amount;
}
ERC721Transfer
struct ERC721Transfer {
address token;
address from;
address to;
uint256 id;
}
ERC1155Transfer
struct ERC1155Transfer {
address token;
address from;
address to;
uint256 id;
uint256 amount;
}
FlowTransfer
struct FlowTransfer {
NativeTransfer[] native;
ERC20Transfer[] erc20;
ERC721Transfer[] erc721;
ERC1155Transfer[] erc1155;
}
IFlowV2
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
- IFlowERC1155V4
- FlowERC20ConfigV2
- IFlowERC20V4
- BurnerNotOwner
- FlowERC721ConfigV2
- IFlowERC721V4
- IFlowV4
FlowERC1155ConfigV2
Initialization config.
struct FlowERC1155ConfigV2 {
string uri;
EvaluableConfigV2 evaluableConfig;
EvaluableConfigV2[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
uri | string | As per Open Zeppelin ERC1155Upgradeable . |
evaluableConfig | EvaluableConfigV2 | The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers. |
flowConfig | EvaluableConfigV2[] | Initialization config for the Evaluable s that define the flow behaviours outside self mints/burns. |
IFlowERC1155V4
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
Name | Type | Description |
---|---|---|
stack | uint256[] | The stack to convert to a FlowERC1155IOV1 . |
Returns
Name | Type | Description |
---|---|---|
flowERC1155IO | FlowERC1155IOV1 | The 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowERC1155IOV1 | flowERC1155IO 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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC1155ConfigV2 | All initialized config. |
FlowERC20ConfigV2
Initialization config.
struct FlowERC20ConfigV2 {
string name;
string symbol;
EvaluableConfigV2 evaluableConfig;
EvaluableConfigV2[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | As per Open Zeppelin ERC20Upgradeable . |
symbol | string | As per Open Zeppelin ERC20Upgradeable . |
evaluableConfig | EvaluableConfigV2 | The EvaluableConfigV2 to use to build the evaluable that can be used to evaluate handleTransfer . |
flowConfig | EvaluableConfigV2[] | The EvaluableConfigV2[] to use to build the evaluable s for all the flows, including self minting and burning. |
IFlowERC20V4
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
Name | Type | Description |
---|---|---|
stack | uint256[] | The stack to convert to a FlowERC20IOV1 . |
Returns
Name | Type | Description |
---|---|---|
flowERC20IO | FlowERC20IOV1 | 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 calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
external
returns (FlowERC20IOV1 calldata flowERC20IO);
Parameters
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
flowERC20IO | FlowERC20IOV1 | The 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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC20ConfigV2 | All initialized config. |
BurnerNotOwner
Thrown when burner of tokens is not the owner of tokens.
error BurnerNotOwner();
FlowERC721ConfigV2
Initialization config.
struct FlowERC721ConfigV2 {
string name;
string symbol;
string baseURI;
EvaluableConfigV2 evaluableConfig;
EvaluableConfigV2[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | As per Open Zeppelin ERC721Upgradeable . |
symbol | string | As per Open Zeppelin ERC721Upgradeable . |
baseURI | string | As per Open Zeppelin ERC721Upgradeable . |
evaluableConfig | EvaluableConfigV2 | The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers and build token IDs for the token URI. |
flowConfig | EvaluableConfigV2[] | Initialization config for the Evaluable s that define the flow behaviours outside self mints/burns. |
IFlowERC721V4
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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
flowERC721IO | FlowERC721IOV1 | The 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
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC721ConfigV2 | All initialized config. |
IFlowV4
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 withUnregisteredFlow
. 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
Name | Type | Description |
---|---|---|
stack | uint256[] | The stack of values to convert to a flow transfer. |
Returns
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | 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 calldata evaluable, uint256[] calldata callerContext, SignedContextV1[] calldata signedContexts)
external
returns (FlowTransferV1 calldata flowTransfer);
Parameters
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The evaluable to evaluate. |
callerContext | uint256[] | The caller context to pass to the evaluable. |
signedContexts | SignedContextV1[] | The signed contexts to pass to the evaluable. |
Returns
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | The resulting flow transfer. |
Events
Initialize
MUST be emitted when the flow contract is initialized.
event Initialize(address sender, EvaluableConfigV2[] config);
Parameters
Name | Type | Description |
---|---|---|
sender | address | The EOA that deployed the flow contract. |
config | EvaluableConfigV2[] | The list of evaluable configs that define the flows. |
FlowERC1155Config
Initializer config.
struct FlowERC1155Config {
string uri;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
uri | string | As per Open Zeppelin ERC1155Upgradeable . |
evaluableConfig | EvaluableConfig | The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers. |
flowConfig | EvaluableConfig[] | Constructor config for the Evaluable s that define the flow behaviours including self mints/burns. |
ERC1155SupplyChange
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
Name | Type | Description |
---|---|---|
account | address | The address the token is being minted/burned to/from. |
id | uint256 | The id of the token being minted/burned. |
amount | uint256 | The amount of the token being minted/burned. |
FlowERC1155IOV1
Represents a set of ERC1155 transfers, including self mints/burns.
struct FlowERC1155IOV1 {
ERC1155SupplyChange[] mints;
ERC1155SupplyChange[] burns;
FlowTransferV1 flow;
}
Properties
Name | Type | Description |
---|---|---|
mints | ERC1155SupplyChange[] | The mints that occurred. |
burns | ERC1155SupplyChange[] | The burns that occurred. |
flow | FlowTransferV1 | The transfers that occured. |
IFlowERC1155V3
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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable that is flowing. |
callerContext | uint256[] | The context of the caller. |
signedContexts | SignedContextV1[] | The signed contexts of the caller. |
Returns
Name | Type | Description |
---|---|---|
flowERC1155IO | FlowERC1155IOV1 | The 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable that is flowing. |
callerContext | uint256[] | The context of the caller. |
signedContexts | SignedContextV1[] | The signed contexts of the caller. |
Returns
Name | Type | Description |
---|---|---|
flowERC1155IO | FlowERC1155IOV1 | The FlowERC1155IOV1 that occurred. |
Events
Initialize
Contract has initialized.
event Initialize(address sender, FlowERC1155Config config);
Constants
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
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
Name | Type | Description |
---|---|---|
account | address | The address the token is being minted/burned to/from. |
amount | uint256 | The amount of the token being minted/burned. |
FlowERC20IOV1
Represents a set of ERC20 transfers, including self mints/burns.
struct FlowERC20IOV1 {
ERC20SupplyChange[] mints;
ERC20SupplyChange[] burns;
FlowTransferV1 flow;
}
Properties
Name | Type | Description |
---|---|---|
mints | ERC20SupplyChange[] | The mints that occurred. |
burns | ERC20SupplyChange[] | The burns that occurred. |
flow | FlowTransferV1 | The transfers that occured. |
FlowERC20Config
Initializer config.
struct FlowERC20Config {
string name;
string symbol;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | As per Open Zeppelin ERC20Upgradeable . |
symbol | string | As per Open Zeppelin ERC20Upgradeable . |
evaluableConfig | EvaluableConfig | The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers. |
flowConfig | EvaluableConfig[] | Initializer config for the Evaluable s that define the flow behaviours including self mints/burns. |
IFlowERC20V3
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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
flowERC20IO | FlowERC20IOV1 | The 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable to use to evaluate the flow. |
callerContext | uint256[] | The caller context to use to evaluate the flow. |
signedContexts | SignedContextV1[] | The signed contexts to use to evaluate the flow. |
Returns
Name | Type | Description |
---|---|---|
flowERC20IO | FlowERC20IOV1 | The FlowERC20IOV1 that occurred. |
Events
Initialize
Contract has initialized.
event Initialize(address sender, FlowERC20Config config);
Parameters
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC20Config | All initialized config. |
Constants
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
Initializer config.
struct FlowERC721Config {
string name;
string symbol;
string baseURI;
EvaluableConfig evaluableConfig;
EvaluableConfig[] flowConfig;
}
Properties
Name | Type | Description |
---|---|---|
name | string | As per Open Zeppelin ERC721Upgradeable . |
symbol | string | As per Open Zeppelin ERC721Upgradeable . |
baseURI | string | As per Open Zeppelin ERC721Upgradeable . |
evaluableConfig | EvaluableConfig | The EvaluableConfigV2 to use to build the evaluable that can be used to handle transfers and token URIs. The token URI entrypoint is optional. |
flowConfig | EvaluableConfig[] | Constructor config for the Evaluable s that define the flow behaviours including self mints/burns. |
ERC721SupplyChange
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
Name | Type | Description |
---|---|---|
account | address | The address the token is being minted/burned to/from. |
id | uint256 | The id of the token being minted/burned. |
FlowERC721IOV1
Represents a set of ERC721 transfers, including self mints/burns.
struct FlowERC721IOV1 {
ERC721SupplyChange[] mints;
ERC721SupplyChange[] burns;
FlowTransferV1 flow;
}
Properties
Name | Type | Description |
---|---|---|
mints | ERC721SupplyChange[] | The mints that occurred. |
burns | ERC721SupplyChange[] | The burns that occurred. |
flow | FlowTransferV1 | The transfers that occured. |
IFlowERC721V3
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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable that is flowing. |
callerContext | uint256[] | The context of the caller. |
signedContexts | SignedContextV1[] | The signed contexts of the caller. |
Returns
Name | Type | Description |
---|---|---|
flowERC721IO | FlowERC721IOV1 | The 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The Evaluable that is flowing. |
callerContext | uint256[] | The context of the caller. |
signedContexts | SignedContextV1[] | The signed contexts of the caller. |
Returns
Name | Type | Description |
---|---|---|
flowERC721IO | FlowERC721IOV1 | The FlowERC721IOV1 that occurred. |
Events
Initialize
Contract has initialized.
event Initialize(address sender, FlowERC721Config config);
Parameters
Name | Type | Description |
---|---|---|
sender | address | msg.sender initializing the contract (factory). |
config | FlowERC721Config | All initialized config. |
Constants
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
Thrown when the flow being evaluated is unregistered.
error UnregisteredFlow(bytes32 unregisteredHash);
Parameters
Name | Type | Description |
---|---|---|
unregisteredHash | bytes32 | Hash of the unregistered flow. |
UnsupportedNativeFlow
Thrown for unsupported native transfers.
error UnsupportedNativeFlow();
UnsupportedERC20Flow
Thrown for unsupported erc20 transfers.
error UnsupportedERC20Flow();
UnsupportedERC721Flow
Thrown for unsupported erc721 transfers.
error UnsupportedERC721Flow();
UnsupportedERC1155Flow
Thrown for unsupported erc1155 transfers.
error UnsupportedERC1155Flow();
FlowConfig
Wraps EvaluableConfig[]
to workaround a Solidity bug.
https://github.com/ethereum/solidity/issues/13597
struct FlowConfig {
EvaluableConfig dummyConfig;
EvaluableConfig[] config;
}
Properties
Name | Type | Description |
---|---|---|
dummyConfig | EvaluableConfig | A dummy config to workaround a Solidity bug. |
config | EvaluableConfig[] | The list of evaluable configs that define the flows. |
ERC20Transfer
Represents a single transfer of a single ERC20 token.
struct ERC20Transfer {
address token;
address from;
address to;
uint256 amount;
}
Properties
Name | Type | Description |
---|---|---|
token | address | The address of the ERC20 token being transferred. |
from | address | The address the token is being transferred from. |
to | address | The address the token is being transferred to. |
amount | uint256 | The amount of the token being transferred. |
ERC721Transfer
Represents a single transfer of a single ERC721 token.
struct ERC721Transfer {
address token;
address from;
address to;
uint256 id;
}
Properties
Name | Type | Description |
---|---|---|
token | address | The address of the ERC721 token being transferred. |
from | address | The address the token is being transferred from. |
to | address | The address the token is being transferred to. |
id | uint256 | The id of the token being transferred. |
ERC1155Transfer
Represents a single transfer of a single ERC1155 token.
struct ERC1155Transfer {
address token;
address from;
address to;
uint256 id;
uint256 amount;
}
Properties
Name | Type | Description |
---|---|---|
token | address | The address of the ERC1155 token being transferred. |
from | address | The address the token is being transferred from. |
to | address | The address the token is being transferred to. |
id | uint256 | The id of the token being transferred. |
amount | uint256 | The amount of the token being transferred. |
FlowTransferV1
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
Name | Type | Description |
---|---|---|
erc20 | ERC20Transfer[] | An array of ERC20 transfers. |
erc721 | ERC721Transfer[] | An array of ERC721 transfers. |
erc1155 | ERC1155Transfer[] | An array of ERC1155 transfers. |
IFlowV3
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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The evaluable to evaluate. |
callerContext | uint256[] | The caller context to use when evaluating the flow. |
signedContexts | SignedContextV1[] | 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
Name | Type | Description |
---|---|---|
evaluable | Evaluable | The evaluable to evaluate. |
callerContext | uint256[] | The caller context to pass to the evaluable. |
signedContexts | SignedContextV1[] | The signed contexts to pass to the evaluable. |
Returns
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | The resulting flow transfer. |
Events
Initialize
MUST be emitted when the flow contract is initialized.
event Initialize(address sender, FlowConfig config);
Parameters
Name | Type | Description |
---|---|---|
sender | address | The EOA that deployed the flow contract. |
config | FlowConfig | The list of evaluable configs that define the flows. |
Constants
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
andtype(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
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
Name | Type | Description |
---|---|---|
stackBottom | Pointer | The bottom of the stack. |
stackTop | Pointer | The top of the stack. |
Returns
Name | Type | Description |
---|---|---|
<none> | FlowTransferV1 | The 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
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | The 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
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | The 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
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | The 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
Name | Type | Description |
---|---|---|
flowTransfer | FlowTransferV1 | The FlowTransferV1 to process. |
interpreterStore | IInterpreterStoreV1 | The IInterpreterStoreV1 to set state on. |
kvs | uint256[] | The key value pairs to set on the interpreter store. |