diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml deleted file mode 100644 index 25a8245f..00000000 --- a/.github/workflows/docs.yaml +++ /dev/null @@ -1,27 +0,0 @@ -name: Build docs - -on: - push: - branches: - - main - -jobs: - docs: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - name: Generate docs - run: forge doc -b - - name: Deploy to GitHub Pages - if: success() - uses: crazy-max/ghaction-github-pages@v3 - with: - target_branch: gh-pages - build_dir: docs/book - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/manual-sol-artifacts.yaml b/.github/workflows/manual-sol-artifacts.yaml new file mode 100644 index 00000000..7f772d82 --- /dev/null +++ b/.github/workflows/manual-sol-artifacts.yaml @@ -0,0 +1,35 @@ +name: Manual sol artifacts +on: + workflow_dispatch: + inputs: + network: + description: 'Network to deploy to' + required: true + type: choice + options: + - polygon + - songbird + - flare + +jobs: + deploy: + runs-on: ubuntu-latest + env: + DEPLOYMENT_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRIVATE_KEY || secrets.PRIVATE_KEY_DEV }} + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - uses: DeterminateSystems/nix-installer-action@v4 + - uses: DeterminateSystems/magic-nix-cache-action@v2 + + - run: nix develop --command rainix-sol-prelude + - run: nix develop --command rainix-sol-artifacts + env: + ETH_RPC_URL: ${{ inputs.network == 'polygon' && secrets.CI_DEPLOY_POLYGON_RPC_URL || inputs.network == 'songbird' && secrets.CI_DEPLOY_SONGBIRD_RPC_URL || inputs.network == 'flare' && secrets.CI_DEPLOY_FLARE_RPC_URL || '' }} + # Flare has hardcoded api key https://flarescan.com/documentation/recipes/foundry-verification + ETHERSCAN_API_KEY: ${{ inputs.network == 'flare' && 'verifyContract' || secrets.EXPLORER_VERIFICATION_KEY }} + DEPLOY_VERIFIER: ${{ inputs.network == 'songbird' && 'blockscout' || inputs.network == 'polygon' && 'etherscan' || '' }} + DEPLOY_VERIFIER_URL: ${{ inputs.network == 'songbird' && 'https://songbird-explorer.flare.network/api' || inputs.network == 'flare' && 'https://api.routescan.io/v2/network/mainnet/evm/14/etherscan' || '' }} diff --git a/.github/workflows/rainix.yaml b/.github/workflows/rainix.yaml new file mode 100644 index 00000000..97d666b3 --- /dev/null +++ b/.github/workflows/rainix.yaml @@ -0,0 +1,31 @@ +name: Rainix CI +on: [push] + +jobs: + standard-tests: + strategy: + matrix: + os: [ubuntu-latest] + task: [rainix-sol-test, rainix-sol-static] + fail-fast: false + runs-on: ${{ matrix.os }} + env: + DEPLOYMENT_KEY: ${{ github.ref == 'refs/heads/main' && secrets.PRIVATE_KEY || secrets.PRIVATE_KEY_DEV }} + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v4 + - uses: DeterminateSystems/magic-nix-cache-action@v2 + + - run: nix develop --command rainix-sol-prelude + + - name: Run ${{ matrix.task }} + env: + ETH_RPC_URL: ${{ secrets.CI_DEPLOY_RPC_URL }} + ETHERSCAN_API_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }} + DEPLOY_VERIFIER: 'etherscan' + run: nix develop --command ${{ matrix.task }} \ No newline at end of file diff --git a/.github/workflows/slither.yaml b/.github/workflows/slither.yaml deleted file mode 100644 index 50d96461..00000000 --- a/.github/workflows/slither.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: Slither Analysis -on: [push] -jobs: - slither-analyze: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: crytic/slither-action@v0.3.0 diff --git a/.github/workflows/snapshot.yaml b/.github/workflows/snapshot.yaml deleted file mode 100644 index 2d3f2dec..00000000 --- a/.github/workflows/snapshot.yaml +++ /dev/null @@ -1,28 +0,0 @@ -name: Gas snapshot - -on: [push] - -env: - FOUNDRY_PROFILE: ci - -jobs: - snapshot: - strategy: - fail-fast: true - - name: Gas snapshot - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Foundry shallow install - run: forge install --shallow - - - name: Check gas snapshots - run: forge snapshot --check - id: snapshot diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index aa8907d7..00000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: Test - -on: [push] - -env: - FOUNDRY_PROFILE: ci - -jobs: - test: - strategy: - fail-fast: true - - name: Tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - - name: Foundry shallow install - run: forge install --shallow - - - name: Run Forge fmt - run: forge fmt --check - id: fmt - - - name: Run Forge build - run: forge build --sizes - id: build - - - name: Run Forge tests - run: forge test -vvv --gas-report - id: test diff --git a/lib/rain.factory b/lib/rain.factory index e6babfcc..c6d66594 160000 --- a/lib/rain.factory +++ b/lib/rain.factory @@ -1 +1 @@ -Subproject commit e6babfcc5788bb387878d6915955228b2a4cfc70 +Subproject commit c6d66594f23e920d7a2605d22138be70af001739 diff --git a/lib/rain.interpreter.interface b/lib/rain.interpreter.interface index 7a782504..dcdee287 160000 --- a/lib/rain.interpreter.interface +++ b/lib/rain.interpreter.interface @@ -1 +1 @@ -Subproject commit 7a78250485c9dd45fc8d877832b547f85223bbe0 +Subproject commit dcdee287da6dcc30739bc454b5de914d210c0b38 diff --git a/src/abstract/FlowCommon.sol b/src/abstract/FlowCommon.sol index 41758ff8..4f338ce3 100644 --- a/src/abstract/FlowCommon.sol +++ b/src/abstract/FlowCommon.sol @@ -3,23 +3,21 @@ pragma solidity =0.8.19; import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; import {Pointer} from "rain.solmem/lib/LibPointer.sol"; -import {IInterpreterCallerV2, SignedContextV1} from "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import {LibEncodedDispatch} from "rain.interpreter/src/lib/caller/LibEncodedDispatch.sol"; -import {LibContext} from "rain.interpreter/src/lib/caller/LibContext.sol"; -import {UnregisteredFlow, MIN_FLOW_SENTINELS} from "../interface/unstable/IFlowV4.sol"; import { - DeployerDiscoverableMetaV2, - DeployerDiscoverableMetaV2ConstructionConfig -} from "rain.interpreter/src/abstract/DeployerDiscoverableMetaV2.sol"; + IInterpreterCallerV2, + SignedContextV1, + EvaluableConfigV3 +} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import {LibEncodedDispatch} from "rain.interpreter.interface/lib/caller/LibEncodedDispatch.sol"; +import {LibContext} from "rain.interpreter.interface/lib/caller/LibContext.sol"; +import {UnregisteredFlow, MIN_FLOW_SENTINELS} from "../interface/unstable/IFlowV5.sol"; +import {LibEvaluable, EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import { - LibEvaluable, - Evaluable, - EvaluableConfigV2, + SourceIndexV2, + IInterpreterV2, + IInterpreterStoreV2, DEFAULT_STATE_NAMESPACE -} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; -import {SourceIndex, IInterpreterV1} from "rain.interpreter/src/interface/IInterpreterV1.sol"; -import {IInterpreterStoreV1} from "rain.interpreter/src/interface/IInterpreterStoreV1.sol"; - +} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; import {MulticallUpgradeable as Multicall} from "openzeppelin-contracts-upgradeable/contracts/utils/MulticallUpgradeable.sol"; import {ERC721HolderUpgradeable as ERC721Holder} from @@ -31,6 +29,8 @@ import { import {ReentrancyGuardUpgradeable as ReentrancyGuard} from "openzeppelin-contracts-upgradeable/contracts/security/ReentrancyGuardUpgradeable.sol"; import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; +import {LibNamespace, StateNamespace} from "rain.interpreter.interface/lib/ns/LibNamespace.sol"; +import {UnsupportedFlowInputs, InsufficientFlowOutputs} from "../error/ErrFlow.sol"; /// 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 @@ -41,7 +41,7 @@ error BadMinStackLength(uint256 flowMinOutputs); /// @dev 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); +SourceIndexV2 constant FLOW_ENTRYPOINT = SourceIndexV2.wrap(0); /// @dev 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; @@ -82,17 +82,11 @@ uint256 constant FLOW_IS_NOT_REGISTERED = 0; /// 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. -abstract contract FlowCommon is - ERC721Holder, - ERC1155Holder, - Multicall, - ReentrancyGuard, - IInterpreterCallerV2, - DeployerDiscoverableMetaV2 -{ +abstract contract FlowCommon is ERC721Holder, ERC1155Holder, Multicall, ReentrancyGuard, IInterpreterCallerV2 { using LibUint256Array for uint256[]; using LibUint256Matrix for uint256[]; - using LibEvaluable for Evaluable; + using LibEvaluable for EvaluableV2; + using LibNamespace for StateNamespace; /// @dev This mapping tracks all flows that are registered at initialization. /// This is used to ensure that only registered flows are evaluated. @@ -107,7 +101,7 @@ abstract contract FlowCommon is /// @param 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. - event FlowInitialized(address sender, Evaluable evaluable); + event FlowInitialized(address sender, EvaluableV2 evaluable); /// Forwards config to `DeployerDiscoverableMetaV2` and disables /// initializers. The initializers are disabled because inheriting contracts @@ -116,11 +110,7 @@ abstract contract FlowCommon is /// 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. - /// @param metaHash As per `DeployerDiscoverableMetaV2`. - /// @param config As per `DeployerDiscoverableMetaV2`. - constructor(bytes32 metaHash, DeployerDiscoverableMetaV2ConstructionConfig memory config) - DeployerDiscoverableMetaV2(metaHash, config) - { + constructor() { _disableInitializers(); } @@ -131,7 +121,7 @@ abstract contract FlowCommon is /// movements at runtime for the inheriting contract. /// @param flowMinOutputs The minimum number of outputs for each flow. All /// flows share the same minimum number of outputs for simplicity. - function flowCommonInit(EvaluableConfigV2[] memory evaluableConfigs, uint256 flowMinOutputs) + function flowCommonInit(EvaluableConfigV3[] memory evaluableConfigs, uint256 flowMinOutputs) internal onlyInitializing { @@ -149,8 +139,8 @@ abstract contract FlowCommon is revert BadMinStackLength(flowMinOutputs); } - EvaluableConfigV2 memory config; - Evaluable memory evaluable; + EvaluableConfigV3 memory config; + EvaluableV2 memory evaluable; // Every evaluable MUST deploy cleanly (e.g. pass integrity checks) // otherwise the entire initialization will fail. for (uint256 i = 0; i < evaluableConfigs.length; ++i) { @@ -161,10 +151,26 @@ abstract contract FlowCommon is // Reentrancy is just one of many ways that a malicious deployer // can cause problems, and it's probably the least of your // worries if you're using a malicious deployer. - (IInterpreterV1 interpreter, IInterpreterStoreV1 store, address expression) = config - .deployer - .deployExpression(config.bytecode, config.constants, LibUint256Array.arrayFrom(flowMinOutputs)); - evaluable = Evaluable(interpreter, store, expression); + (IInterpreterV2 interpreter, IInterpreterStoreV2 store, address expression, bytes memory io) = + config.deployer.deployExpression2(config.bytecode, config.constants); + + { + uint256 flowInputs; + uint256 flowOutputs; + assembly ("memory-safe") { + let ioWord := mload(add(io, 0x20)) + flowInputs := byte(0, ioWord) + flowOutputs := byte(1, ioWord) + } + if (flowInputs != 0) { + revert UnsupportedFlowInputs(); + } + if (flowOutputs < flowMinOutputs) { + revert InsufficientFlowOutputs(); + } + } + + evaluable = EvaluableV2(interpreter, store, expression); // There's no way to set this mapping before the external // contract call because the output of the external contract // call is used to build the evaluable that we're registering. @@ -196,7 +202,7 @@ abstract contract FlowCommon is /// @return The top of the stack after evaluation. /// @return The key-value pairs that were emitted during evaluation. function _flowStack( - Evaluable memory evaluable, + EvaluableV2 memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts ) internal returns (Pointer, Pointer, uint256[] memory) { @@ -211,11 +217,12 @@ abstract contract FlowCommon is } } - (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval( + (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval2( evaluable.store, - DEFAULT_STATE_NAMESPACE, - LibEncodedDispatch.encode(evaluable.expression, FLOW_ENTRYPOINT, FLOW_MAX_OUTPUTS), - context + DEFAULT_STATE_NAMESPACE.qualifyNamespace(address(this)), + LibEncodedDispatch.encode2(evaluable.expression, FLOW_ENTRYPOINT, FLOW_MAX_OUTPUTS), + context, + new uint256[](0) ); return (stack.dataPointer(), stack.endPointer(), kvs); } diff --git a/src/concrete/basic/Flow.meta.json b/src/concrete/basic/Flow.meta.json deleted file mode 100644 index 03fb64f6..00000000 --- a/src/concrete/basic/Flow.meta.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "Flow", - "abiName": "Flow", - "desc": "Flow contracts transfer tokens based on deployed expressions.", - "alias": "flow", - "source": "https://github.com/rainprotocol/rain-protocol", - "methods": [ - { - "name": "initialize", - "abiName": "initialize", - "desc": "Initialize a new Flow ERC20 contract.", - "expressions": [ - { - "name": "Dummy Expression", - "abiName": "dummyConfig", - "desc": "A dummy expression. This is here to workaround a bug in Solidity.", - "path": "[13].inputs[1].components[0]" - }, - { - "name": "Flows", - "abiName": "config", - "desc": "The available flows.", - "path": "[13].inputs[1].components[1]", - "signedContext": true, - "callerContext": true, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column.", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Flow caller", - "desc": "The contract or wallet that called flow.", - "alias": "flow-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the flow contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/concrete/basic/Flow.sol b/src/concrete/basic/Flow.sol index c9931735..840b5f13 100644 --- a/src/concrete/basic/Flow.sol +++ b/src/concrete/basic/Flow.sol @@ -2,51 +2,43 @@ pragma solidity =0.8.19; import {ICloneableV2, ICLONEABLE_V2_SUCCESS} from "rain.factory/src/interface/ICloneableV2.sol"; -import {FlowCommon, DeployerDiscoverableMetaV2ConstructionConfig, LibContext} from "../../abstract/FlowCommon.sol"; -import {IFlowV4, MIN_FLOW_SENTINELS} from "../../interface/unstable/IFlowV4.sol"; +import {FlowCommon, LibContext} from "../../abstract/FlowCommon.sol"; +import {IFlowV5, MIN_FLOW_SENTINELS, FlowTransferV1} from "../../interface/unstable/IFlowV5.sol"; import {LibFlow} from "../../lib/LibFlow.sol"; import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; import {Pointer} from "rain.solmem/lib/LibPointer.sol"; import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; -import {Evaluable, EvaluableConfigV2} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; -import {FlowTransferV1} from "../../interface/unstable/IFlowV4.sol"; -import {SignedContextV1} from "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; - -/// @dev The hash of the meta data expected to be passed to `FlowCommon`'s -/// constructor. -bytes32 constant CALLER_META_HASH = bytes32(0x95de68a447a477b8fab10701f1265b3e85a98b24710b3e40e6a96aa6d76263bc); +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {SignedContextV1, EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; /// @title Flow -/// See `IFlowV4` docs. -contract Flow is ICloneableV2, IFlowV4, FlowCommon { +/// See `IFlowV5` docs. +contract Flow is ICloneableV2, IFlowV5, FlowCommon { using LibUint256Matrix for uint256[]; using LibUint256Array for uint256[]; - /// Forwards to `FlowCommon` constructor. - constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config) {} - /// Overloaded typed initialize function MUST revert with this error. /// As per `ICloneableV2` interface. - function initialize(EvaluableConfigV2[] memory) external pure { + function initialize(EvaluableConfigV3[] memory) external pure { revert InitializeSignatureFn(); } /// @inheritdoc ICloneableV2 function initialize(bytes calldata data) external initializer returns (bytes32) { - EvaluableConfigV2[] memory flowConfig = abi.decode(data, (EvaluableConfigV2[])); + EvaluableConfigV3[] memory flowConfig = abi.decode(data, (EvaluableConfigV3[])); emit Initialize(msg.sender, flowConfig); flowCommonInit(flowConfig, MIN_FLOW_SENTINELS); return ICLONEABLE_V2_SUCCESS; } - /// @inheritdoc IFlowV4 + /// @inheritdoc IFlowV5 function stackToFlow(uint256[] memory stack) external pure virtual override returns (FlowTransferV1 memory) { return LibFlow.stackToFlow(stack.dataPointer(), stack.endPointer()); } - /// @inheritdoc IFlowV4 - function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) + /// @inheritdoc IFlowV5 + function flow(EvaluableV2 memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) external virtual nonReentrant diff --git a/src/concrete/erc1155/FlowERC1155.meta.json b/src/concrete/erc1155/FlowERC1155.meta.json deleted file mode 100644 index c004228c..00000000 --- a/src/concrete/erc1155/FlowERC1155.meta.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "name": "Flow ERC1155", - "abiName": "FlowERC1155", - "desc": "A flow contract that is also an ERC1155 token that can be minted by expressions. Transfers can be restricted by an expression.", - "alias": "flow-erc1155", - "source": "https://github.com/rainprotocol/rain-protocol", - "methods": [ - { - "name": "initialize", - "abiName": "initialize", - "desc": "Initialize a FlowERC1155 contract.", - "inputs": [ - { - "name": "URI", - "abiName": "uri", - "desc": "The URI for the ERC1155 token that is minted.", - "path": "[14].inputs[1].components[0]" - } - ], - "expressions": [ - { - "name": "Handle transfer", - "abiName": "evaluableConfig", - "desc": "Review and restrict transfers of the flow contract's own ERC1155 token. Mint and burns must be controlled by the flow expressions instead.", - "path": "[14].inputs[1].components[1]", - "signedContext": false, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Transfer caller", - "desc": "The contract or wallet that called transfer.", - "alias": "transfer-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the FlowERC1155 contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - }, - { - "name": "Transfer information", - "desc": "Information about the current transfer.", - "alias": "transfer-information", - "columnIndex": 1, - "cells": [ - { - "name": "Transfer operator", - "desc": "The address of the transfer operator.", - "alias": "transfer-operator-address", - "cellIndex": 0 - }, - { - "name": "Transfer from address", - "desc": "The address the token is being transferred from.", - "alias": "transfer-from-address", - "cellIndex": 1 - }, - { - "name": "Transfer to address", - "desc": "The address the token is being transferred to.", - "alias": "transfer-to-address", - "cellIndex": 2 - } - ] - }, - { - "name": "Token IDs", - "desc": "All the ERC1155 ids being transferred.", - "alias": "transfer-ids", - "columnIndex": 2 - }, - { - "name": "Token amounts", - "desc": "All the ERC1155 amounts being transferred, pairwise with the IDs.", - "alias": "transfer-amounts", - "columnIndex": 3 - } - ] - }, - { - "name": "Flows", - "abiName": "flowConfig", - "desc": "The available flows for this token.", - "path": "[14].inputs[1].components[2]", - "signedContext": true, - "callerContext": true, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column.", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Flow caller", - "desc": "The contract or wallet that called flow.", - "alias": "flow-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the FlowERC1155 contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/concrete/erc1155/FlowERC1155.sol b/src/concrete/erc1155/FlowERC1155.sol index f86aa2ce..b08f576f 100644 --- a/src/concrete/erc1155/FlowERC1155.sol +++ b/src/concrete/erc1155/FlowERC1155.sol @@ -4,84 +4,96 @@ pragma solidity =0.8.19; import {ERC1155Upgradeable as ERC1155} from "openzeppelin-contracts-upgradeable/contracts/token/ERC1155/ERC1155Upgradeable.sol"; -import {LibEncodedDispatch, EncodedDispatch} from "rain.interpreter/src/lib/caller/LibEncodedDispatch.sol"; +import {LibEncodedDispatch, EncodedDispatch} from "rain.interpreter.interface/lib/caller/LibEncodedDispatch.sol"; import {Sentinel, LibStackSentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import {ICloneableV2, ICLONEABLE_V2_SUCCESS} from "rain.factory/src/interface/ICloneableV2.sol"; import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; import { - IFlowERC1155V4, + IFlowERC1155V5, FlowERC1155IOV1, SignedContextV1, - FlowERC1155ConfigV2, + FlowERC1155ConfigV3, ERC1155SupplyChange, RAIN_FLOW_SENTINEL, FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT, FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS, FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS, - FLOW_ERC1155_MIN_FLOW_SENTINELS -} from "../../interface/unstable/IFlowERC1155V4.sol"; + FLOW_ERC1155_MIN_FLOW_SENTINELS, + EvaluableV2 +} from "../../interface/unstable/IFlowERC1155V5.sol"; import {LibBytecode} from "rain.interpreter.interface/lib/bytecode/LibBytecode.sol"; -import {IInterpreterV1} from "rain.interpreter/src/interface/IInterpreterV1.sol"; -import {IInterpreterStoreV1} from "rain.interpreter/src/interface/IInterpreterStoreV1.sol"; -import {Evaluable, DEFAULT_STATE_NAMESPACE} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import { + IInterpreterV2, DEFAULT_STATE_NAMESPACE +} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; +import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/unstable/IInterpreterStoreV2.sol"; import {Pointer} from "rain.solmem/lib/LibPointer.sol"; import {LibFlow} from "../../lib/LibFlow.sol"; -import {SourceIndex} from "rain.interpreter/src/interface/IInterpreterV1.sol"; -import { - FlowCommon, DeployerDiscoverableMetaV2ConstructionConfig, ERC1155Receiver -} from "../../abstract/FlowCommon.sol"; -import {LibContext} from "rain.interpreter/src/lib/caller/LibContext.sol"; - -/// @dev The hash of the meta data expected by the `FlowCommon` constructor. -bytes32 constant CALLER_META_HASH = bytes32(0x7ea70f837234357ec1bb5b777e04453ebaf3ca778a98805c4bb20a738d559a21); +import {SourceIndexV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; +import {FlowCommon, ERC1155Receiver} from "../../abstract/FlowCommon.sol"; +import {LibContext} from "rain.interpreter.interface/lib/caller/LibContext.sol"; +import {LibNamespace, StateNamespace} from "rain.interpreter.interface/lib/ns/LibNamespace.sol"; +import {InsufficientHandleTransferOutputs, UnsupportedHandleTransferInputs} from "../../error/ErrFlow.sol"; /// @title FlowERC1155 -/// See `IFlowERC1155V4` for documentation. -contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { +/// See `IFlowERC1155V5` for documentation. +contract FlowERC1155 is ICloneableV2, IFlowERC1155V5, FlowCommon, ERC1155 { using LibStackSentinel for Pointer; using LibUint256Matrix for uint256[]; using LibUint256Array for uint256[]; + using LibNamespace for StateNamespace; /// True if the evaluable needs to be called on every transfer. bool private sEvalHandleTransfer; /// The `Evaluable` that handles transfers. - Evaluable internal sEvaluable; - - /// Forwards the `FlowCommon` constructor. - constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config) {} + EvaluableV2 internal sEvaluable; /// Overloaded typed initialize function MUST revert with this error. /// As per `ICloneableV2` interface. - function initialize(FlowERC1155ConfigV2 memory) external pure { + function initialize(FlowERC1155ConfigV3 memory) external pure { revert InitializeSignatureFn(); } /// @inheritdoc ICloneableV2 function initialize(bytes calldata data) external initializer returns (bytes32) { - FlowERC1155ConfigV2 memory flowERC1155Config = abi.decode(data, (FlowERC1155ConfigV2)); + FlowERC1155ConfigV3 memory flowERC1155Config = abi.decode(data, (FlowERC1155ConfigV3)); emit Initialize(msg.sender, flowERC1155Config); __ERC1155_init(flowERC1155Config.uri); // Set state before external calls here. bool evalHandleTransfer = LibBytecode.sourceCount(flowERC1155Config.evaluableConfig.bytecode) > 0 && LibBytecode.sourceOpsCount( - flowERC1155Config.evaluableConfig.bytecode, SourceIndex.unwrap(FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT) + flowERC1155Config.evaluableConfig.bytecode, SourceIndexV2.unwrap(FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT) ) > 0; sEvalHandleTransfer = evalHandleTransfer; flowCommonInit(flowERC1155Config.flowConfig, FLOW_ERC1155_MIN_FLOW_SENTINELS); if (evalHandleTransfer) { - (IInterpreterV1 interpreter, IInterpreterStoreV1 store, address expression) = flowERC1155Config - .evaluableConfig - .deployer - .deployExpression( - flowERC1155Config.evaluableConfig.bytecode, - flowERC1155Config.evaluableConfig.constants, - LibUint256Array.arrayFrom(FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS) + (IInterpreterV2 interpreter, IInterpreterStoreV2 store, address expression, bytes memory io) = + flowERC1155Config.evaluableConfig.deployer.deployExpression2( + flowERC1155Config.evaluableConfig.bytecode, flowERC1155Config.evaluableConfig.constants ); + + { + uint256 handleTransferInputs; + uint256 handleTransferOutputs; + assembly ("memory-safe") { + let ioWord := mload(add(io, 0x20)) + handleTransferInputs := byte(0, ioWord) + handleTransferOutputs := byte(1, ioWord) + } + + if (handleTransferInputs != 0) { + revert UnsupportedHandleTransferInputs(); + } + + if (handleTransferOutputs < FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS) { + revert InsufficientHandleTransferOutputs(); + } + } + // There's no way to set this before the external call because the // output of the `deployExpression` call is the input to `Evaluable`. // Even if we could set it before the external call, we wouldn't want @@ -89,7 +101,7 @@ contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { // integrity checks are complete. // The deployer MUST be a trusted contract anyway. // slither-disable-next-line reentrancy-benign - sEvaluable = Evaluable(interpreter, store, expression); + sEvaluable = EvaluableV2(interpreter, store, expression); } return ICLONEABLE_V2_SUCCESS; @@ -121,7 +133,7 @@ contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { // Mint and burn access MUST be handled by flow. // HANDLE_TRANSFER will only restrict subsequent transfers. if (sEvalHandleTransfer && !(from == address(0) || to == address(0))) { - Evaluable memory evaluable = sEvaluable; + EvaluableV2 memory evaluable = sEvaluable; uint256[][] memory context; { context = LibContext.build( @@ -138,15 +150,16 @@ contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { ); } - (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval( + (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval2( evaluable.store, - DEFAULT_STATE_NAMESPACE, - LibEncodedDispatch.encode( + DEFAULT_STATE_NAMESPACE.qualifyNamespace(address(this)), + LibEncodedDispatch.encode2( evaluable.expression, FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT, FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS ), - context + context, + new uint256[](0) ); (stack); if (kvs.length > 0) { @@ -156,7 +169,7 @@ contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { } } - /// @inheritdoc IFlowERC1155V4 + /// @inheritdoc IFlowERC1155V5 function stackToFlow(uint256[] memory stack) external pure @@ -166,8 +179,8 @@ contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { return _stackToFlow(stack.dataPointer(), stack.endPointer()); } - /// @inheritdoc IFlowERC1155V4 - function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) + /// @inheritdoc IFlowERC1155V5 + function flow(EvaluableV2 memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) external virtual returns (FlowERC1155IOV1 memory) @@ -207,12 +220,11 @@ contract FlowERC1155 is ICloneableV2, IFlowERC1155V4, FlowCommon, ERC1155 { /// 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) - { + function _flow( + EvaluableV2 memory evaluable, + uint256[] memory callerContext, + SignedContextV1[] memory signedContexts + ) internal virtual nonReentrant returns (FlowERC1155IOV1 memory) { unchecked { (Pointer stackBottom, Pointer stackTop, uint256[] memory kvs) = _flowStack(evaluable, callerContext, signedContexts); diff --git a/src/concrete/erc20/FlowERC20.meta.json b/src/concrete/erc20/FlowERC20.meta.json deleted file mode 100644 index 4b624ed2..00000000 --- a/src/concrete/erc20/FlowERC20.meta.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "name": "Flow ERC20", - "abiName": "FlowERC20", - "desc": "A flow contract that is also an ERC20 token that can be minted by expressions. Transfers can be restricted by an expression.", - "alias": "flow-erc20", - "source": "https://github.com/rainprotocol/rain-protocol", - "methods": [ - { - "name": "initialize", - "abiName": "initialize", - "desc": "Initialize a FlowERC20 contract.", - "inputs": [ - { - "name": "Name", - "abiName": "name", - "desc": "Name of the ERC20 token that is minted.", - "path": "[14].inputs[1].components[0]" - }, - { - "name": "Symbol", - "abiName": "symbol", - "desc": "Symbol of the ERC20 token that is minted.", - "path": "[14].inputs[1].components[1]" - } - ], - "expressions": [ - { - "name": "Handle Transfer", - "abiName": "evaluableConfig", - "desc": "Review and restrict transfers of the flow contract's own ERC20 token. Mint and burns must be controlled by the flow expressions instead.", - "path": "[14].inputs[1].components[2]", - "signedContext": false, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column.", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Transfer caller", - "desc": "The contract or wallet that called transfer.", - "alias": "transfer-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the FlowERC20 contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - }, - { - "name": "Transfer information", - "desc": "Information about the current transfer.", - "alias": "transfer-information", - "columnIndex": 1, - "cells": [ - { - "name": "Transfer from address", - "desc": "The address the token is being transferred from.", - "alias": "transfer-from-address", - "cellIndex": 0 - }, - { - "name": "Transfer to address", - "desc": "The address the token is being transferred to.", - "alias": "transfer-to-address", - "cellIndex": 1 - }, - { - "name": "Transfer amount", - "desc": "The amount of token being transferred.", - "alias": "transfer-amount", - "cellIndex": 2 - } - ] - } - ] - }, - { - "name": "Flows", - "abiName": "flowConfig", - "desc": "The available flows.", - "path": "[14].inputs[1].components[3]", - "signedContext": true, - "callerContext": true, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column.", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Flow caller", - "desc": "The contract or wallet that called flow.", - "alias": "flow-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the FlowERC20 contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/concrete/erc20/FlowERC20.sol b/src/concrete/erc20/FlowERC20.sol index eb8faf6e..a266d8d5 100644 --- a/src/concrete/erc20/FlowERC20.sol +++ b/src/concrete/erc20/FlowERC20.sol @@ -7,7 +7,7 @@ import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; import {ICloneableV2, ICLONEABLE_V2_SUCCESS} from "rain.factory/src/interface/ICloneableV2.sol"; import { - IFlowERC20V4, + IFlowERC20V5, FlowERC20IOV1, FlowERC20ConfigV2, ERC20SupplyChange, @@ -17,32 +17,31 @@ import { FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS, RAIN_FLOW_SENTINEL, FLOW_ERC20_MIN_FLOW_SENTINELS -} from "../../interface/unstable/IFlowERC20V4.sol"; +} from "../../interface/unstable/IFlowERC20V5.sol"; import {LibBytecode} from "rain.interpreter.interface/lib/bytecode/LibBytecode.sol"; -import {EncodedDispatch, LibEncodedDispatch} from "rain.interpreter/src/lib/caller/LibEncodedDispatch.sol"; +import {EncodedDispatch, LibEncodedDispatch} from "rain.interpreter.interface/lib/caller/LibEncodedDispatch.sol"; import {Sentinel, LibStackSentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import {LibFlow} from "../../lib/LibFlow.sol"; +import {FlowCommon} from "../../abstract/FlowCommon.sol"; import { - FlowCommon, - DeployerDiscoverableMetaV2, - DeployerDiscoverableMetaV2ConstructionConfig -} from "../../abstract/FlowCommon.sol"; -import {SourceIndex, IInterpreterV1} from "rain.interpreter/src/interface/IInterpreterV1.sol"; -import {IInterpreterStoreV1} from "rain.interpreter/src/interface/IInterpreterStoreV1.sol"; + SourceIndexV2, + IInterpreterV2, + DEFAULT_STATE_NAMESPACE +} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; +import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/unstable/IInterpreterStoreV2.sol"; import {Pointer} from "rain.solmem/lib/LibPointer.sol"; -import {Evaluable, DEFAULT_STATE_NAMESPACE} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; -import {LibContext} from "rain.interpreter/src/lib/caller/LibContext.sol"; - -/// @dev The hash of the meta data expected to be passed to `FlowCommon`'s -/// constructor. -bytes32 constant CALLER_META_HASH = bytes32(0xff0499e4ee7171a54d176cfe13165a7ea512d146dbd99d42b3d3ec9963025acf); +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {LibContext} from "rain.interpreter.interface/lib/caller/LibContext.sol"; +import {LibNamespace, StateNamespace} from "rain.interpreter.interface/lib/ns/LibNamespace.sol"; +import {UnsupportedHandleTransferInputs, InsufficientHandleTransferOutputs} from "../../error/ErrFlow.sol"; /// @title FlowERC20 -/// See `IFlowERC20V4` for documentation. -contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { +/// See `IFlowERC20V5` for documentation. +contract FlowERC20 is ICloneableV2, IFlowERC20V5, FlowCommon, ERC20 { using LibStackSentinel for Pointer; using LibUint256Matrix for uint256[]; using LibUint256Array for uint256[]; + using LibNamespace for StateNamespace; /// @dev 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 @@ -51,10 +50,8 @@ contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { /// @dev The evaluable that will be used to evaluate `handleTransfer` on /// every transfer. This is only set if `sEvalHandleTransfer` is true. - Evaluable internal sEvaluable; - - /// Forwards the `FlowCommon` constructor arguments to the `FlowCommon`. - constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config) {} + EvaluableV2 internal sEvaluable = + EvaluableV2(IInterpreterV2(address(0)), IInterpreterStoreV2(address(0)), address(0)); /// Overloaded typed initialize function MUST revert with this error. /// As per `ICloneableV2` interface. @@ -71,21 +68,36 @@ contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { // Set state before external calls here. bool evalHandleTransfer = LibBytecode.sourceCount(flowERC20Config.evaluableConfig.bytecode) > 0 && LibBytecode.sourceOpsCount( - flowERC20Config.evaluableConfig.bytecode, SourceIndex.unwrap(FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT) + flowERC20Config.evaluableConfig.bytecode, SourceIndexV2.unwrap(FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT) ) > 0; sEvalHandleTransfer = evalHandleTransfer; flowCommonInit(flowERC20Config.flowConfig, FLOW_ERC20_MIN_FLOW_SENTINELS); if (evalHandleTransfer) { - (IInterpreterV1 interpreter, IInterpreterStoreV1 store, address expression) = flowERC20Config - .evaluableConfig - .deployer - .deployExpression( - flowERC20Config.evaluableConfig.bytecode, - flowERC20Config.evaluableConfig.constants, - LibUint256Array.arrayFrom(FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS) + (IInterpreterV2 interpreter, IInterpreterStoreV2 store, address expression, bytes memory io) = + flowERC20Config.evaluableConfig.deployer.deployExpression2( + flowERC20Config.evaluableConfig.bytecode, flowERC20Config.evaluableConfig.constants ); + + { + uint256 handleTransferInputs; + uint256 handleTransferOutputs; + assembly ("memory-safe") { + let ioWord := mload(add(io, 0x20)) + handleTransferInputs := byte(0, ioWord) + handleTransferOutputs := byte(1, ioWord) + } + + if (handleTransferInputs != 0) { + revert UnsupportedHandleTransferInputs(); + } + + if (handleTransferOutputs < FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS) { + revert InsufficientHandleTransferOutputs(); + } + } + // There's no way to set this before the external call because the // output of the `deployExpression` call is the input to `Evaluable`. // Even if we could set it before the external call, we wouldn't want @@ -93,19 +105,19 @@ contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { // integrity checks are complete. // The deployer MUST be a trusted contract anyway. // slither-disable-next-line reentrancy-benign - sEvaluable = Evaluable(interpreter, store, expression); + sEvaluable = EvaluableV2(interpreter, store, expression); } return ICLONEABLE_V2_SUCCESS; } - /// @inheritdoc IFlowERC20V4 + /// @inheritdoc IFlowERC20V5 function stackToFlow(uint256[] memory stack) external pure virtual override returns (FlowERC20IOV1 memory) { return _stackToFlow(stack.dataPointer(), stack.endPointer()); } - /// @inheritdoc IFlowERC20V4 - function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) + /// @inheritdoc IFlowERC20V5 + function flow(EvaluableV2 memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) external virtual returns (FlowERC20IOV1 memory) @@ -126,11 +138,11 @@ contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { // Mint and burn access MUST be handled by flow. // HANDLE_TRANSFER will only restrict subsequent transfers. if (sEvalHandleTransfer && !(from == address(0) || to == address(0))) { - Evaluable memory evaluable = sEvaluable; - (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval( + EvaluableV2 memory evaluable = sEvaluable; + (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval2( evaluable.store, - DEFAULT_STATE_NAMESPACE, - LibEncodedDispatch.encode( + DEFAULT_STATE_NAMESPACE.qualifyNamespace(address(this)), + LibEncodedDispatch.encode2( evaluable.expression, FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT, FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS @@ -140,7 +152,8 @@ contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { // is triggering the transfer. LibUint256Array.arrayFrom(uint256(uint160(from)), uint256(uint160(to)), amount).matrixFrom(), new SignedContextV1[](0) - ) + ), + new uint256[](0) ); (stack); if (kvs.length > 0) { @@ -185,12 +198,11 @@ contract FlowERC20 is ICloneableV2, IFlowERC20V4, FlowCommon, ERC20 { /// 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) - { + function _flow( + EvaluableV2 memory evaluable, + uint256[] memory callerContext, + SignedContextV1[] memory signedContexts + ) internal virtual nonReentrant returns (FlowERC20IOV1 memory) { unchecked { (Pointer stackBottom, Pointer stackTop, uint256[] memory kvs) = _flowStack(evaluable, callerContext, signedContexts); diff --git a/src/concrete/erc721/FlowERC721.meta.json b/src/concrete/erc721/FlowERC721.meta.json deleted file mode 100644 index be45f4de..00000000 --- a/src/concrete/erc721/FlowERC721.meta.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "name": "Flow ERC721", - "abiName": "FlowERC721", - "desc": "A flow contract that is also an ERC721 token that can be minted by expressions. Transfers can be restricted by an expression.", - "alias": "flow-erc721", - "source": "https://github.com/rainprotocol/rain-protocol", - "methods": [ - { - "name": "initialize", - "abiName": "initialize", - "desc": "Initialize a FlowERC721 contract.", - "inputs": [ - { - "name": "Name", - "abiName": "name", - "desc": "Name of the ERC721 token that is minted.", - "path": "[16].inputs[1].components[0]" - }, - { - "name": "Symbol", - "abiName": "symbol", - "desc": "Symbol for the ERC721 token that is minted.", - "path": "[16].inputs[1].components[1]" - }, - { - "name": "Base URI", - "abiName": "baseURI", - "desc": "Base URI for the ERC721 token that is minted.", - "path": "[16].inputs[1].components[2]" - } - ], - "expressions": [ - { - "name": "Handle transfer", - "abiName": "evaluableConfig", - "desc": "Review and restrict transfers of the flow contract's own ERC721 token. Mint and burns must be controlled by the flow expressions instead.", - "path": "[16].inputs[1].components[3]", - "signedContext": false, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Transfer caller", - "desc": "The contract or wallet that called transfer.", - "alias": "transfer-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the FlowERC721 contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - }, - { - "name": "Transfer information", - "desc": "Information about the current transfer.", - "alias": "transfer-information", - "columnIndex": 1, - "cells": [ - { - "name": "Transfer from address", - "desc": "The address the token is being transferred from.", - "alias": "transfer-from-address", - "cellIndex": 0 - }, - { - "name": "Transfer to address", - "desc": "The address the token is being transferred to.", - "alias": "transfer-to-address", - "cellIndex": 1 - }, - { - "name": "Token ID", - "desc": "The ID of the token being transferred.", - "alias": "transfer-token-id", - "cellIndex": 2 - } - ] - } - ] - }, - { - "name": "Flows", - "abiName": "flowConfig", - "desc": "The available flows.", - "path": "[16].inputs[1].components[4]", - "signedContext": true, - "callerContext": true, - "contextColumns": [ - { - "name": "Base", - "desc": "Base context column.", - "alias": "base", - "columnIndex": 0, - "cells": [ - { - "name": "Flow caller", - "desc": "The contract or wallet that called flow.", - "alias": "flow-caller", - "cellIndex": 0 - }, - { - "name": "Flow contract", - "desc": "The address of the FlowERC721 contract.", - "alias": "flow-contract", - "cellIndex": 1 - } - ] - } - ] - } - ] - } - ] -} diff --git a/src/concrete/erc721/FlowERC721.sol b/src/concrete/erc721/FlowERC721.sol index 6b1fddda..958c438d 100644 --- a/src/concrete/erc721/FlowERC721.sol +++ b/src/concrete/erc721/FlowERC721.sol @@ -7,10 +7,10 @@ import {ERC721Upgradeable as ERC721} from import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; import {Sentinel, LibStackSentinel} from "rain.solmem/lib/LibStackSentinel.sol"; -import {EncodedDispatch, LibEncodedDispatch} from "rain.interpreter/src/lib/caller/LibEncodedDispatch.sol"; +import {EncodedDispatch, LibEncodedDispatch} from "rain.interpreter.interface/lib/caller/LibEncodedDispatch.sol"; import {ICloneableV2, ICLONEABLE_V2_SUCCESS} from "rain.factory/src/interface/ICloneableV2.sol"; import { - IFlowERC721V4, + IFlowERC721V5, FlowERC721IOV1, SignedContextV1, FlowERC721ConfigV2, @@ -21,33 +21,35 @@ import { FLOW_ERC721_HANDLE_TRANSFER_MAX_OUTPUTS, FLOW_ERC721_TOKEN_URI_ENTRYPOINT, FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT, - FLOW_ERC721_MIN_FLOW_SENTINELS -} from "../../interface/unstable/IFlowERC721V4.sol"; + FLOW_ERC721_MIN_FLOW_SENTINELS, + RAIN_FLOW_SENTINEL +} from "../../interface/unstable/IFlowERC721V5.sol"; import {LibBytecode} from "rain.interpreter.interface/lib/bytecode/LibBytecode.sol"; -import {SourceIndex} from "rain.interpreter/src/interface/IInterpreterV1.sol"; +import {SourceIndexV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; import {LibFlow} from "../../lib/LibFlow.sol"; +import {FlowCommon, LibContext, ERC1155Receiver} from "../../abstract/FlowCommon.sol"; +import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import { - FlowCommon, - DeployerDiscoverableMetaV2ConstructionConfig, - LibContext, - ERC1155Receiver -} from "../../abstract/FlowCommon.sol"; -import {Evaluable, DEFAULT_STATE_NAMESPACE} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; -import {IInterpreterV1} from "rain.interpreter/src/interface/IInterpreterV1.sol"; -import {IInterpreterStoreV1} from "rain.interpreter/src/interface/IInterpreterStoreV1.sol"; + IInterpreterV2, DEFAULT_STATE_NAMESPACE +} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; +import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/unstable/IInterpreterStoreV2.sol"; import {Pointer} from "rain.solmem/lib/LibPointer.sol"; -import {RAIN_FLOW_SENTINEL, BurnerNotOwner} from "../../interface/unstable/IFlowERC721V4.sol"; - -/// @dev The hash of the meta data expected to be passed to `FlowCommon`'s -/// constructor. -bytes32 constant CALLER_META_HASH = bytes32(0xf0003e81ff90467c9933f3ac68db3ca49df8b30ab83a0b88e1ed8381ed28fdd6); +import {BurnerNotOwner} from "../../error/ErrFlow.sol"; +import {LibNamespace, StateNamespace} from "rain.interpreter.interface/lib/ns/LibNamespace.sol"; +import { + InsufficientHandleTransferOutputs, + InsufficientTokenURIOutputs, + UnsupportedHandleTransferInputs, + UnsupportedTokenURIInputs +} from "../../error/ErrFlow.sol"; /// @title FlowERC721 /// See `IFlowERC721V4` for documentation. -contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { +contract FlowERC721 is ICloneableV2, IFlowERC721V5, FlowCommon, ERC721 { using LibUint256Matrix for uint256[]; using LibUint256Array for uint256[]; using LibStackSentinel for Pointer; + using LibNamespace for StateNamespace; /// @dev 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 @@ -62,16 +64,14 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { /// @dev The evaluable that contains the entrypoints for `handleTransfer` and /// `tokenURI`. This is only set if `sEvalHandleTransfer` or `sEvalTokenURI` /// is true. - Evaluable internal sEvaluable; + EvaluableV2 internal sEvaluable = + EvaluableV2(IInterpreterV2(address(0)), IInterpreterStoreV2(address(0)), address(0)); /// @dev 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; - /// Forwards the `FlowCommon` constructor arguments to the `FlowCommon`. - constructor(DeployerDiscoverableMetaV2ConstructionConfig memory config) FlowCommon(CALLER_META_HASH, config) {} - /// Needed here to fix Open Zeppelin implementing `supportsInterface` on /// multiple base contracts. function supportsInterface(bytes4 interfaceId) @@ -101,11 +101,11 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { uint256 sourceCount = LibBytecode.sourceCount(flowERC721Config.evaluableConfig.bytecode); bool evalHandleTransfer = sourceCount > 0 && LibBytecode.sourceOpsCount( - flowERC721Config.evaluableConfig.bytecode, SourceIndex.unwrap(FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT) + flowERC721Config.evaluableConfig.bytecode, SourceIndexV2.unwrap(FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT) ) > 0; bool evalTokenURI = sourceCount > 1 && LibBytecode.sourceOpsCount( - flowERC721Config.evaluableConfig.bytecode, SourceIndex.unwrap(FLOW_ERC721_TOKEN_URI_ENTRYPOINT) + flowERC721Config.evaluableConfig.bytecode, SourceIndexV2.unwrap(FLOW_ERC721_TOKEN_URI_ENTRYPOINT) ) > 0; sEvalHandleTransfer = evalHandleTransfer; sEvalTokenURI = evalTokenURI; @@ -113,18 +113,41 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { flowCommonInit(flowERC721Config.flowConfig, FLOW_ERC721_MIN_FLOW_SENTINELS); if (evalHandleTransfer) { - // Include the token URI min outputs if we expect to eval it, - // otherwise only include the handle transfer min outputs. - uint256[] memory minOutputs = evalTokenURI - ? LibUint256Array.arrayFrom(FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS, FLOW_ERC721_TOKEN_URI_MIN_OUTPUTS) - : LibUint256Array.arrayFrom(FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS); - - (IInterpreterV1 interpreter, IInterpreterStoreV1 store, address expression) = flowERC721Config - .evaluableConfig - .deployer - .deployExpression( - flowERC721Config.evaluableConfig.bytecode, flowERC721Config.evaluableConfig.constants, minOutputs + (IInterpreterV2 interpreter, IInterpreterStoreV2 store, address expression, bytes memory io) = + flowERC721Config.evaluableConfig.deployer.deployExpression2( + flowERC721Config.evaluableConfig.bytecode, flowERC721Config.evaluableConfig.constants ); + + { + uint256 handleTransferInputs; + uint256 handleTransferOutputs; + uint256 tokenURIInputs; + uint256 tokenURIOutputs; + assembly ("memory-safe") { + let ioWords := add(io, 0x20) + handleTransferInputs := byte(0, ioWords) + handleTransferOutputs := byte(1, ioWords) + tokenURIInputs := byte(2, ioWords) + tokenURIOutputs := byte(3, ioWords) + } + + if (handleTransferInputs != 0) { + revert UnsupportedHandleTransferInputs(); + } + + if (handleTransferOutputs < FLOW_ERC721_HANDLE_TRANSFER_MIN_OUTPUTS) { + revert InsufficientHandleTransferOutputs(); + } + + if (evalTokenURI && tokenURIInputs != 0) { + revert UnsupportedTokenURIInputs(); + } + + if (evalTokenURI && tokenURIOutputs < FLOW_ERC721_TOKEN_URI_MIN_OUTPUTS) { + revert InsufficientTokenURIOutputs(); + } + } + // There's no way to set this before the external call because the // output of the `deployExpression` call is the input to `Evaluable`. // Even if we could set it before the external call, we wouldn't want @@ -132,7 +155,7 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { // integrity checks are complete. // The deployer MUST be a trusted contract anyway. // slither-disable-next-line reentrancy-benign - sEvaluable = Evaluable(interpreter, store, expression); + sEvaluable = EvaluableV2(interpreter, store, expression); } return ICLONEABLE_V2_SUCCESS; @@ -154,14 +177,15 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { /// @inheritdoc ERC721 function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { if (sEvalTokenURI) { - Evaluable memory evaluable = sEvaluable; - (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval( + EvaluableV2 memory evaluable = sEvaluable; + (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval2( evaluable.store, - DEFAULT_STATE_NAMESPACE, - LibEncodedDispatch.encode( + DEFAULT_STATE_NAMESPACE.qualifyNamespace(address(this)), + LibEncodedDispatch.encode2( evaluable.expression, FLOW_ERC721_TOKEN_URI_ENTRYPOINT, FLOW_ERC721_TOKEN_URI_MAX_OUTPUTS ), - LibContext.build(LibUint256Array.arrayFrom(tokenId).matrixFrom(), new SignedContextV1[](0)) + LibContext.build(LibUint256Array.arrayFrom(tokenId).matrixFrom(), new SignedContextV1[](0)), + new uint256[](0) ); // @todo it would be nice if we could do something with the kvs here, // but the interface is view. @@ -172,13 +196,13 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { return super.tokenURI(tokenId); } - /// @inheritdoc IFlowERC721V4 + /// @inheritdoc IFlowERC721V5 function stackToFlow(uint256[] memory stack) external pure virtual override returns (FlowERC721IOV1 memory) { return _stackToFlow(stack.dataPointer(), stack.endPointer()); } - /// @inheritdoc IFlowERC721V4 - function flow(Evaluable memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) + /// @inheritdoc IFlowERC721V5 + function flow(EvaluableV2 memory evaluable, uint256[] memory callerContext, SignedContextV1[] memory signedContexts) external virtual returns (FlowERC721IOV1 memory) @@ -203,11 +227,11 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { // Mint and burn access MUST be handled by flow. // HANDLE_TRANSFER will only restrict subsequent transfers. if (sEvalHandleTransfer && !(from == address(0) || to == address(0))) { - Evaluable memory evaluable = sEvaluable; - (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval( + EvaluableV2 memory evaluable = sEvaluable; + (uint256[] memory stack, uint256[] memory kvs) = evaluable.interpreter.eval2( evaluable.store, - DEFAULT_STATE_NAMESPACE, - LibEncodedDispatch.encode( + DEFAULT_STATE_NAMESPACE.qualifyNamespace(address(this)), + LibEncodedDispatch.encode2( evaluable.expression, FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT, FLOW_ERC721_HANDLE_TRANSFER_MAX_OUTPUTS @@ -218,7 +242,8 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { // transfer is NOT called for mints. LibUint256Array.arrayFrom(uint256(uint160(from)), uint256(uint160(to)), tokenId).matrixFrom(), new SignedContextV1[](0) - ) + ), + new uint256[](0) ); (stack); if (kvs.length > 0) { @@ -264,12 +289,11 @@ contract FlowERC721 is ICloneableV2, IFlowERC721V4, FlowCommon, ERC721 { /// 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) - { + function _flow( + EvaluableV2 memory evaluable, + uint256[] memory callerContext, + SignedContextV1[] memory signedContexts + ) internal virtual nonReentrant returns (FlowERC721IOV1 memory) { unchecked { (Pointer stackBottom, Pointer stackTop, uint256[] memory kvs) = _flowStack(evaluable, callerContext, signedContexts); diff --git a/src/error/ErrFlow.sol b/src/error/ErrFlow.sol new file mode 100644 index 00000000..27160f0e --- /dev/null +++ b/src/error/ErrFlow.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +/// 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(); + +contract ErrFLow {} + +/// Thrown when burner of tokens is not the owner of tokens. +error BurnerNotOwner(); + +error UnsupportedHandleTransferInputs(); +error InsufficientHandleTransferOutputs(); +error UnsupportedTokenURIInputs(); +error InsufficientTokenURIOutputs(); +error UnsupportedFlowInputs(); +error InsufficientFlowOutputs(); diff --git a/src/interface/deprecated/v1/IFlowERC1155V1.sol b/src/interface/deprecated/v1/IFlowERC1155V1.sol index ec3be66a..f161dfd9 100644 --- a/src/interface/deprecated/v1/IFlowERC1155V1.sol +++ b/src/interface/deprecated/v1/IFlowERC1155V1.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/deprecated/IInterpreterCallerV1.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV1.sol"; diff --git a/src/interface/deprecated/v1/IFlowERC20V1.sol b/src/interface/deprecated/v1/IFlowERC20V1.sol index 6fa7f8e1..542f0950 100644 --- a/src/interface/deprecated/v1/IFlowERC20V1.sol +++ b/src/interface/deprecated/v1/IFlowERC20V1.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/deprecated/IInterpreterCallerV1.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV1.sol"; diff --git a/src/interface/deprecated/v1/IFlowERC721V1.sol b/src/interface/deprecated/v1/IFlowERC721V1.sol index 0087e8a8..f58fd292 100644 --- a/src/interface/deprecated/v1/IFlowERC721V1.sol +++ b/src/interface/deprecated/v1/IFlowERC721V1.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/deprecated/IInterpreterCallerV1.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV1.sol"; diff --git a/src/interface/deprecated/v1/IFlowV1.sol b/src/interface/deprecated/v1/IFlowV1.sol index 83c2042b..65ae0eb0 100644 --- a/src/interface/deprecated/v1/IFlowV1.sol +++ b/src/interface/deprecated/v1/IFlowV1.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/deprecated/IInterpreterCallerV1.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; struct FlowConfig { // https://github.com/ethereum/solidity/issues/13597 diff --git a/src/interface/deprecated/v2/IFlowERC1155V2.sol b/src/interface/deprecated/v2/IFlowERC1155V2.sol index f3febff2..a30dc1b9 100644 --- a/src/interface/deprecated/v2/IFlowERC1155V2.sol +++ b/src/interface/deprecated/v2/IFlowERC1155V2.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV2.sol"; diff --git a/src/interface/deprecated/v2/IFlowERC20V2.sol b/src/interface/deprecated/v2/IFlowERC20V2.sol index 83e68027..4b5a7533 100644 --- a/src/interface/deprecated/v2/IFlowERC20V2.sol +++ b/src/interface/deprecated/v2/IFlowERC20V2.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV2.sol"; diff --git a/src/interface/deprecated/v2/IFlowERC721V2.sol b/src/interface/deprecated/v2/IFlowERC721V2.sol index 0d150ec7..4f2b7e8a 100644 --- a/src/interface/deprecated/v2/IFlowERC721V2.sol +++ b/src/interface/deprecated/v2/IFlowERC721V2.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV2.sol"; diff --git a/src/interface/deprecated/v2/IFlowV2.sol b/src/interface/deprecated/v2/IFlowV2.sol index a0558eb5..cb1e9d53 100644 --- a/src/interface/deprecated/v2/IFlowV2.sol +++ b/src/interface/deprecated/v2/IFlowV2.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {Evaluable, EvaluableConfig} from "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; struct FlowConfig { // https://github.com/ethereum/solidity/issues/13597 diff --git a/src/interface/IFlowERC1155V3.sol b/src/interface/deprecated/v3/IFlowERC1155V3.sol similarity index 96% rename from src/interface/IFlowERC1155V3.sol rename to src/interface/deprecated/v3/IFlowERC1155V3.sol index 7176c231..1ec09470 100644 --- a/src/interface/IFlowERC1155V3.sol +++ b/src/interface/deprecated/v3/IFlowERC1155V3.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import {SignedContextV1} from "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import {EvaluableConfigV2} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import "./IFlowV3.sol"; diff --git a/src/interface/IFlowERC20V3.sol b/src/interface/deprecated/v3/IFlowERC20V3.sol similarity index 94% rename from src/interface/IFlowERC20V3.sol rename to src/interface/deprecated/v3/IFlowERC20V3.sol index 5c19abb2..0c191742 100644 --- a/src/interface/IFlowERC20V3.sol +++ b/src/interface/deprecated/v3/IFlowERC20V3.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {SourceIndex} from "rain.interpreter.interface/interface/deprecated/IInterpreterV1.sol"; +import {EvaluableConfig, Evaluable} from "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import {MIN_FLOW_SENTINELS, SENTINEL_HIGH_BITS, FlowTransferV1} from "./IFlowV3.sol"; diff --git a/src/interface/IFlowERC721V3.sol b/src/interface/deprecated/v3/IFlowERC721V3.sol similarity index 97% rename from src/interface/IFlowERC721V3.sol rename to src/interface/deprecated/v3/IFlowERC721V3.sol index a8223a30..0af859da 100644 --- a/src/interface/IFlowERC721V3.sol +++ b/src/interface/deprecated/v3/IFlowERC721V3.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; +import "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import "./IFlowV3.sol"; diff --git a/src/interface/IFlowV3.sol b/src/interface/deprecated/v3/IFlowV3.sol similarity index 90% rename from src/interface/IFlowV3.sol rename to src/interface/deprecated/v3/IFlowV3.sol index 1f262621..f305d4f9 100644 --- a/src/interface/IFlowV3.sol +++ b/src/interface/deprecated/v3/IFlowV3.sol @@ -1,26 +1,12 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +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; diff --git a/src/interface/unstable/IFlowERC1155V4.sol b/src/interface/deprecated/v4/IFlowERC1155V4.sol similarity index 90% rename from src/interface/unstable/IFlowERC1155V4.sol rename to src/interface/deprecated/v4/IFlowERC1155V4.sol index db58b3df..ea122c86 100644 --- a/src/interface/unstable/IFlowERC1155V4.sol +++ b/src/interface/deprecated/v4/IFlowERC1155V4.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import {SignedContextV1} from "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import {Evaluable, EvaluableConfigV2} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {EvaluableConfigV2, Evaluable} from "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +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 {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; @@ -13,7 +14,7 @@ import { FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT, FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS, FLOW_ERC1155_MIN_FLOW_SENTINELS -} from "../IFlowERC1155V3.sol"; +} from "../v3/IFlowERC1155V3.sol"; /// Initialization config. /// @param uri As per Open Zeppelin `ERC1155Upgradeable`. diff --git a/src/interface/unstable/IFlowERC20V4.sol b/src/interface/deprecated/v4/IFlowERC20V4.sol similarity index 92% rename from src/interface/unstable/IFlowERC20V4.sol rename to src/interface/deprecated/v4/IFlowERC20V4.sol index 5aa349f3..873424b1 100644 --- a/src/interface/unstable/IFlowERC20V4.sol +++ b/src/interface/deprecated/v4/IFlowERC20V4.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import {SignedContextV1} from "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import {Evaluable, EvaluableConfigV2} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {Evaluable, EvaluableConfigV2} from "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import { FlowERC20IOV1, @@ -11,7 +11,7 @@ import { FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS, FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS, FLOW_ERC20_MIN_FLOW_SENTINELS -} from "../IFlowERC20V3.sol"; +} from "../v3/IFlowERC20V3.sol"; import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; /// Initialization config. diff --git a/src/interface/unstable/IFlowERC721V4.sol b/src/interface/deprecated/v4/IFlowERC721V4.sol similarity index 91% rename from src/interface/unstable/IFlowERC721V4.sol rename to src/interface/deprecated/v4/IFlowERC721V4.sol index 2190a26d..789d7e25 100644 --- a/src/interface/unstable/IFlowERC721V4.sol +++ b/src/interface/deprecated/v4/IFlowERC721V4.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {Evaluable, EvaluableConfigV2} 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 { FlowERC721IOV1, @@ -14,7 +15,7 @@ import { FLOW_ERC721_TOKEN_URI_ENTRYPOINT, FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT, FLOW_ERC721_MIN_FLOW_SENTINELS -} from "../IFlowERC721V3.sol"; +} from "../v3/IFlowERC721V3.sol"; import {RAIN_FLOW_SENTINEL} from "./IFlowV4.sol"; diff --git a/src/interface/unstable/IFlowV4.sol b/src/interface/deprecated/v4/IFlowV4.sol similarity index 96% rename from src/interface/unstable/IFlowV4.sol rename to src/interface/deprecated/v4/IFlowV4.sol index 7a4fd40a..a225df2e 100644 --- a/src/interface/unstable/IFlowV4.sol +++ b/src/interface/deprecated/v4/IFlowV4.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import {SignedContextV1} from "rain.interpreter/src/interface/IInterpreterCallerV2.sol"; -import {EvaluableConfigV2, Evaluable} from "rain.interpreter/src/lib/caller/LibEvaluable.sol"; +import {EvaluableConfigV2, Evaluable} from "rain.interpreter.interface/interface/deprecated/IInterpreterCallerV1.sol"; +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 {Pointer} from "rain.solmem/lib/LibPointer.sol"; @@ -12,12 +13,8 @@ import { ERC721Transfer, ERC1155Transfer, RAIN_FLOW_SENTINEL, - UnregisteredFlow, - UnsupportedERC20Flow, - UnsupportedERC721Flow, - UnsupportedERC1155Flow, MIN_FLOW_SENTINELS -} from "../IFlowV3.sol"; +} from "../v3/IFlowV3.sol"; /// @title IFlowV4 /// @notice Interface for a flow contract that does NOT require native minting diff --git a/src/interface/unstable/IFlowERC1155V5.sol b/src/interface/unstable/IFlowERC1155V5.sol new file mode 100644 index 00000000..5c0aad17 --- /dev/null +++ b/src/interface/unstable/IFlowERC1155V5.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SourceIndexV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; +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 "./IFlowV5.sol"; + +import { + FlowERC1155IOV1, + ERC1155SupplyChange, + FLOW_ERC1155_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC1155_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC1155_MIN_FLOW_SENTINELS +} from "../deprecated/v4/IFlowERC1155V4.sol"; + +SourceIndexV2 constant FLOW_ERC1155_HANDLE_TRANSFER_ENTRYPOINT = SourceIndexV2.wrap(0); + +/// Initialization config. +/// @param uri As per Open Zeppelin `ERC1155Upgradeable`. +/// @param evaluableConfig Config 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 FlowERC1155ConfigV3 { + string uri; + EvaluableConfigV3 evaluableConfig; + EvaluableConfigV3[] flowConfig; +} + +/// @title IFlowERC1155V5 +/// Conceptually identical to `IFlowV5`, 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 `IFlowV5`. +interface IFlowERC1155V5 { + /// Contract has initialized. + /// @param sender `msg.sender` initializing the contract (factory). + /// @param config All initialized config. + event Initialize(address sender, FlowERC1155ConfigV3 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..95223cb4 --- /dev/null +++ b/src/interface/unstable/IFlowERC20V5.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: CAL +pragma solidity ^0.8.18; + +import {SourceIndexV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; +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 { + FlowERC20IOV1, + ERC20SupplyChange, + FLOW_ERC20_HANDLE_TRANSFER_MIN_OUTPUTS, + FLOW_ERC20_HANDLE_TRANSFER_MAX_OUTPUTS, + FLOW_ERC20_MIN_FLOW_SENTINELS +} from "../deprecated/v4/IFlowERC20V4.sol"; +import {RAIN_FLOW_SENTINEL} from "./IFlowV5.sol"; + +SourceIndexV2 constant FLOW_ERC20_HANDLE_TRANSFER_ENTRYPOINT = SourceIndexV2.wrap(0); + +/// 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; + EvaluableConfigV3 evaluableConfig; + EvaluableConfigV3[] 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( + EvaluableV2 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..152a5dad --- /dev/null +++ b/src/interface/unstable/IFlowERC721V5.sol @@ -0,0 +1,80 @@ +// 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 {SourceIndexV2} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.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_MIN_FLOW_SENTINELS +} from "../deprecated/v4/IFlowERC721V4.sol"; + +SourceIndexV2 constant FLOW_ERC721_HANDLE_TRANSFER_ENTRYPOINT = SourceIndexV2.wrap(0); +SourceIndexV2 constant FLOW_ERC721_TOKEN_URI_ENTRYPOINT = SourceIndexV2.wrap(1); + +import {RAIN_FLOW_SENTINEL} from "../deprecated/v4/IFlowV4.sol"; + +/// 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 Config 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; + EvaluableConfigV3 evaluableConfig; + EvaluableConfigV3[] flowConfig; +} + +/// @title IFlowERC721V5 +/// Conceptually identical to `IFlowV5`, 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 `IFlowV5`. +interface IFlowERC721V5 { + /// 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( + EvaluableV2 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..38ba0df8 --- /dev/null +++ b/src/interface/unstable/IFlowV5.sol @@ -0,0 +1,159 @@ +// 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, + MIN_FLOW_SENTINELS +} from "../deprecated/v4/IFlowV4.sol"; +import {UnregisteredFlow} from "../../error/ErrFlow.sol"; + +/// @title IFlowV5 +/// @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 IFlowV5 { + /// 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/lib/LibFlow.sol b/src/lib/LibFlow.sol index 40d1199e..c4c88bdb 100644 --- a/src/lib/LibFlow.sol +++ b/src/lib/LibFlow.sol @@ -1,24 +1,18 @@ // SPDX-License-Identifier: CAL pragma solidity ^0.8.18; -import {IFlowV4, RAIN_FLOW_SENTINEL} from "../interface/unstable/IFlowV4.sol"; +import {IFlowV5, RAIN_FLOW_SENTINEL} from "../interface/unstable/IFlowV5.sol"; import {Pointer} from "rain.solmem/lib/LibPointer.sol"; -import { - FlowTransferV1, - ERC20Transfer, - ERC721Transfer, - ERC1155Transfer, - UnsupportedERC20Flow, - UnsupportedERC721Flow, - UnsupportedERC1155Flow -} from "../interface/unstable/IFlowV4.sol"; -import {IInterpreterStoreV1, DEFAULT_STATE_NAMESPACE} from "rain.interpreter/src/interface/IInterpreterStoreV1.sol"; +import {FlowTransferV1, ERC20Transfer, ERC721Transfer, ERC1155Transfer} from "../interface/unstable/IFlowV5.sol"; +import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/unstable/IInterpreterStoreV2.sol"; import {LibStackSentinel} from "rain.solmem/lib/LibStackSentinel.sol"; +import {DEFAULT_STATE_NAMESPACE} from "rain.interpreter.interface/interface/unstable/IInterpreterV2.sol"; import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; import {IERC1155} from "openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol"; +import {UnsupportedERC20Flow, UnsupportedERC721Flow, UnsupportedERC1155Flow} from "../error/ErrFlow.sol"; /// @title LibFlow /// Standard processing used by all variants of `Flow`. These utilities can't @@ -135,7 +129,7 @@ library LibFlow { /// @param flowTransfer The `FlowTransferV1` to process. /// @param interpreterStore The `IInterpreterStoreV1` to set state on. /// @param kvs The key value pairs to set on the interpreter store. - function flow(FlowTransferV1 memory flowTransfer, IInterpreterStoreV1 interpreterStore, uint256[] memory kvs) + function flow(FlowTransferV1 memory flowTransfer, IInterpreterStoreV2 interpreterStore, uint256[] memory kvs) internal { if (kvs.length > 0) { diff --git a/test/interface/unstable/IFlowV4.t.sol b/test/interface/deprecated/v4/IFlowV4.t.sol similarity index 73% rename from test/interface/unstable/IFlowV4.t.sol rename to test/interface/deprecated/v4/IFlowV4.t.sol index b6325e5e..6eac05ce 100644 --- a/test/interface/unstable/IFlowV4.t.sol +++ b/test/interface/deprecated/v4/IFlowV4.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: CAL pragma solidity =0.8.19; -import "forge-std/Test.sol"; +import {Test} from "forge-std/Test.sol"; -import {IFlowV4, RAIN_FLOW_SENTINEL} from "src/interface/unstable/IFlowV4.sol"; +import {IFlowV4, RAIN_FLOW_SENTINEL} from "src/interface/deprecated/v4/IFlowV4.sol"; import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; contract IFlowV4Test is Test { diff --git a/test/interface/unstable/IFlowV5.t.sol b/test/interface/unstable/IFlowV5.t.sol new file mode 100644 index 00000000..64024ef2 --- /dev/null +++ b/test/interface/unstable/IFlowV5.t.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.19; + +import {Test} from "forge-std/Test.sol"; + +import {IFlowV4, RAIN_FLOW_SENTINEL} from "src/interface/deprecated/v4/IFlowV4.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; + +contract IFlowV5Test is Test { + function testSentinelValue() external { + assertEq( + 0xfea74d0c9bf4a3c28f0dd0674db22a3d7f8bf259c56af19f4ac1e735b156974f, Sentinel.unwrap(RAIN_FLOW_SENTINEL) + ); + } +}