Skip to content

Commit

Permalink
feat: implement CommunityTokenDeployer contract
Browse files Browse the repository at this point in the history
This commit introduces the `CommunityTokenDeployer` contract discussed
in status-im/status-desktop#11954.

The idea is that, instead of having accounts deploy `OwnerToken` and
`MasterToken` directly, they'd use a deployer contract instead, which
maintains a registry of known `OwnerToken` addresses, mapped to Status
community addresses.

The following changes have been made:

It was, and still is, a requirement that both, `OwnerToken` and
`MasterToken` are deployed within a single transaction, so that when
something goes wrong, we don't end up in an inconsistent state.

That's why `OwnerToken` used to instantiated `MasterToken` and required
all of its constructor arguments as well.

Unfortunately, this resulted in compilation issues in the context of the
newly introduce deployer contract, where there are too many function
arguments.

Because we now delegate deployment to a dedicated contract, we can
instantiate both `OwnerToken` and `MasterToken` in a single transaction,
without having `OwnerToken` being responsible to instantiate
`MasterToken`.

This fixes the compilation issues and simplifies the constructor of
`OwnerToken`.

The new `CommunityTokenDeployer` contract is now responsble for
deploying the aforementioned tokens and ensures that they are deployed
within a single transaction.

To deploy an `OwnerToken` and `MasterToken` accounts can now call
`CommunityDeloyerToken.deploy(TokenConfig, TokenConfig,
DeploymentSignature)`.

The `DeploymentSignature` uses `EIP712` structured type hash data to let
the contract verify that the deployer is allowed to deploy the contracts
on behalf of a community account.
  • Loading branch information
