diff --git a/src/interface/deprecated/v3/IFlowERC1155V3.sol b/src/interface/deprecated/v3/IFlowERC1155V3.sol new file mode 100644 index 00000000..1ec09470 --- /dev/null +++ b/src/interface/deprecated/v3/IFlowERC1155V3.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; + +import "./IFlowV3.sol"; + +/// @dev Entrypont of the `handleTransfer` evaluation. +SourceIndex constant FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT = SourceIndex.wrap(0); + +/// @dev 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; + +/// @dev 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; + +/// @dev 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; + +/// @dev 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)); + +/// Initializer config. +/// @param uri As per Open Zeppelin `ERC1155Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to handle transfers. +/// @param flowConfig Constructor config for the `Evaluable`s that define the +/// flow behaviours including self mints/burns. +struct FlowERC1155Config { + string uri; + EvaluableConfig evaluableConfig; + EvaluableConfig[] flowConfig; +} + +/// Represents a single mint or burn of a single ERC1155 token. Whether this is +/// a mint or burn must be implied by the context. +/// @param account The address the token is being minted/burned to/from. +/// @param id The id of the token being minted/burned. +/// @param amount The amount of the token being minted/burned. +struct ERC1155SupplyChange { + address account; + uint256 id; + uint256 amount; +} + +/// Represents a set of ERC1155 transfers, including self mints/burns. +/// @param mints The mints that occurred. +/// @param burns The burns that occurred. +/// @param flow The transfers that occured. +struct FlowERC1155IOV1 { + ERC1155SupplyChange[] mints; + ERC1155SupplyChange[] burns; + FlowTransferV1 flow; +} + +/// @title IFlowERC1155V3 +/// Conceptually identical to `IFlowV3`, but the flow contract itself is an +/// ERC1155 token. This means that ERC1155 self mints and burns are included in +/// the stack that the flows must evaluate to. As stacks are processed by flow +/// from bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC1155 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// Otherwise the flow contract is identical to `IFlowV3`. +interface IFlowERC1155V3 { + /// Contract has initialized. + event Initialize(address sender, FlowERC1155Config config); + + /// As per `IFlowV3` but returns a `FlowERC1155IOV1` instead of a + /// `FlowTransferV1`. + /// @param evaluable The `Evaluable` that is flowing. + /// @param callerContext The context of the caller. + /// @param signedContexts The signed contexts of the caller. + /// @return flowERC1155IO The `FlowERC1155IOV1` that occurred. + function previewFlow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external view returns (FlowERC1155IOV1 calldata flowERC1155IO); + + /// As per `IFlowV3` but returns a `FlowERC1155IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC1155 accordingly. + /// @param evaluable The `Evaluable` that is flowing. + /// @param callerContext The context of the caller. + /// @param signedContexts The signed contexts of the caller. + /// @return flowERC1155IO The `FlowERC1155IOV1` that occurred. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC1155IOV1 calldata flowERC1155IO); +} diff --git a/src/interface/deprecated/v3/IFlowERC20V3.sol b/src/interface/deprecated/v3/IFlowERC20V3.sol new file mode 100644 index 00000000..bb4d93ef --- /dev/null +++ b/src/interface/deprecated/v3/IFlowERC20V3.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import {MIN_FLOW_SENTINELS, SENTINEL_HIGH_BITS, FlowTransferV1} from "./IFlowV3.sol"; + +/// @dev 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)); + +/// @dev Entrypont of the `handleTransfer` evaluation. +SourceIndex constant FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT = SourceIndex.wrap(0); + +/// @dev 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; + +/// @dev 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; + +/// @dev 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; + +/// Represents a single mint or burn of a single ERC20 token. Whether this is +/// a mint or burn must be implied by the context. +/// @param account The address the token is being minted/burned to/from. +/// @param amount The amount of the token being minted/burned. +struct ERC20SupplyChange { + address account; + uint256 amount; +} + +/// Represents a set of ERC20 transfers, including self mints/burns. +/// @param mints The mints that occurred. +/// @param burns The burns that occurred. +/// @param flow The transfers that occured. +struct FlowERC20IOV1 { + ERC20SupplyChange[] mints; + ERC20SupplyChange[] burns; + FlowTransferV1 flow; +} + +/// Initializer config. +/// @param name As per Open Zeppelin `ERC20Upgradeable`. +/// @param symbol As per Open Zeppelin `ERC20Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to handle transfers. +/// @param flowConfig Initializer config for the `Evaluable`s that define the +/// flow behaviours including self mints/burns. +struct FlowERC20Config { + string name; + string symbol; + EvaluableConfig evaluableConfig; + EvaluableConfig[] flowConfig; +} + +/// @title IFlowERC20V3 +/// @notice 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. +interface IFlowERC20V3 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC20Config config); + + /// As per `IFlowV3` but returns a `FlowERC20IOV1` instead of a + /// `FlowTransferV1`. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC20IO The `FlowERC20IOV1` that occurred. + function previewFlow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external view returns (FlowERC20IOV1 calldata flowERC20IO); + + /// As per `IFlowV3` but returns a `FlowERC20IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC20 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC20IO The `FlowERC20IOV1` that occurred. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC20IOV1 calldata flowERC20IO); +} diff --git a/src/interface/deprecated/v3/IFlowERC721V3.sol b/src/interface/deprecated/v3/IFlowERC721V3.sol new file mode 100644 index 00000000..0af859da --- /dev/null +++ b/src/interface/deprecated/v3/IFlowERC721V3.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; + +import "./IFlowV3.sol"; + +/// @dev Entrypont of the `handleTransfer` evaluation. +SourceIndex constant FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT = SourceIndex.wrap(0); +/// @dev Entrypont of the `tokenURI` evaluation. +SourceIndex constant FLOW_ERC721_TOKEN_URI_ENTRYPOINT = SourceIndex.wrap(1); + +/// @dev 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; +/// @dev 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; + +/// @dev 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; +/// @dev 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; + +/// @dev 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)); + +/// @dev 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; + +/// Initializer config. +/// @param name As per Open Zeppelin `ERC721Upgradeable`. +/// @param symbol As per Open Zeppelin `ERC721Upgradeable`. +/// @param baseURI As per Open Zeppelin `ERC721Upgradeable`. +/// @param 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. +/// @param flowConfig Constructor config for the `Evaluable`s that define the +/// flow behaviours including self mints/burns. +struct FlowERC721Config { + string name; + string symbol; + string baseURI; + EvaluableConfig evaluableConfig; + EvaluableConfig[] flowConfig; +} + +/// Represents a single mint or burn of a single ERC721 token. Whether this is +/// a mint or burn must be implied by the context. +/// @param account The address the token is being minted/burned to/from. +/// @param id The id of the token being minted/burned. +struct ERC721SupplyChange { + address account; + uint256 id; +} + +/// Represents a set of ERC721 transfers, including self mints/burns. +/// @param mints The mints that occurred. +/// @param burns The burns that occurred. +/// @param flow The transfers that occured. +struct FlowERC721IOV1 { + ERC721SupplyChange[] mints; + ERC721SupplyChange[] burns; + FlowTransferV1 flow; +} + +/// @title IFlowERC721V3 +/// Conceptually identical to `IFlowV3`, but the flow contract itself is an +/// ERC721 token. This means that ERC721 self mints and burns are included in +/// the stack. +/// +/// As the flow is an ERC721 token, there are two entrypoints in addition to +/// the flows: +/// - `handleTransfer` is called when the flow is transferred. +/// - `tokenURI` is called when the token URI is requested. +/// +/// The `handleTransfer` entrypoint is mandatory, but the `tokenURI` entrypoint +/// is optional. If the `tokenURI` entrypoint is not provided, the default +/// Open Zeppelin implementation will be used. +/// +/// The `handleTransfer` entrypoint may be used to restrict transfers of the +/// flow token. For example, it may be used to restrict transfers to only +/// occur when the flow is in a certain state. +/// +/// The `tokenURI` entrypoint may be used to provide a custom token ID to build +/// a token URI for the flow token. +/// +/// Otherwise the flow contract behaves identically to `IFlowV3`. +interface IFlowERC721V3 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC721Config config); + + /// As per `IFlowV3` but returns a `FlowERC721IOV1` instead of a + /// `FlowTransferV1`. + /// @param evaluable The `Evaluable` that is flowing. + /// @param callerContext The context of the caller. + /// @param signedContexts The signed contexts of the caller. + /// @return flowERC721IO The `FlowERC721IOV1` that would occur if the flow + /// was executed. + function previewFlow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external view returns (FlowERC721IOV1 calldata flowERC721IO); + + /// As per `IFlowV3` but returns a `FlowERC721IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC721 accordingly. + /// @param evaluable The `Evaluable` that is flowing. + /// @param callerContext The context of the caller. + /// @param signedContexts The signed contexts of the caller. + /// @return flowERC721IO The `FlowERC721IOV1` that occurred. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC721IOV1 calldata flowERC721IO); +} diff --git a/src/interface/deprecated/v3/IFlowV3.sol b/src/interface/deprecated/v3/IFlowV3.sol new file mode 100644 index 00000000..6fef197d --- /dev/null +++ b/src/interface/deprecated/v3/IFlowV3.sol @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SourceIndex} from "rain.interpreter.interface/interface/deprecated/IInterpreterV1.sol"; +import {Evaluable, EvaluableConfig} from "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; + +/// Thrown when the flow being evaluated is unregistered. +/// @param unregisteredHash Hash of the unregistered flow. +error UnregisteredFlow(bytes32 unregisteredHash); + +/// Thrown for unsupported native transfers. +error UnsupportedNativeFlow(); + +/// Thrown for unsupported erc20 transfers. +error UnsupportedERC20Flow(); + +/// Thrown for unsupported erc721 transfers. +error UnsupportedERC721Flow(); + +/// Thrown for unsupported erc1155 transfers. +error UnsupportedERC1155Flow(); + +/// @dev The number of sentinels required by `FlowCommon`. An evaluable can never +/// have fewer minimum outputs than required sentinels. +uint256 constant MIN_FLOW_SENTINELS = 3; + +/// @dev 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); + +/// @dev 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)); + +/// Wraps `EvaluableConfig[]` to workaround a Solidity bug. +/// https://github.com/ethereum/solidity/issues/13597 +/// @param dummyConfig A dummy config to workaround a Solidity bug. +/// @param config The list of evaluable configs that define the flows. +struct FlowConfig { + EvaluableConfig dummyConfig; + EvaluableConfig[] config; +} + +/// Represents a single transfer of a single ERC20 token. +/// @param token The address of the ERC20 token being transferred. +/// @param from The address the token is being transferred from. +/// @param to The address the token is being transferred to. +/// @param amount The amount of the token being transferred. +struct ERC20Transfer { + address token; + address from; + address to; + uint256 amount; +} + +/// Represents a single transfer of a single ERC721 token. +/// @param token The address of the ERC721 token being transferred. +/// @param from The address the token is being transferred from. +/// @param to The address the token is being transferred to. +/// @param id The id of the token being transferred. +struct ERC721Transfer { + address token; + address from; + address to; + uint256 id; +} + +/// Represents a single transfer of a single ERC1155 token. +/// @param token The address of the ERC1155 token being transferred. +/// @param from The address the token is being transferred from. +/// @param to The address the token is being transferred to. +/// @param id The id of the token being transferred. +/// @param amount The amount of the token being transferred. +struct ERC1155Transfer { + address token; + address from; + address to; + uint256 id; + uint256 amount; +} + +/// Represents an ordered set of transfers that will be or have been executed. +/// Supports ERC20, ERC721, and ERC1155 transfers. +/// @param erc20 An array of ERC20 transfers. +/// @param erc721 An array of ERC721 transfers. +/// @param erc1155 An array of ERC1155 transfers. +struct FlowTransferV1 { + ERC20Transfer[] erc20; + ERC721Transfer[] erc721; + ERC1155Transfer[] erc1155; +} + +/// @title IFlowV3 +/// At a high level, identical to `IFlowV4` but with an older, less flexible +/// previewing system, and the older `FlowConfig` struct that was used with +/// older versions of the interpreter. +interface IFlowV3 { + /// MUST be emitted when the flow contract is initialized. + /// @param sender The EOA that deployed the flow contract. + /// @param config The list of evaluable configs that define the flows. + event Initialize(address sender, FlowConfig config); + + /// "Dry run" of a flow, returning the resulting token transfers without + /// actually executing them. + /// @param evaluable The evaluable to evaluate. + /// @param callerContext The caller context to use when evaluating the + /// flow. + /// @param signedContexts The signed contexts to use when evaluating the + /// flow. + function previewFlow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external view returns (FlowTransferV1 calldata flowTransfer); + + /// 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). + /// @param evaluable The evaluable to evaluate. + /// @param callerContext The caller context to pass to the evaluable. + /// @param signedContexts The signed contexts to pass to the evaluable. + /// @return flowTransfer The resulting flow transfer. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowTransferV1 calldata flowTransfer); +} diff --git a/src/interface/deprecated/v4/IFlowERC1155V4.sol b/src/interface/deprecated/v4/IFlowERC1155V4.sol new file mode 100644 index 00000000..4c2cdcb1 --- /dev/null +++ b/src/interface/deprecated/v4/IFlowERC1155V4.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1, EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; + +import { + FlowERC1155IOV1, + ERC1155SupplyChange, + FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT, + FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC1155_MIN_FLOW_SENTINELS +} from "../IFlowERC1155V3.sol"; + +/// Initialization config. +/// @param uri As per Open Zeppelin `ERC1155Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to handle transfers. +/// @param flowConfig Initialization config for the `Evaluable`s that define the +/// flow behaviours outside self mints/burns. +struct FlowERC1155ConfigV2 { + string uri; + EvaluableConfigV3 evaluableConfig; + EvaluableConfigV3[] flowConfig; +} + +/// @title IFlowERC1155V4 +/// Conceptually identical to `IFlowV4`, but the flow contract itself is an +/// ERC1155 token. This means that ERC1155 self mints and burns are included in +/// the stack that the flows must evaluate to. As stacks are processed by flow +/// from bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC1155 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// Otherwise the flow contract is identical to `IFlowV4`. +interface IFlowERC1155V4 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC1155ConfigV2 config); + + /// As per `IFlowV4` but returns a `FlowERC1155IOV1` instead of a + /// `FlowTransferV1`. + /// @param stack The stack to convert to a `FlowERC1155IOV1`. + /// @return flowERC1155IO The `FlowERC1155IOV1` representation of the stack. + function stackToFlow(uint256[] memory stack) external pure returns (FlowERC1155IOV1 memory flowERC1155IO); + + /// As per `IFlowV4` but returns a `FlowERC1155IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC1155 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC1155IO The `FlowERC1155IOV1` representing all token + /// mint/burns and transfers that occurred during the flow. + function flow( + EvaluableV2 calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC1155IOV1 calldata); +} diff --git a/src/interface/deprecated/v4/IFlowERC20V4.sol b/src/interface/deprecated/v4/IFlowERC20V4.sol new file mode 100644 index 00000000..728e093a --- /dev/null +++ b/src/interface/deprecated/v4/IFlowERC20V4.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import { + FlowERC20IOV1, + ERC20SupplyChange, + FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT, + FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC20_MIN_FLOW_SENTINELS +} from "../IFlowERC20V3.sol"; +import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; + +/// Initialization config. +/// @param name As per Open Zeppelin `ERC20Upgradeable`. +/// @param symbol As per Open Zeppelin `ERC20Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to evaluate `handleTransfer`. +/// @param flowConfig The `EvaluableConfigV2[]` to use to build the +/// `evaluable`s for all the flows, including self minting and burning. +struct FlowERC20ConfigV2 { + string name; + string symbol; + EvaluableConfigV2 evaluableConfig; + EvaluableConfigV2[] flowConfig; +} + +/// @title IFlowERC20V4 +/// Conceptually identical to `IFlowV4`, but the flow contract itself is an +/// ERC20 token. This means that ERC20 self mints and burns are included in the +/// stack that the flows must evaluate to. As stacks are processed by flow from +/// bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC20 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// Otherwise the flow contract is identical to `IFlowV4`. +interface IFlowERC20V4 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC20ConfigV2 config); + + /// As per `IFlowV4` but returns a `FlowERC20IOV1` instead of a + /// `FlowTransferV1`. + /// @param stack The stack to convert to a `FlowERC20IOV1`. + /// @return flowERC20IO The `FlowERC20IOV1` representation of the stack. + function stackToFlow(uint256[] memory stack) external pure returns (FlowERC20IOV1 memory flowERC20IO); + + /// As per `IFlowV4` but returns a `FlowERC20IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC20 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC20IO The `FlowERC20IOV1` representing all token mint/burns + /// and transfers that occurred during the flow. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC20IOV1 calldata flowERC20IO); +} diff --git a/src/interface/deprecated/v4/IFlowERC721V4.sol b/src/interface/deprecated/v4/IFlowERC721V4.sol new file mode 100644 index 00000000..d76a94a2 --- /dev/null +++ b/src/interface/deprecated/v4/IFlowERC721V4.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; + +import { + FlowERC721IOV1, + ERC721SupplyChange, + FLOW_ERC721_TOKEN_URI_MIN_OUTPUTS, + FLOW_ERC721_TOKEN_URI_MAX_OUTPUTS, + FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC721_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC721_TOKEN_URI_ENTRYPOINT, + FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT, + FLOW_ERC721_MIN_FLOW_SENTINELS +} from "../IFlowERC721V3.sol"; + +import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; + +/// Thrown when burner of tokens is not the owner of tokens. +error BurnerNotOwner(); + +/// Initialization config. +/// @param name As per Open Zeppelin `ERC721Upgradeable`. +/// @param symbol As per Open Zeppelin `ERC721Upgradeable`. +/// @param baseURI As per Open Zeppelin `ERC721Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to handle transfers and build token IDs for the +/// token URI. +/// @param flowConfig Initialization config for the `Evaluable`s that define the +/// flow behaviours outside self mints/burns. +struct FlowERC721ConfigV2 { + string name; + string symbol; + string baseURI; + EvaluableConfigV2 evaluableConfig; + EvaluableConfigV2[] flowConfig; +} + +/// @title IFlowERC721V4 +/// Conceptually identical to `IFlowV4`, but the flow contract itself is an +/// ERC721 token. This means that ERC721 self mints and burns are included in the +/// stack that the flows must evaluate to. As stacks are processed by flow from +/// bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC721 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// The flow contract also includes an evaluation to be run on every token URI +/// request. This is the `tokenURI` entrypoint. The return value of this +/// evaluation is the token ID to use for the token URI. This entryoint is +/// optional, and if not provided the token URI will be the default Open Zeppelin +/// token URI logic. +/// +/// Otherwise the flow contract is identical to `IFlowV4`. +interface IFlowERC721V4 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC721ConfigV2 config); + + /// As per `IFlowV4` but returns a `FlowERC721IOV1` instead of a + /// `FlowTransferV1`. + function stackToFlow(uint256[] memory stack) external pure returns (FlowERC721IOV1 memory flowERC721IO); + + /// As per `IFlowV4` but returns a `FlowERC721IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC721 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC721IO The `FlowERC721IOV1` representing all token + /// mint/burns and transfers that occurred during the flow. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC721IOV1 calldata flowERC721IO); +} diff --git a/src/interface/deprecated/v4/IFlowV4.sol b/src/interface/deprecated/v4/IFlowV4.sol new file mode 100644 index 00000000..27998b5e --- /dev/null +++ b/src/interface/deprecated/v4/IFlowV4.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1, EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import {Pointer} from "rain.solmem/lib/LibPointer.sol"; + +import { + FlowTransferV1, + ERC20Transfer, + ERC721Transfer, + ERC1155Transfer, + RAIN_FLOW_SENTINEL, + UnregisteredFlow, + UnsupportedERC20Flow, + UnsupportedERC721Flow, + UnsupportedERC1155Flow, + MIN_FLOW_SENTINELS +} from "../IFlowV3.sol"; + +/// @title IFlowV4 +/// @notice 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`. +interface IFlowV4 { + /// MUST be emitted when the flow contract is initialized. + /// @param sender The EOA that deployed the flow contract. + /// @param config The list of evaluable configs that define the flows. + event Initialize(address sender, EvaluableConfigV3[] config); + + /// 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. + /// @param stack The stack of values to convert to a flow transfer. + /// @return flowTransfer The resulting flow transfer. + function stackToFlow(uint256[] memory stack) external pure returns (FlowTransferV1 calldata flowTransfer); + + /// 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). + /// @param evaluable The evaluable to evaluate. + /// @param callerContext The caller context to pass to the evaluable. + /// @param signedContexts The signed contexts to pass to the evaluable. + /// @return flowTransfer The resulting flow transfer. + function flow( + EvaluableV2 calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowTransferV1 calldata flowTransfer); +} diff --git a/src/interface/unstable/IFlowERC1155V5.sol b/src/interface/unstable/IFlowERC1155V5.sol new file mode 100644 index 00000000..4c2cdcb1 --- /dev/null +++ b/src/interface/unstable/IFlowERC1155V5.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1, EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; + +import { + FlowERC1155IOV1, + ERC1155SupplyChange, + FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT, + FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC1155_MIN_FLOW_SENTINELS +} from "../IFlowERC1155V3.sol"; + +/// Initialization config. +/// @param uri As per Open Zeppelin `ERC1155Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to handle transfers. +/// @param flowConfig Initialization config for the `Evaluable`s that define the +/// flow behaviours outside self mints/burns. +struct FlowERC1155ConfigV2 { + string uri; + EvaluableConfigV3 evaluableConfig; + EvaluableConfigV3[] flowConfig; +} + +/// @title IFlowERC1155V4 +/// Conceptually identical to `IFlowV4`, but the flow contract itself is an +/// ERC1155 token. This means that ERC1155 self mints and burns are included in +/// the stack that the flows must evaluate to. As stacks are processed by flow +/// from bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC1155 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// Otherwise the flow contract is identical to `IFlowV4`. +interface IFlowERC1155V4 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC1155ConfigV2 config); + + /// As per `IFlowV4` but returns a `FlowERC1155IOV1` instead of a + /// `FlowTransferV1`. + /// @param stack The stack to convert to a `FlowERC1155IOV1`. + /// @return flowERC1155IO The `FlowERC1155IOV1` representation of the stack. + function stackToFlow(uint256[] memory stack) external pure returns (FlowERC1155IOV1 memory flowERC1155IO); + + /// As per `IFlowV4` but returns a `FlowERC1155IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC1155 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC1155IO The `FlowERC1155IOV1` representing all token + /// mint/burns and transfers that occurred during the flow. + function flow( + EvaluableV2 calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC1155IOV1 calldata); +} diff --git a/src/interface/unstable/IFlowERC20V5.sol b/src/interface/unstable/IFlowERC20V5.sol new file mode 100644 index 00000000..190c1cd4 --- /dev/null +++ b/src/interface/unstable/IFlowERC20V5.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import { + FlowERC20IOV1, + ERC20SupplyChange, + FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT, + FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC20_MIN_FLOW_SENTINELS +} from "../IFlowERC20V3.sol"; +import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; + +/// Initialization config. +/// @param name As per Open Zeppelin `ERC20Upgradeable`. +/// @param symbol As per Open Zeppelin `ERC20Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to evaluate `handleTransfer`. +/// @param flowConfig The `EvaluableConfigV2[]` to use to build the +/// `evaluable`s for all the flows, including self minting and burning. +struct FlowERC20ConfigV2 { + string name; + string symbol; + EvaluableConfigV2 evaluableConfig; + EvaluableConfigV2[] flowConfig; +} + +/// @title IFlowERC20V5 +/// Conceptually identical to `IFlowV5`, but the flow contract itself is an +/// ERC20 token. This means that ERC20 self mints and burns are included in the +/// stack that the flows must evaluate to. As stacks are processed by flow from +/// bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC20 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// Otherwise the flow contract is identical to `IFlowV5`. +interface IFlowERC20V5 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC20ConfigV2 config); + + /// As per `IFlowV4` but returns a `FlowERC20IOV1` instead of a + /// `FlowTransferV1`. + /// @param stack The stack to convert to a `FlowERC20IOV1`. + /// @return flowERC20IO The `FlowERC20IOV1` representation of the stack. + function stackToFlow(uint256[] memory stack) external pure returns (FlowERC20IOV1 memory flowERC20IO); + + /// As per `IFlowV4` but returns a `FlowERC20IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC20 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC20IO The `FlowERC20IOV1` representing all token mint/burns + /// and transfers that occurred during the flow. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC20IOV1 calldata flowERC20IO); +} diff --git a/src/interface/unstable/IFlowERC721V5.sol b/src/interface/unstable/IFlowERC721V5.sol new file mode 100644 index 00000000..d76a94a2 --- /dev/null +++ b/src/interface/unstable/IFlowERC721V5.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; + +import { + FlowERC721IOV1, + ERC721SupplyChange, + FLOW_ERC721_TOKEN_URI_MIN_OUTPUTS, + FLOW_ERC721_TOKEN_URI_MAX_OUTPUTS, + FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC721_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC721_TOKEN_URI_ENTRYPOINT, + FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT, + FLOW_ERC721_MIN_FLOW_SENTINELS +} from "../IFlowERC721V3.sol"; + +import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; + +/// Thrown when burner of tokens is not the owner of tokens. +error BurnerNotOwner(); + +/// Initialization config. +/// @param name As per Open Zeppelin `ERC721Upgradeable`. +/// @param symbol As per Open Zeppelin `ERC721Upgradeable`. +/// @param baseURI As per Open Zeppelin `ERC721Upgradeable`. +/// @param evaluableConfig The `EvaluableConfigV2` to use to build the +/// `evaluable` that can be used to handle transfers and build token IDs for the +/// token URI. +/// @param flowConfig Initialization config for the `Evaluable`s that define the +/// flow behaviours outside self mints/burns. +struct FlowERC721ConfigV2 { + string name; + string symbol; + string baseURI; + EvaluableConfigV2 evaluableConfig; + EvaluableConfigV2[] flowConfig; +} + +/// @title IFlowERC721V4 +/// Conceptually identical to `IFlowV4`, but the flow contract itself is an +/// ERC721 token. This means that ERC721 self mints and burns are included in the +/// stack that the flows must evaluate to. As stacks are processed by flow from +/// bottom to top, this means that the self mint/burn will be the last thing +/// evaluated, with mints at the bottom and burns next, followed by the flows. +/// +/// As the flow is an ERC721 token it also includes an evaluation to be run on +/// every token transfer. This is the `handleTransfer` entrypoint. The return +/// stack of this evaluation is ignored, but reverts MUST be respected. This +/// allows expression authors to prevent transfers from occurring if they don't +/// want them to, by reverting within the expression. +/// +/// The flow contract also includes an evaluation to be run on every token URI +/// request. This is the `tokenURI` entrypoint. The return value of this +/// evaluation is the token ID to use for the token URI. This entryoint is +/// optional, and if not provided the token URI will be the default Open Zeppelin +/// token URI logic. +/// +/// Otherwise the flow contract is identical to `IFlowV4`. +interface IFlowERC721V4 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC721ConfigV2 config); + + /// As per `IFlowV4` but returns a `FlowERC721IOV1` instead of a + /// `FlowTransferV1`. + function stackToFlow(uint256[] memory stack) external pure returns (FlowERC721IOV1 memory flowERC721IO); + + /// As per `IFlowV4` but returns a `FlowERC721IOV1` instead of a + /// `FlowTransferV1` and mints/burns itself as an ERC721 accordingly. + /// @param evaluable The `Evaluable` to use to evaluate the flow. + /// @param callerContext The caller context to use to evaluate the flow. + /// @param signedContexts The signed contexts to use to evaluate the flow. + /// @return flowERC721IO The `FlowERC721IOV1` representing all token + /// mint/burns and transfers that occurred during the flow. + function flow( + Evaluable calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowERC721IOV1 calldata flowERC721IO); +} diff --git a/src/interface/unstable/IFlowV5.sol b/src/interface/unstable/IFlowV5.sol new file mode 100644 index 00000000..27998b5e --- /dev/null +++ b/src/interface/unstable/IFlowV5.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SignedContextV1, EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import {Pointer} from "rain.solmem/lib/LibPointer.sol"; + +import { + FlowTransferV1, + ERC20Transfer, + ERC721Transfer, + ERC1155Transfer, + RAIN_FLOW_SENTINEL, + UnregisteredFlow, + UnsupportedERC20Flow, + UnsupportedERC721Flow, + UnsupportedERC1155Flow, + MIN_FLOW_SENTINELS +} from "../IFlowV3.sol"; + +/// @title IFlowV4 +/// @notice 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`. +interface IFlowV4 { + /// MUST be emitted when the flow contract is initialized. + /// @param sender The EOA that deployed the flow contract. + /// @param config The list of evaluable configs that define the flows. + event Initialize(address sender, EvaluableConfigV3[] config); + + /// 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. + /// @param stack The stack of values to convert to a flow transfer. + /// @return flowTransfer The resulting flow transfer. + function stackToFlow(uint256[] memory stack) external pure returns (FlowTransferV1 calldata flowTransfer); + + /// 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). + /// @param evaluable The evaluable to evaluate. + /// @param callerContext The caller context to pass to the evaluable. + /// @param signedContexts The signed contexts to pass to the evaluable. + /// @return flowTransfer The resulting flow transfer. + function flow( + EvaluableV2 calldata evaluable, + uint256[] calldata callerContext, + SignedContextV1[] calldata signedContexts + ) external returns (FlowTransferV1 calldata flowTransfer); +}