diff --git a/contracts/terminus/ERC1155WithTerminusStorage.sol b/contracts/terminus/ERC1155WithTerminusStorage.sol index 143f473..e621541 100644 --- a/contracts/terminus/ERC1155WithTerminusStorage.sol +++ b/contracts/terminus/ERC1155WithTerminusStorage.sol @@ -292,6 +292,11 @@ contract ERC1155WithTerminusStorage is bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); + LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage(); + require( + ts.poolSupply[id] + amount <= ts.poolCapacity[id], + "ERC1155WithTerminusStorage: _mint -- Minted tokens would exceed pool capacity" + ); address operator = _msgSender(); @@ -304,7 +309,6 @@ contract ERC1155WithTerminusStorage is data ); - LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage(); ts.poolBalances[id][to] += amount; emit TransferSingle(operator, address(0), to, id, amount); @@ -339,12 +343,19 @@ contract ERC1155WithTerminusStorage is "ERC1155: ids and amounts length mismatch" ); + LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage(); + + for (uint256 i = 0; i < ids.length; i++) { + require( + ts.poolSupply[ids[i]] + amounts[i] <= ts.poolCapacity[ids[i]], + "ERC1155WithTerminusStorage: _mintBatch -- Minted tokens would exceed pool capacity" + ); + } + address operator = _msgSender(); _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); - LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage(); - for (uint256 i = 0; i < ids.length; i++) { ts.poolBalances[ids[i]][to] += amounts[i]; } diff --git a/contracts/terminus/LibTerminus.sol b/contracts/terminus/LibTerminus.sol index 7862522..529b86d 100644 --- a/contracts/terminus/LibTerminus.sol +++ b/contracts/terminus/LibTerminus.sol @@ -27,8 +27,8 @@ library LibTerminus { uint256 poolBasePrice; // Terminus pools mapping(uint256 => address) poolController; - mapping(uint256 => bool) poolActive; mapping(uint256 => string) poolURI; + mapping(uint256 => uint256) poolCapacity; mapping(uint256 => uint256) poolSupply; mapping(uint256 => mapping(address => uint256)) poolBalances; mapping(address => mapping(address => bool)) globalOperatorApprovals; @@ -81,30 +81,15 @@ library LibTerminus { emit PoolControlTransferred(poolID, previousController, newController); } - function createSimplePool() internal returns (uint256) { + function createSimplePool(uint256 _capacity) internal returns (uint256) { TerminusStorage storage ts = terminusStorage(); uint256 poolID = ts.currentPoolID + 1; setPoolController(poolID, msg.sender); + ts.poolCapacity[poolID] = _capacity; ts.currentPoolID++; return poolID; } - function enforceIsActive() internal view { - TerminusStorage storage ts = terminusStorage(); - require( - ts.isTerminusActive, - "LibTerminus: Terminus contract must be active" - ); - } - - function enforcePoolIsActive(uint256 poolID) internal view { - TerminusStorage storage ts = terminusStorage(); - require( - ts.poolActive[poolID], - "LibTerminus: Terminus pool must be active" - ); - } - function enforcePoolIsController(uint256 poolID, address maybeController) internal view diff --git a/contracts/terminus/TerminusFacet.sol b/contracts/terminus/TerminusFacet.sol index 6cde104..7a86d2d 100644 --- a/contracts/terminus/TerminusFacet.sol +++ b/contracts/terminus/TerminusFacet.sol @@ -5,6 +5,15 @@ * GitHub: https://github.com/bugout-dev/dao * * This is an implementation of the Terminus decentralized authorization contract. + * + * Terminus users can create authorization pools. Each authorization pool has the following properties: + * 1. Controller: The address that controls the pool. Initially set to be the address of the pool creator. + * 2. Pool URI: Metadata URI for the authorization pool. + * 3. Pool capacity: The total number of tokens that can be minted in that authorization pool. + * 4. Pool supply: The number of tokens that have actually been minted in that authorization pool. + * 5. Transferable: A boolean value which denotes whether or not tokens from that pool can be transfered + * between addresses. + * 6. Burnable: A boolean value which denotes whether or not tokens from that pool can be burned. */ pragma solidity ^0.8.0; @@ -72,7 +81,23 @@ contract TerminusFacet is ERC1155WithTerminusStorage { return LibTerminus.terminusStorage().currentPoolID; } - function createSimplePool() external returns (uint256) { + function terminusPoolController(uint256 poolID) + external + view + returns (address) + { + return LibTerminus.terminusStorage().poolController[poolID]; + } + + function terminusPoolCapacity(uint256 poolID) + external + view + returns (uint256) + { + return LibTerminus.terminusStorage().poolCapacity[poolID]; + } + + function createSimplePool(uint256 _capacity) external returns (uint256) { LibTerminus.TerminusStorage storage ts = LibTerminus.terminusStorage(); uint256 requiredPayment = ts.poolBasePrice; IERC20 paymentTokenContract = _paymentTokenContract(); @@ -86,15 +111,7 @@ contract TerminusFacet is ERC1155WithTerminusStorage { address(this), requiredPayment ); - return LibTerminus.createSimplePool(); - } - - function terminusPoolController(uint256 poolID) - external - view - returns (address) - { - return LibTerminus.terminusStorage().poolController[poolID]; + return LibTerminus.createSimplePool(_capacity); } function mint( diff --git a/dao/TerminusFacet.py b/dao/TerminusFacet.py index 9efb786..41f3fad 100644 --- a/dao/TerminusFacet.py +++ b/dao/TerminusFacet.py @@ -98,9 +98,9 @@ class TerminusFacet: self.assert_contract_is_instantiated() return self.contract.balanceOfBatch.call(accounts, ids) - def create_simple_pool(self, transaction_config) -> Any: + def create_simple_pool(self, _capacity: int, transaction_config) -> Any: self.assert_contract_is_instantiated() - return self.contract.createSimplePool(transaction_config) + return self.contract.createSimplePool(_capacity, transaction_config) def is_approved_for_all( self, account: ChecksumAddress, operator: ChecksumAddress @@ -194,6 +194,10 @@ class TerminusFacet: self.assert_contract_is_instantiated() return self.contract.terminusController.call() + def terminus_pool_capacity(self, pool_id: int) -> Any: + self.assert_contract_is_instantiated() + return self.contract.terminusPoolCapacity.call(pool_id) + def terminus_pool_controller(self, pool_id: int) -> Any: self.assert_contract_is_instantiated() return self.contract.terminusPoolController.call(pool_id) @@ -282,7 +286,9 @@ def handle_create_simple_pool(args: argparse.Namespace) -> None: network.connect(args.network) contract = TerminusFacet(args.address) transaction_config = get_transaction_config(args) - result = contract.create_simple_pool(transaction_config=transaction_config) + result = contract.create_simple_pool( + _capacity=args.capacity_arg, transaction_config=transaction_config + ) print(result) @@ -423,6 +429,13 @@ def handle_terminus_controller(args: argparse.Namespace) -> None: print(result) +def handle_terminus_pool_capacity(args: argparse.Namespace) -> None: + network.connect(args.network) + contract = TerminusFacet(args.address) + result = contract.terminus_pool_capacity(pool_id=args.pool_id) + print(result) + + def handle_terminus_pool_controller(args: argparse.Namespace) -> None: network.connect(args.network) contract = TerminusFacet(args.address) @@ -485,6 +498,9 @@ def generate_cli() -> argparse.ArgumentParser: create_simple_pool_parser = subcommands.add_parser("create-simple-pool") add_default_arguments(create_simple_pool_parser, True) + create_simple_pool_parser.add_argument( + "--capacity-arg", required=True, help="Type: uint256", type=int + ) create_simple_pool_parser.set_defaults(func=handle_create_simple_pool) is_approved_for_all_parser = subcommands.add_parser("is-approved-for-all") @@ -610,6 +626,13 @@ def generate_cli() -> argparse.ArgumentParser: add_default_arguments(terminus_controller_parser, False) terminus_controller_parser.set_defaults(func=handle_terminus_controller) + terminus_pool_capacity_parser = subcommands.add_parser("terminus-pool-capacity") + add_default_arguments(terminus_pool_capacity_parser, False) + terminus_pool_capacity_parser.add_argument( + "--pool-id", required=True, help="Type: uint256", type=int + ) + terminus_pool_capacity_parser.set_defaults(func=handle_terminus_pool_capacity) + terminus_pool_controller_parser = subcommands.add_parser("terminus-pool-controller") add_default_arguments(terminus_pool_controller_parser, False) terminus_pool_controller_parser.add_argument( diff --git a/dao/test_terminus.py b/dao/test_terminus.py index 3f8d927..f26a79b 100644 --- a/dao/test_terminus.py +++ b/dao/test_terminus.py @@ -62,7 +62,7 @@ class TestPoolCreation(TerminusTestCase): initial_total_pools = diamond_terminus.total_pools() - diamond_terminus.create_simple_pool({"from": accounts[1]}) + diamond_terminus.create_simple_pool(10, {"from": accounts[1]}) final_total_pools = diamond_terminus.total_pools() self.assertEqual(final_total_pools, initial_total_pools + 1) @@ -114,6 +114,9 @@ class TestPoolCreation(TerminusTestCase): pool_controller = diamond_terminus.terminus_pool_controller(final_total_pools) self.assertEqual(pool_controller, accounts[1].address) + pool_capacity = diamond_terminus.terminus_pool_capacity(final_total_pools) + self.assertEqual(pool_capacity, 10) + if __name__ == "__main__": unittest.main() diff --git a/test.sh b/test.sh index 0a75eeb..03e636c 100755 --- a/test.sh +++ b/test.sh @@ -4,6 +4,8 @@ # You can set up the local copy of `dao` for development using: # pip install -e .[dev] +set -e + usage() { echo "Usage: $0" [TEST_SPEC ...] echo