0x-r4bbit committed Sep 19, 2023
1 parent e52eff0 commit 5542cc0
Show file tree
Hide file tree
Showing 25 changed files with 1,357 additions and 74 deletions.
66 changes: 58 additions & 8 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,27 +1,77 @@
AddEntryTest:test_AddEntry() (gas: 44369)
AddEntryTest:test_RevertWhen_EntryAlreadyExists() (gas: 42598)
AddEntryTest:test_RevertWhen_InvalidAddress() (gas: 25087)
AddEntryTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 14804)
CollectibleV1Test:test_Deployment() (gas: 38626)
CommunityERC20Test:test_Deployment() (gas: 29720)
CommunityTokenDeployerTest:test_Deployment() (gas: 14805)
CreateTest:test_Create() (gas: 2252539)
CreateTest:test_Create() (gas: 2517349)
CreateTest:test_RevertWhen_InvalidOwnerTokenAddress() (gas: 15523)
CreateTest:test_RevertWhen_InvalidReceiverAddress() (gas: 15656)
CreateTest:test_RevertWhen_InvalidSignerPublicKey() (gas: 17057)
CreateTest:test_RevertWhen_InvalidTokenMetadata() (gas: 27936)
CreateTest:test_RevertWhen_InvalidTokenMetadata() (gas: 28173)
CreateTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 16421)
CreateTest:test_RevertWhen_SenderIsNotTokenDeployer() (gas: 16524)
DeployContracts:test() (gas: 120)
DeployOwnerAndMasterToken:test() (gas: 120)
DeployTest:test_Deploy() (gas: 4840232)
DeployTest:test_Deployment() (gas: 14925)
DeployTest:test_RevertWhen_AlreadyDeployed() (gas: 4836115)
DeployTest:test_RevertWhen_DeploymentSignatureExpired() (gas: 53642)
DeployTest:test_RevertWhen_InvalidCommunityAddress() (gas: 51341)
DeployTest:test_RevertWhen_InvalidDeployerAddress() (gas: 55329)
DeployTest:test_RevertWhen_InvalidDeploymentSignature() (gas: 65663)
DeployTest:test_RevertWhen_InvalidSignerPublicKey() (gas: 53435)
DeployTest:test_RevertWhen_InvalidTokenMetadata() (gas: 2639807)
DeploymentTest:test_Deployment() (gas: 14671)
DeploymentTest:test_Deployment() (gas: 14671)
DeploymentTest:test_Deployment() (gas: 17250)
GetEntryTest:test_ReturnZeroAddressIfEntryDoesNotExist() (gas: 11906)
MintToTest:test_Deployment() (gas: 29742)
MintToTest:test_Deployment() (gas: 38626)
MintToTest:test_Deployment() (gas: 85415)
MintToTest:test_MintTo() (gas: 509977)
MintToTest:test_Deployment() (gas: 85460)
MintToTest:test_MintTo() (gas: 509082)
MintToTest:test_RevertWhen_AddressesAndAmountsAreNotEqualLength() (gas: 24193)
MintToTest:test_RevertWhen_MaxSupplyIsReached() (gas: 23267)
MintToTest:test_RevertWhen_MaxSupplyIsReached() (gas: 505463)
MintToTest:test_RevertWhen_MaxSupplyReached() (gas: 123426)
MintToTest:test_RevertWhen_SenderIsNotOwner() (gas: 36358)
OwnerTokenTest:test_Deployment() (gas: 85415)
OwnerTokenTest:test_Deployment() (gas: 85460)
RemoteBurnTest:test_Deployment() (gas: 38626)
RemoteBurnTest:test_Deployment() (gas: 85437)
RemoteBurnTest:test_Deployment() (gas: 85482)
RemoteBurnTest:test_RemoteBurn() (gas: 455285)
RemoteBurnTest:test_RevertWhen_RemoteBurn() (gas: 19499)
RemoteBurnTest:test_RevertWhen_SenderIsNotOwner() (gas: 25211)
SetCommunityTokenDeployerAddressTest:test_RevertWhen_InvalidTokenDeployerAddress() (gas: 12963)
SetCommunityTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12504)
SetCommunityTokenDeployerAddressTest:test_SetCommunityTokenDeployerAddress() (gas: 22807)
SetDeploymentRegistryAddressTest:test_Deployment() (gas: 14827)
SetDeploymentRegistryAddressTest:test_RevertWhen_InvalidDeploymentRegistryAddress() (gas: 13014)
SetDeploymentRegistryAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12508)
SetDeploymentRegistryAddressTest:test_SetDeploymentRegistryAddress() (gas: 22903)
SetMasterTokenFactoryAddressTest:test_Deployment() (gas: 14805)
SetMasterTokenFactoryAddressTest:test_RevertWhen_InvalidTokenFactoryAddress() (gas: 13014)
SetMasterTokenFactoryAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12487)
SetMasterTokenFactoryAddressTest:test_SetOwnerTokenFactoryAddress() (gas: 22861)
SetMaxSupplyTest:test_Deployment() (gas: 29720)
SetMaxSupplyTest:test_Deployment() (gas: 85437)
SetMaxSupplyTest:test_Deployment() (gas: 85482)
SetMaxSupplyTest:test_RevertWhen_CalledBecauseMaxSupplyIsLocked() (gas: 16521)
SetMaxSupplyTest:test_RevertWhen_MaxSupplyLowerThanTotalSupply() (gas: 149095)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 12852)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 12823)
SetMaxSupplyTest:test_RevertWhen_SenderIsNotOwner() (gas: 17335)
SetMaxSupplyTest:test_SetMaxSupply() (gas: 15597)
SetSignerPublicKeyTest:test_Deployment() (gas: 85415)
SetOwnerTokenFactoryAddressTest:test_Deployment() (gas: 14805)
SetOwnerTokenFactoryAddressTest:test_RevertWhen_InvalidTokenFactoryAddress() (gas: 12992)
SetOwnerTokenFactoryAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12465)
SetOwnerTokenFactoryAddressTest:test_SetOwnerTokenFactoryAddress() (gas: 22862)
SetSignerPublicKeyTest:test_Deployment() (gas: 85460)
SetSignerPublicKeyTest:test_RevertWhen_SenderIsNotOwner() (gas: 18036)
SetSignerPublicKeyTest:test_SetSignerPublicKey() (gas: 26357)
SetSignerPublicKeyTest:test_SetSignerPublicKey() (gas: 26357)
SetTokenDeployerAddressTest:test_RevertWhen_InvalidTokenDeployerAddress() (gas: 12964)
SetTokenDeployerAddressTest:test_RevertWhen_InvalidTokenDeployerAddress() (gas: 12964)
SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
SetTokenDeployerAddressTest:test_RevertWhen_SenderIsNotOwner() (gas: 12438)
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
SetTokenDeployerAddressTest:test_SetTokenDeployerAddress() (gas: 22768)
91 changes: 91 additions & 0 deletions contracts/CommunityOwnerTokenRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.17;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IAddressRegistry } from "./interfaces/IAddressRegistry.sol";
import { OwnerToken } from "./tokens/OwnerToken.sol";

