Skip to content

Commit

Permalink
feat(world-modules): add ERC20 module
Browse files Browse the repository at this point in the history
  • Loading branch information
alvrs committed Oct 18, 2023
1 parent 8225f50 commit 17c09e7
Show file tree
Hide file tree
Showing 19 changed files with 2,485 additions and 1 deletion.
30 changes: 30 additions & 0 deletions packages/world-modules/gas-report.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
[
{
"file": "test/ERC20.t.sol",
"test": "testApprove",
"name": "approve",
"gasUsed": 114450
},
{
"file": "test/ERC20.t.sol",
"test": "testBurn",
"name": "burn",
"gasUsed": 75963
},
{
"file": "test/ERC20.t.sol",
"test": "testMint",
"name": "mint",
"gasUsed": 161802
},
{
"file": "test/ERC20.t.sol",
"test": "testTransfer",
"name": "transfer",
"gasUsed": 93069
},
{
"file": "test/ERC20.t.sol",
"test": "testTransferFrom",
"name": "transferFrom",
"gasUsed": 130371
},
{
"file": "test/KeysInTableModule.t.sol",
"test": "testInstallComposite",
Expand Down
47 changes: 47 additions & 0 deletions packages/world-modules/mud.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,53 @@ export default mudConfig({
},
tableIdArgument: true,
},
/************************************************************************
*
* ERC20 MODULE
*
************************************************************************/
Balances: {
directory: "modules/erc20/tables",
keySchema: {
account: "address",
},
valueSchema: {
value: "uint256",
},
tableIdArgument: true,
},
Allowances: {
directory: "modules/erc20/tables",
keySchema: {
account: "address",
spender: "address",
},
valueSchema: {
value: "uint256",
},
tableIdArgument: true,
},
Metadata: {
directory: "modules/erc20/tables",
keySchema: {},
valueSchema: {
totalSupply: "uint256",
decimals: "uint8",
name: "string",
symbol: "string",
},
tableIdArgument: true,
},
ERC20Registry: {
directory: "modules/erc20/tables",
keySchema: {
namespaceId: "ResourceId",
},
valueSchema: {
erc20Address: "address",
},
tableIdArgument: true,
},
},

excludeSystems: ["UniqueEntitySystem"],
Expand Down
4 changes: 4 additions & 0 deletions packages/world-modules/src/index.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ import { UniqueEntity } from "./modules/uniqueentity/tables/UniqueEntity.sol";
import { CallboundDelegations, CallboundDelegationsTableId } from "./modules/std-delegations/tables/CallboundDelegations.sol";
import { TimeboundDelegations, TimeboundDelegationsTableId } from "./modules/std-delegations/tables/TimeboundDelegations.sol";
import { PuppetRegistry } from "./modules/puppet/tables/PuppetRegistry.sol";
import { Balances } from "./modules/erc20/tables/Balances.sol";
import { Allowances } from "./modules/erc20/tables/Allowances.sol";
import { Metadata, MetadataData } from "./modules/erc20/tables/Metadata.sol";
import { ERC20Registry } from "./modules/erc20/tables/ERC20Registry.sol";
4 changes: 3 additions & 1 deletion packages/world-modules/src/interfaces/IBaseWorld.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ pragma solidity >=0.8.21;
import { IStore } from "@latticexyz/store/src/IStore.sol";
import { IWorldKernel } from "@latticexyz/world/src/IWorldKernel.sol";

import { IERC20System } from "./IERC20System.sol";

/**
* @title IBaseWorld
* @notice This interface integrates all systems and associated function selectors
* that are dynamically registered in the World during deployment.
* @dev This is an autogenerated file; do not edit manually.
*/
interface IBaseWorld is IStore, IWorldKernel {
interface IBaseWorld is IStore, IWorldKernel, IERC20System {

}
32 changes: 32 additions & 0 deletions packages/world-modules/src/interfaces/IERC20System.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

/* Autogenerated file. Do not edit manually. */

/**
* @title IERC20System
* @dev This interface is automatically generated from the corresponding system contract. Do not edit manually.
*/
interface IERC20System {
function name() external view returns (string memory);

function symbol() external view returns (string memory);

function decimals() external view returns (uint8);

function totalSupply() external view returns (uint256);

function balanceOf(address account) external view returns (uint256);

function allowance(address owner, address spender) external view returns (uint256);

function transfer(address to, uint256 value) external returns (bool);

function approve(address spender, uint256 value) external returns (bool);

function transferFrom(address from, address to, uint256 value) external returns (bool);

function mint(address account, uint256 value) external;

function burn(address account, uint256 value) external;
}
95 changes: 95 additions & 0 deletions packages/world-modules/src/modules/erc20/ERC20Module.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.21;

import { ResourceIds } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol";
import { ResourceId } from "@latticexyz/store/src/ResourceId.sol";
import { Module } from "@latticexyz/world/src/Module.sol";
import { WorldResourceIdLib } from "@latticexyz/world/src/WorldResourceId.sol";
import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
import { InstalledModules } from "@latticexyz/world/src/codegen/tables/InstalledModules.sol";

import { Puppet } from "../puppet/Puppet.sol";
import { registerPuppet } from "../puppet/registerPuppet.sol";
import { MODULE_NAME as PUPPET_MODULE_NAME } from "../puppet/constants.sol";
import { PuppetModule } from "../puppet/PuppetModule.sol";

import { MODULE_NAME, MODULE_NAMESPACE, MODULE_NAMESPACE_ID, ERC20_REGISTRY_TABLE_ID } from "./constants.sol";
import { _allowancesTableId, _balancesTableId, _metadataTableId, _erc20SystemId } from "./utils.sol";
import { ERC20System } from "./ERC20System.sol";

import { ERC20Registry } from "./tables/ERC20Registry.sol";
import { Balances } from "./tables/Balances.sol";
import { Allowances } from "./tables/Allowances.sol";
import { Metadata, MetadataData } from "./tables/Metadata.sol";

contract ERC20Module is Module {
error ERC20Module_InvalidNamespace(bytes14 namespace);

function getName() public pure override returns (bytes16) {
return MODULE_NAME;
}

/**
* Register systems and tables for a new ERC20 token in a given namespace
*/
function _registerERC20(bytes14 namespace) internal {
// Register the tables
Allowances.register(_allowancesTableId(namespace));
Balances.register(_balancesTableId(namespace));
Metadata.register(_metadataTableId(namespace));

// Register a new ERC20System
IBaseWorld(_world()).registerSystem(_erc20SystemId(namespace), new ERC20System(), true);
}

function _installDependencies() internal {
// If the PuppetModule is not installed yet, install it
if (InstalledModules.get(PUPPET_MODULE_NAME, keccak256(new bytes(0))) == address(0)) {
IBaseWorld(_world()).installModule(new PuppetModule(), new bytes(0));
}
}

function install(bytes memory args) public {
// Require the module to not be installed with these args yet
if (InstalledModules.get(MODULE_NAME, keccak256(args)) != address(0)) {
revert Module_AlreadyInstalled();
}

// Extract args
(bytes14 namespace, MetadataData memory metadata) = abi.decode(args, (bytes14, MetadataData));

// Require the namespace to not be the module's namespace
if (namespace == MODULE_NAMESPACE) {
revert ERC20Module_InvalidNamespace(namespace);
}

// Install dependencies
_installDependencies();

// Register the ERC20 tables and system
_registerERC20(namespace);

// Initialize the Metadata
Metadata.set(_metadataTableId(namespace), metadata);

// Deploy and register the ERC20 puppet.
IBaseWorld world = IBaseWorld(_world());
ResourceId erc20SystemId = _erc20SystemId(namespace);
Puppet puppet = new Puppet(world, erc20SystemId);
registerPuppet(world, erc20SystemId, address(puppet));

// Transfer ownership of the namespace to the caller
ResourceId namespaceId = WorldResourceIdLib.encodeNamespace(namespace);
world.transferOwnership(namespaceId, _msgSender());

// Register the ERC20 in the ERC20Registry
if (!ResourceIds.getExists(ERC20_REGISTRY_TABLE_ID)) {
ERC20Registry.register(ERC20_REGISTRY_TABLE_ID);
}
ERC20Registry.set(ERC20_REGISTRY_TABLE_ID, namespaceId, address(puppet));
}

function installRoot(bytes memory) public pure {
revert Module_RootInstallNotSupported();
}
}
Loading

0 comments on commit 17c09e7

Please sign in to comment.