Skip to content

Commit

Permalink
add safe transfer acceptance checking
Browse files Browse the repository at this point in the history
  • Loading branch information
nxt3d committed Jan 29, 2024
1 parent 570323c commit 3d8ea04
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 10 deletions.
6 changes: 3 additions & 3 deletions contracts/l2/FuseController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.17;

import "./L2Registry.sol";
import "./IFuseController.sol";
import "./IControllerUpgrade.sol";
import "./IControllerUpgradeTarget.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

error Unauthorised(bytes32 node, address addr);
Expand All @@ -24,7 +24,7 @@ error nameExpired(bytes32 node);
contract FuseController is Ownable, IFuseController {
L2Registry immutable registry;

IControllerUpgrade upgradeContract;
IControllerUpgradeTarget upgradeContract;

// A struct to hold the unpacked data
struct TokenData {
Expand Down Expand Up @@ -252,7 +252,7 @@ contract FuseController is Ownable, IFuseController {

// A function that sets the upgrade contract.
function setUpgradeController(
IControllerUpgrade _upgradeContract
IControllerUpgradeTarget _upgradeContract
) external onlyOwner {
upgradeContract = _upgradeContract;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ pragma solidity ^0.8.17;

import "./IController.sol";

interface IControllerUpgrade is IController {
interface IControllerUpgradeTarget is IController {
function upgradeFrom(bytes32 node, bytes calldata extraData) external;
}
4 changes: 2 additions & 2 deletions contracts/l2/IFuseController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.17;

import "./IController.sol";
import "./IControllerUpgrade.sol";
import "./IControllerUpgradeTarget.sol";

interface IFuseController is IController {
function expiryOf(bytes32 node) external view returns (uint64);
Expand All @@ -14,6 +14,6 @@ interface IFuseController is IController {
function upgrade(bytes32 node, bytes calldata extraData) external;

function setUpgradeController(
IControllerUpgrade _upgradeController
IControllerUpgradeTarget _upgradeController
) external;
}
101 changes: 101 additions & 0 deletions contracts/l2/L2Registry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/interfaces/IERC1155.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/interfaces/IERC165.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/utils/Address.sol";
Expand All @@ -12,6 +13,8 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import "./IController.sol";

contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {
using Address for address;

struct Record {
string name;
bytes data;
Expand All @@ -25,6 +28,8 @@ contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {

error TokenDoesNotExist(uint256 id);

event NewController(uint256 id, address controller);

constructor(bytes memory root, IMetadataService _metadataService) {
tokens[0].data = root;
metadataService = _metadataService;
Expand Down Expand Up @@ -61,6 +66,8 @@ contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {
) external {
_safeTransferFrom(from, to, id, value, data);
emit TransferSingle(msg.sender, from, to, id, value);

_doSafeTransferAcceptanceCheck(msg.sender, from, to, id, value, data);
}

function safeBatchTransferFrom(
Expand All @@ -75,6 +82,15 @@ contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {
_safeTransferFrom(from, to, ids[i], values[i], data);
}
emit TransferBatch(msg.sender, from, to, ids, values);

_doSafeBatchTransferAcceptanceCheck(
msg.sender,
from,
to,
ids,
values,
data
);
}

function setApprovalForAll(address operator, bool approved) external {
Expand Down Expand Up @@ -215,6 +231,12 @@ contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {
// Only the controller may call this function
require(address(oldController) == msg.sender);

// Fetch the new controller and emit `NewController` if needed.
IController newController = _getController(data);
if (oldController != newController) {
emit NewController(id, address(newController));
}

// Update the data for this node.
tokens[id].data = data;
}
Expand All @@ -240,13 +262,29 @@ contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {
? address(0)
: oldSubnodeController.ownerOfWithData(oldSubnodeData);

// Get the address of the new controller
IController newSubnodeController = _getController(subnodeData);
if (newSubnodeController != oldSubnodeController) {
emit NewController(subnode, address(newSubnodeController));
}

tokens[subnode].data = subnodeData;

// Fetch the to address, if not supplied, for the TransferSingle event.
if (to == address(0) && subnodeData.length >= 20) {
to = _getController(subnodeData).ownerOfWithData(subnodeData);
}

emit TransferSingle(operator, oldOwner, to, subnode, 1);

_doSafeTransferAcceptanceCheck(
operator,
oldOwner,
to,
subnode,
1,
bytes("")
);
}

/**********************
Expand Down Expand Up @@ -292,4 +330,67 @@ contract L2Registry is Ownable, IERC1155, IERC1155MetadataURI {

tokens[id].data = newTokenData;
}

function _doSafeTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256 id,
uint256 amount,
bytes memory data
) private {
if (to.isContract()) {
try
IERC1155Receiver(to).onERC1155Received(
operator,
from,
id,
amount,
data
)
returns (bytes4 response) {
if (
response != IERC1155Receiver(to).onERC1155Received.selector
) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non ERC1155Receiver implementer");
}
}
}

function _doSafeBatchTransferAcceptanceCheck(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) private {
if (to.isContract()) {
try
IERC1155Receiver(to).onERC1155BatchReceived(
operator,
from,
ids,
amounts,
data
)
returns (bytes4 response) {
if (
response !=
IERC1155Receiver(to).onERC1155BatchReceived.selector
) {
revert("ERC1155: ERC1155Receiver rejected tokens");
}
} catch Error(string memory reason) {
revert(reason);
} catch {
revert("ERC1155: transfer to non ERC1155Receiver implementer");
}
}
}
}
8 changes: 4 additions & 4 deletions contracts/l2/mocks/FuseControllerUpgraded.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.17;

import "../L2Registry.sol";
import "../IFuseController.sol";
import "../IControllerUpgrade.sol";
import "../IControllerUpgradeTarget.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

import "hardhat/console.sol";
Expand All @@ -26,11 +26,11 @@ error nameExpired(bytes32 node);
contract FuseControllerUpgraded is
Ownable,
IFuseController,
IControllerUpgrade
IControllerUpgradeTarget
{
L2Registry immutable registry;

IControllerUpgrade upgradeContract;
IControllerUpgradeTarget upgradeContract;

// A struct to hold the unpacked data
struct TokenData {
Expand Down Expand Up @@ -246,7 +246,7 @@ contract FuseControllerUpgraded is

// A function that sets the upgrade contract.
function setUpgradeController(
IControllerUpgrade _upgradeContract
IControllerUpgradeTarget _upgradeContract
) external onlyOwner {
upgradeContract = _upgradeContract;
}
Expand Down

0 comments on commit 3d8ea04

Please sign in to comment.