Skip to content

Commit

Permalink
feat: implement eip-165 for input relays
Browse files Browse the repository at this point in the history
  • Loading branch information
ZzzzHui committed Dec 7, 2023
1 parent e2bc71c commit 90d5ccc
Show file tree
Hide file tree
Showing 15 changed files with 180 additions and 8 deletions.
6 changes: 6 additions & 0 deletions onchain/rollups/.changeset/sharp-actors-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@cartesi/rollups": major
---

Implemented EIP-165 for input relays.
This is because `CartesiDApp` can return an array of input relays. EIP-165 helps to tell which interfaces the relay implements.
3 changes: 2 additions & 1 deletion onchain/rollups/contracts/inputs/IInputRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
pragma solidity ^0.8.8;

import {IInputBox} from "./IInputBox.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/// @title Input Relay interface
interface IInputRelay {
interface IInputRelay is IERC165 {
// Permissionless functions

/// @notice Get the input box used by this input relay.
Expand Down
11 changes: 10 additions & 1 deletion onchain/rollups/contracts/inputs/InputRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ pragma solidity ^0.8.8;

import {IInputRelay} from "./IInputRelay.sol";
import {IInputBox} from "./IInputBox.sol";
import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";

/// @title Input Relay
/// @notice This contract serves as a base for all the other input relays.
contract InputRelay is IInputRelay {
contract InputRelay is IInputRelay, ERC165 {
/// @notice The input box used by the input relay.
IInputBox internal immutable inputBox;

Expand All @@ -18,6 +19,14 @@ contract InputRelay is IInputRelay {
inputBox = _inputBox;
}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC165, IERC165) returns (bool) {
return
interfaceId == type(IInputRelay).interfaceId ||
super.supportsInterface(interfaceId);
}

function getInputBox() external view override returns (IInputBox) {
return inputBox;
}
Expand Down
11 changes: 10 additions & 1 deletion onchain/rollups/contracts/portals/ERC1155BatchPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pragma solidity ^0.8.8;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IERC1155BatchPortal} from "./IERC1155BatchPortal.sol";
import {InputRelay} from "../inputs/InputRelay.sol";
Expand All @@ -14,11 +15,19 @@ import {InputEncoding} from "../common/InputEncoding.sol";
///
/// @notice This contract allows anyone to perform batch transfers of
/// ERC-1155 tokens to a DApp while informing the off-chain machine.
contract ERC1155BatchPortal is InputRelay, IERC1155BatchPortal {
contract ERC1155BatchPortal is IERC1155BatchPortal, InputRelay {
/// @notice Constructs the portal.
/// @param _inputBox The input box used by the portal
constructor(IInputBox _inputBox) InputRelay(_inputBox) {}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, InputRelay) returns (bool) {
return
interfaceId == type(IERC1155BatchPortal).interfaceId ||
super.supportsInterface(interfaceId);
}

function depositBatchERC1155Token(
IERC1155 _token,
address _dapp,
Expand Down
11 changes: 10 additions & 1 deletion onchain/rollups/contracts/portals/ERC1155SinglePortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pragma solidity ^0.8.8;

import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IERC1155SinglePortal} from "./IERC1155SinglePortal.sol";
import {InputRelay} from "../inputs/InputRelay.sol";
Expand All @@ -14,11 +15,19 @@ import {InputEncoding} from "../common/InputEncoding.sol";
///
/// @notice This contract allows anyone to perform single transfers of
/// ERC-1155 tokens to a DApp while informing the off-chain machine.
contract ERC1155SinglePortal is InputRelay, IERC1155SinglePortal {
contract ERC1155SinglePortal is IERC1155SinglePortal, InputRelay {
/// @notice Constructs the portal.
/// @param _inputBox The input box used by the portal
constructor(IInputBox _inputBox) InputRelay(_inputBox) {}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, InputRelay) returns (bool) {
return
interfaceId == type(IERC1155SinglePortal).interfaceId ||
super.supportsInterface(interfaceId);
}

function depositSingleERC1155Token(
IERC1155 _token,
address _dapp,
Expand Down
11 changes: 10 additions & 1 deletion onchain/rollups/contracts/portals/ERC20Portal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pragma solidity ^0.8.8;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IERC20Portal} from "./IERC20Portal.sol";
import {InputRelay} from "../inputs/InputRelay.sol";
Expand All @@ -15,13 +16,21 @@ import {InputEncoding} from "../common/InputEncoding.sol";
///
/// @notice This contract allows anyone to perform transfers of
/// ERC-20 tokens to a DApp while informing the off-chain machine.
contract ERC20Portal is InputRelay, IERC20Portal {
contract ERC20Portal is IERC20Portal, InputRelay {
using SafeERC20 for IERC20;

/// @notice Constructs the portal.
/// @param _inputBox The input box used by the portal
constructor(IInputBox _inputBox) InputRelay(_inputBox) {}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, InputRelay) returns (bool) {
return
interfaceId == type(IERC20Portal).interfaceId ||
super.supportsInterface(interfaceId);
}

function depositERC20Tokens(
IERC20 _token,
address _dapp,
Expand Down
11 changes: 10 additions & 1 deletion onchain/rollups/contracts/portals/ERC721Portal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pragma solidity ^0.8.8;

import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IERC721Portal} from "./IERC721Portal.sol";
import {InputRelay} from "../inputs/InputRelay.sol";
Expand All @@ -14,11 +15,19 @@ import {InputEncoding} from "../common/InputEncoding.sol";
///
/// @notice This contract allows anyone to perform transfers of
/// ERC-721 tokens to a DApp while informing the off-chain machine.
contract ERC721Portal is InputRelay, IERC721Portal {
contract ERC721Portal is IERC721Portal, InputRelay {
/// @notice Constructs the portal.
/// @param _inputBox The input box used by the portal
constructor(IInputBox _inputBox) InputRelay(_inputBox) {}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, InputRelay) returns (bool) {
return
interfaceId == type(IERC721Portal).interfaceId ||
super.supportsInterface(interfaceId);
}

function depositERC721Token(
IERC721 _token,
address _dapp,
Expand Down
12 changes: 11 additions & 1 deletion onchain/rollups/contracts/portals/EtherPortal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

pragma solidity ^0.8.8;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IEtherPortal} from "./IEtherPortal.sol";
import {InputRelay} from "../inputs/InputRelay.sol";
import {IInputBox} from "../inputs/IInputBox.sol";
Expand All @@ -12,14 +14,22 @@ import {InputEncoding} from "../common/InputEncoding.sol";
///
/// @notice This contract allows anyone to perform transfers of
/// Ether to a DApp while informing the off-chain machine.
contract EtherPortal is InputRelay, IEtherPortal {
contract EtherPortal is IEtherPortal, InputRelay {
/// @notice Raised when the Ether transfer fails.
error EtherTransferFailed();

/// @notice Constructs the portal.
/// @param _inputBox The input box used by the portal
constructor(IInputBox _inputBox) InputRelay(_inputBox) {}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, InputRelay) returns (bool) {
return
interfaceId == type(IEtherPortal).interfaceId ||
super.supportsInterface(interfaceId);
}

function depositEther(
address _dapp,
bytes calldata _execLayerData
Expand Down
12 changes: 11 additions & 1 deletion onchain/rollups/contracts/relays/DAppAddressRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

pragma solidity ^0.8.8;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import {IDAppAddressRelay} from "./IDAppAddressRelay.sol";
import {InputRelay} from "../inputs/InputRelay.sol";
import {IInputBox} from "../inputs/IInputBox.sol";
Expand All @@ -12,11 +14,19 @@ import {InputEncoding} from "../common/InputEncoding.sol";
///
/// @notice This contract allows anyone to inform the off-chain machine
/// of the address of the DApp contract in a trustless and permissionless way.
contract DAppAddressRelay is InputRelay, IDAppAddressRelay {
contract DAppAddressRelay is IDAppAddressRelay, InputRelay {
/// @notice Constructs the relay.
/// @param _inputBox The input box used by the relay
constructor(IInputBox _inputBox) InputRelay(_inputBox) {}

function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, InputRelay) returns (bool) {
return
interfaceId == type(IDAppAddressRelay).interfaceId ||
super.supportsInterface(interfaceId);
}

function relayDAppAddress(address _dapp) external override {
bytes memory input = InputEncoding.encodeDAppAddressRelay(_dapp);
inputBox.addInput(_dapp, input);
Expand Down
17 changes: 17 additions & 0 deletions onchain/rollups/test/foundry/portals/ERC1155BatchPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {IERC1155BatchPortal} from "contracts/portals/IERC1155BatchPortal.sol";
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {InputBox} from "contracts/inputs/InputBox.sol";
import {IInputRelay} from "contracts/inputs/IInputRelay.sol";

contract BatchToken is ERC1155 {
constructor(
Expand Down Expand Up @@ -80,6 +82,21 @@ contract ERC1155BatchPortalTest is Test {
bob = vm.addr(3);
}

function testSupportsInterface(bytes4 _randomInterfaceId) public {
assertTrue(
portal.supportsInterface(type(IERC1155BatchPortal).interfaceId)
);
assertTrue(portal.supportsInterface(type(IInputRelay).interfaceId));
assertTrue(portal.supportsInterface(type(IERC165).interfaceId));

assertFalse(portal.supportsInterface(bytes4(0xffffffff)));

vm.assume(_randomInterfaceId != type(IERC1155BatchPortal).interfaceId);
vm.assume(_randomInterfaceId != type(IInputRelay).interfaceId);
vm.assume(_randomInterfaceId != type(IERC165).interfaceId);
assertFalse(portal.supportsInterface(_randomInterfaceId));
}

function testGetInputBoxBatch() public {
assertEq(address(portal.getInputBox()), address(inputBox));
}
Expand Down
17 changes: 17 additions & 0 deletions onchain/rollups/test/foundry/portals/ERC1155SinglePortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {IERC1155SinglePortal} from "contracts/portals/IERC1155SinglePortal.sol";
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {InputBox} from "contracts/inputs/InputBox.sol";
import {IInputRelay} from "contracts/inputs/IInputRelay.sol";

contract NormalToken is ERC1155 {
constructor(
Expand Down Expand Up @@ -90,6 +92,21 @@ contract ERC1155SinglePortalTest is Test {
bob = vm.addr(3);
}

function testSupportsInterface(bytes4 _randomInterfaceId) public {
assertTrue(
portal.supportsInterface(type(IERC1155SinglePortal).interfaceId)
);
assertTrue(portal.supportsInterface(type(IInputRelay).interfaceId));
assertTrue(portal.supportsInterface(type(IERC165).interfaceId));

assertFalse(portal.supportsInterface(bytes4(0xffffffff)));

vm.assume(_randomInterfaceId != type(IERC1155SinglePortal).interfaceId);
vm.assume(_randomInterfaceId != type(IInputRelay).interfaceId);
vm.assume(_randomInterfaceId != type(IERC165).interfaceId);
assertFalse(portal.supportsInterface(_randomInterfaceId));
}

function testGetInputBox() public {
assertEq(address(portal.getInputBox()), address(inputBox));
}
Expand Down
15 changes: 15 additions & 0 deletions onchain/rollups/test/foundry/portals/ERC20Portal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {ERC20Portal} from "contracts/portals/ERC20Portal.sol";
import {IERC20Portal} from "contracts/portals/IERC20Portal.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {InputBox} from "contracts/inputs/InputBox.sol";
import {IInputRelay} from "contracts/inputs/IInputRelay.sol";

contract NormalToken is ERC20 {
constructor(
Expand Down Expand Up @@ -137,6 +139,19 @@ contract ERC20PortalTest is Test {
dapp = vm.addr(2);
}

function testSupportsInterface(bytes4 _randomInterfaceId) public {
assertTrue(portal.supportsInterface(type(IERC20Portal).interfaceId));
assertTrue(portal.supportsInterface(type(IInputRelay).interfaceId));
assertTrue(portal.supportsInterface(type(IERC165).interfaceId));

assertFalse(portal.supportsInterface(bytes4(0xffffffff)));

vm.assume(_randomInterfaceId != type(IERC20Portal).interfaceId);
vm.assume(_randomInterfaceId != type(IInputRelay).interfaceId);
vm.assume(_randomInterfaceId != type(IERC165).interfaceId);
assertFalse(portal.supportsInterface(_randomInterfaceId));
}

function testGetInputBox() public {
assertEq(address(portal.getInputBox()), address(inputBox));
}
Expand Down
15 changes: 15 additions & 0 deletions onchain/rollups/test/foundry/portals/ERC721Portal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {IERC721Portal} from "contracts/portals/IERC721Portal.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {InputBox} from "contracts/inputs/InputBox.sol";
import {InputEncoding} from "contracts/common/InputEncoding.sol";
import {IInputRelay} from "contracts/inputs/IInputRelay.sol";

contract NormalToken is ERC721 {
constructor(
Expand Down Expand Up @@ -114,6 +116,19 @@ contract ERC721PortalTest is Test {
alice = vm.addr(1);
}

function testSupportsInterface(bytes4 _randomInterfaceId) public {
assertTrue(portal.supportsInterface(type(IERC721Portal).interfaceId));
assertTrue(portal.supportsInterface(type(IInputRelay).interfaceId));
assertTrue(portal.supportsInterface(type(IERC165).interfaceId));

assertFalse(portal.supportsInterface(bytes4(0xffffffff)));

vm.assume(_randomInterfaceId != type(IERC721Portal).interfaceId);
vm.assume(_randomInterfaceId != type(IInputRelay).interfaceId);
vm.assume(_randomInterfaceId != type(IERC165).interfaceId);
assertFalse(portal.supportsInterface(_randomInterfaceId));
}

function testGetInputBox() public {
assertEq(address(portal.getInputBox()), address(inputBox));
}
Expand Down
19 changes: 19 additions & 0 deletions onchain/rollups/test/foundry/portals/EtherPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
pragma solidity ^0.8.8;

import {Test} from "forge-std/Test.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {EtherPortal} from "contracts/portals/EtherPortal.sol";
import {IEtherPortal} from "contracts/portals/IEtherPortal.sol";
import {IInputBox} from "contracts/inputs/IInputBox.sol";
import {InputBox} from "contracts/inputs/InputBox.sol";
import {InputEncoding} from "contracts/common/InputEncoding.sol";
import {IInputRelay} from "contracts/inputs/IInputRelay.sol";

contract BadEtherReceiver {
receive() external payable {
Expand Down Expand Up @@ -65,6 +67,23 @@ contract EtherPortalTest is Test {
dapp = address(0x12345678);
}

function testSupportsInterface(bytes4 _randomInterfaceId) public {
assertTrue(
etherPortal.supportsInterface(type(IEtherPortal).interfaceId)
);
assertTrue(
etherPortal.supportsInterface(type(IInputRelay).interfaceId)
);
assertTrue(etherPortal.supportsInterface(type(IERC165).interfaceId));

assertFalse(etherPortal.supportsInterface(bytes4(0xffffffff)));

vm.assume(_randomInterfaceId != type(IEtherPortal).interfaceId);
vm.assume(_randomInterfaceId != type(IInputRelay).interfaceId);
vm.assume(_randomInterfaceId != type(IERC165).interfaceId);
assertFalse(etherPortal.supportsInterface(_randomInterfaceId));
}

function testGetInputBox() public {
assertEq(address(etherPortal.getInputBox()), address(inputBox));
}
Expand Down
Loading

0 comments on commit 90d5ccc

Please sign in to comment.