/**
* @title CommunityOwnerTokenRegistry contract
* @author 0x-r4bbit
*
* This contract serves as a simple registry to map Status community addresses
* to Status community `OwnerToken` addresses.
* The `CommunityTokenDeployer` contract uses this registry contract to maintain
* a list of community address and their token addresses.
* @notice This contract will be deployed by Status similar to the `CommunityTokenDeployer`
* contract.
* @notice This contract maps community addresses to `OwnerToken` addresses.
* @notice Only one entry per community address can exist in the registry.
* @dev This registry has been extracted into its own contract so that it's possible
* to introduce different version of a `CommunityDeployerContract` without needing to
* migrate existing registry data, as the deployer contract would simply point at this
* registry contract.
* @dev Only `tokenDeployer` can add entries to the registry.
*/
contract CommunityOwnerTokenRegistry is IAddressRegistry, Ownable {
error CommunityOwnerTokenRegistry_NotAuthorized();
error CommunityOwnerTokenRegistry_EntryAlreadyExists();
error CommunityOwnerTokenRegistry_InvalidAddress();

event TokenDeployerAddressChange(address indexed);
event AddEntry(address indexed, address indexed);

/// @dev The address of the token deployer contract.
address public tokenDeployer;

mapping(address => address) public communityAddressToTokenAddress;

modifier onlyTokenDeployer() {
if (msg.sender != tokenDeployer) {
revert CommunityOwnerTokenRegistry_NotAuthorized();
}
_;
}

/**
* @notice Sets the address of the community token deployer contract. This is needed to
* ensure only the known token deployer contract can add new entries to the registry.
* @dev Only the owner of this contract can call this function.
* @dev Emits a {TokenDeployerAddressChange} event.
*
* @param _tokenDeployer The address of the community token deployer contract
*/
function setCommunityTokenDeployerAddress(address _tokenDeployer) external onlyOwner {
if (_tokenDeployer == address(0)) {
revert CommunityOwnerTokenRegistry_InvalidAddress();
}
tokenDeployer = _tokenDeployer;
emit TokenDeployerAddressChange(tokenDeployer);
}

/**
* @notice Adds an entry to the registry. Only one entry per community address can exist.
* @dev Only the token deployer contract can call this function.
* @dev Reverts when the entry already exists.
* @dev Reverts when either `_communityAddress` or `_tokenAddress` are zero addresses.
* @dev Emits a {AddEntry} event.
*/
function addEntry(address _communityAddress, address _tokenAddress) external onlyTokenDeployer {
if (getEntry(_communityAddress) != address(0)) {
revert CommunityOwnerTokenRegistry_EntryAlreadyExists();
}

if (_communityAddress == address(0) || _tokenAddress == address(0)) {
revert CommunityOwnerTokenRegistry_InvalidAddress();
}

communityAddressToTokenAddress[_communityAddress] = _tokenAddress;
emit AddEntry(_communityAddress, _tokenAddress);
}

/**
* @notice Returns the owner token address for a given community address.
* @param _communityAddress The community address to look up an owner token address.
* @return address The owner token address for the community addres, or zero address .
*/
function getEntry(address _communityAddress) public view returns (address) {
return communityAddressToTokenAddress[_communityAddress];
}
}
Loading

0 comments on commit 5542cc0

Please sign in to comment.