Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TD-1396] Add zone manager role to ImmutableSignedZoneV2 #210

Merged
merged 1 commit into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
// solhint-disable-next-line compiler-version
pragma solidity ^0.8.20;

import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol";
import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {AccessControlEnumerable} from "openzeppelin-contracts-5.0.2/access/extensions/AccessControlEnumerable.sol";
import {ECDSA} from "openzeppelin-contracts-5.0.2/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "openzeppelin-contracts-5.0.2/utils/cryptography/MessageHashUtils.sol";
import {ERC165} from "openzeppelin-contracts-5.0.2/utils/introspection/ERC165.sol";
import {Math} from "openzeppelin-contracts-5.0.2/utils/math/Math.sol";
import {ZoneInterface} from "seaport/contracts/interfaces/ZoneInterface.sol";
import {ZoneParameters, Schema, ReceivedItem} from "seaport-types/src/lib/ConsiderationStructs.sol";
import {ZoneAccessControl} from "./ZoneAccessControl.sol";
import {SIP5Interface} from "./interfaces/SIP5Interface.sol";
import {SIP6Interface} from "./interfaces/SIP6Interface.sol";
import {SIP7Interface} from "./interfaces/SIP7Interface.sol";
Expand All @@ -24,11 +25,11 @@ import {SIP7Interface} from "./interfaces/SIP7Interface.sol";
*/
contract ImmutableSignedZoneV2 is
ERC165,
ZoneAccessControl,
ZoneInterface,
SIP5Interface,
SIP6Interface,
SIP7Interface,
AccessControlEnumerable
SIP7Interface
{
/// @dev The EIP-712 domain type hash.
bytes32 private constant _EIP_712_DOMAIN_TYPEHASH = keccak256(
Expand Down Expand Up @@ -82,7 +83,9 @@ contract ImmutableSignedZoneV2 is
* @param owner The address of the owner of this contract. Specified in the
* constructor to be CREATE2 / CREATE3 compatible.
*/
constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner) {
constructor(string memory zoneName, string memory apiEndpoint, string memory documentationURI, address owner)
ZoneAccessControl(owner)
{
// Set the zone name.
_ZONE_NAME = zoneName;

Expand All @@ -100,17 +103,14 @@ contract ImmutableSignedZoneV2 is

// Emit an event to signal a SIP-5 contract has been deployed.
emit SeaportCompatibleContractDeployed();

// Grant admin role to the specified owner.
_grantRole(DEFAULT_ADMIN_ROLE, owner);
}

/**
* @notice Add a new signer to the zone.
*
* @param signer The new signer address to add.
*/
function addSigner(address signer) external override onlyRole(DEFAULT_ADMIN_ROLE) {
function addSigner(address signer) external override onlyRole(ZONE_MANAGER_ROLE) {
// Do not allow the zero address to be added as a signer.
if (signer == address(0)) {
revert SignerCannotBeZeroAddress();
Expand Down Expand Up @@ -140,7 +140,7 @@ contract ImmutableSignedZoneV2 is
*
* @param signer The signer address to remove.
*/
function removeSigner(address signer) external override onlyRole(DEFAULT_ADMIN_ROLE) {
function removeSigner(address signer) external override onlyRole(ZONE_MANAGER_ROLE) {
// Revert if the signer is not active.
if (!_signers[signer].active) {
revert SignerNotActive(signer);
Expand All @@ -158,7 +158,7 @@ contract ImmutableSignedZoneV2 is
*
* @param newApiEndpoint The new API endpoint.
*/
function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyRole(DEFAULT_ADMIN_ROLE) {
function updateAPIEndpoint(string calldata newApiEndpoint) external override onlyRole(ZONE_MANAGER_ROLE) {
_apiEndpoint = newApiEndpoint;
}

Expand All @@ -170,7 +170,7 @@ contract ImmutableSignedZoneV2 is
function updateDocumentationURI(string calldata newDocumentationURI)
external
override
onlyRole(DEFAULT_ADMIN_ROLE)
onlyRole(ZONE_MANAGER_ROLE)
{
_documentationURI = newDocumentationURI;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ flowchart LR
seaport -- 3a. transferFrom --> erc20[IERC20.sol]
seaport -- 3b. transferFrom --> erc721[IERC721.sol]
seaport -- 3c. safeTransferFrom --> erc1155[IERC1155.sol]
seaport -- 4. validateOrder --> zone[ImmutableSignedZoneV2.sol]
seaport -- 4. validateOrder --> Zone
subgraph Zone
direction TB
zone[ImmutableSignedZoneV2.sol] --> AccessControlEnumerable.sol
end
```

The sequence of events is as follows:

1. The client makes a HTTP `POST .../fulfillment-data` request to the Immutable Orderbook, which will construct signs and sign an `extraData` payload to return to the client
1. The client makes a HTTP `POST .../fulfillment-data` request to the Immutable Orderbook, which will construct and sign an `extraData` payload to return to the client
2. The client calls `fulfillAdvancedOrder` or `fulfillAvailableAdavancedOrders` on `ImmutableSeaport.sol` to fulfill an order
3. `ImmutableSeaport.sol` executes the fufilment by transferring items between parties
4. `ImmutableSeaport.sol` calls `validateOrder` on `ImmutableSignedZoneV2.sol`, passing it the fulfilment execution details as well as the `extraData` parameter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Immutable Pty Ltd 2018 - 2024
// SPDX-License-Identifier: Apache-2

// solhint-disable-next-line compiler-version
pragma solidity ^0.8.20;

import {AccessControl} from "openzeppelin-contracts-5.0.2/access/AccessControl.sol";
import {IAccessControl} from "openzeppelin-contracts-5.0.2/access/IAccessControl.sol";
import {AccessControlEnumerable} from "openzeppelin-contracts-5.0.2/access/extensions/AccessControlEnumerable.sol";
import {ZoneAccessControlEventsAndErrors} from
"../../../../../../contracts/trading/seaport/zones/immutable-signed-zone/v2/interfaces/ZoneAccessControlEventsAndErrors.sol";

/**
* @notice ZoneAccessControl encapsulates access control functionality for the zone.
*/
abstract contract ZoneAccessControl is AccessControlEnumerable, ZoneAccessControlEventsAndErrors {
/// @dev Zone manager manages the zone.
bytes32 public constant ZONE_MANAGER_ROLE = bytes32("ZONE_MANAGER");

/**
* @notice Constructor to setup initial default admin.
*
* @param owner The address to assign the DEFAULT_ADMIN_ROLE.
*/
constructor(address owner) {
// Grant admin role to the specified owner.
_grantRole(DEFAULT_ADMIN_ROLE, owner);
}

/**
* @inheritdoc AccessControl
*/
function revokeRole(bytes32 role, address account) public override(AccessControl, IAccessControl) onlyRole(getRoleAdmin(role)) {
super.revokeRole(role, account);

if (role == DEFAULT_ADMIN_ROLE && super.getRoleMemberCount(DEFAULT_ADMIN_ROLE) == 0) {
revert LastDefaultAdminRole(account);
}
}

/**
* @inheritdoc AccessControl
*/
function renounceRole(bytes32 role, address callerConfirmation) public override(AccessControl, IAccessControl) {
super.renounceRole(role, callerConfirmation);

if (role == DEFAULT_ADMIN_ROLE && super.getRoleMemberCount(DEFAULT_ADMIN_ROLE) == 0) {
revert LastDefaultAdminRole(callerConfirmation);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Immutable Pty Ltd 2018 - 2024
// SPDX-License-Identifier: Apache-2

// solhint-disable compiler-version
pragma solidity ^0.8.17;

/**
* @notice ZoneAccessControlEventsAndErrors contains errors and events
* related to zone access control.
*/
interface ZoneAccessControlEventsAndErrors {
/**
* @dev Revert with an error if revoking last DEFAULT_ADMIN_ROLE.
*/
error LastDefaultAdminRole(address account);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper
"./foundry-out/ImmutableSignedZoneV2Harness.t.sol/ImmutableSignedZoneV2Harness.json";

address private immutable OWNER = makeAddr("owner");
address private immutable ZONE_MANAGER = makeAddr("zone_manager");
address private immutable SIGNER;
uint256 private immutable SIGNER_PRIVATE_KEY;
address private immutable FULFILLER = makeAddr("fulfiller");
Expand Down Expand Up @@ -98,6 +99,10 @@ contract ImmutableSeaportSignedZoneV2IntegrationTest is Test, SigningTestHelper
)
);
vm.prank(OWNER);
bytes32 managerRole = zone.ZONE_MANAGER_ROLE();
vm.prank(OWNER);
zone.grantRole(managerRole, ZONE_MANAGER);
vm.prank(ZONE_MANAGER);
zone.addSigner(SIGNER);

// seaport
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import {SIP7Interface} from
// solhint-disable func-name-mixedcase

interface IImmutableSignedZoneV2Harness is ZoneInterface, SIP7Interface {
function grantRole(bytes32 role, address account) external;

function DEFAULT_ADMIN_ROLE() external view returns (bytes32);

function ZONE_MANAGER_ROLE() external view returns (bytes32);

function exposed_domainSeparator() external view returns (bytes32);

function exposed_deriveDomainSeparator() external view returns (bytes32 domainSeparator);
Expand Down
Loading
Loading