diff --git a/script/DeployBrokerTokenFactory.s.sol b/script/DeployBrokerTokenFactory.s.sol new file mode 100644 index 0000000..3cf91b7 --- /dev/null +++ b/script/DeployBrokerTokenFactory.s.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.8.24; + +import {Script, console} from "forge-std/Script.sol"; +import {BrokerTokenFactory} from "../src/BrokerTokenFactory.sol"; + +contract DeployBrokerTokenFactory is Script { + function run() public { + string memory ownerStr = vm.envString("ERC20_FACTORY_OWNER"); + address owner = vm.parseAddress(ownerStr); + + vm.startBroadcast(); // start broadcasting transactions to the blockchain + BrokerTokenFactory factory = new BrokerTokenFactory(owner); + vm.stopBroadcast(); + + console.log("BrokerToken Factory address: %s", address(factory)); + } +} diff --git a/src/BrokerToken.sol b/src/BrokerToken.sol index 4945bd7..ff3f657 100644 --- a/src/BrokerToken.sol +++ b/src/BrokerToken.sol @@ -15,6 +15,7 @@ contract BrokerToken is ERC20Permit { * Mints the supply to the deployer. * @param name Name of the Token. * @param symbol Symbol of the Token. + * @param decimals_ Number of decimals of the Token. * @param supply Maximum supply of the Token. */ constructor(string memory name, string memory symbol, uint8 decimals_, uint256 supply) diff --git a/src/BrokerTokenFactory.sol b/src/BrokerTokenFactory.sol new file mode 100644 index 0000000..2676eae --- /dev/null +++ b/src/BrokerTokenFactory.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/access/Ownable.sol"; +import "./BrokerToken.sol"; + +/** + * @title BrokerTokenFactory + * @notice Factory contract to create new instances of BrokerToken. + * Only the owner can create new tokens. + */ +contract BrokerTokenFactory is Ownable { + // Array to store addresses of created tokens + address[] public brokerTokens; + + /** + * @notice Event emitted when a new BrokerToken is created. + * @param tokenAddress Address of the newly created BrokerToken. + */ + event BrokerTokenCreated(address indexed tokenAddress); + + constructor(address owner) Ownable(owner) {} + + /** + * @notice Creates a new BrokerToken and mints the supply to the factory owner. + * @param name Name of the Token. + * @param symbol Symbol of the Token. + * @param supply Maximum supply of the Token. + */ + function createBrokerToken(string memory name, string memory symbol, uint8 decimals, uint256 supply) + external + onlyOwner + { + BrokerToken token = new BrokerToken(name, symbol, decimals, supply); + brokerTokens.push(address(token)); + emit BrokerTokenCreated(address(token)); + } + + /** + * @notice Returns the number of created BrokerTokens. + * @return uint256 Number of created BrokerTokens. + */ + function getBrokerTokensCount() external view returns (uint256) { + return brokerTokens.length; + } + + /** + * @notice Returns the address of a created BrokerToken at a given index. + * @param index Index of the token in the brokerTokens array. + * @return address Address of the BrokerToken. + */ + function getBrokerToken(uint256 index) external view returns (address) { + require(index < brokerTokens.length, "Index out of bounds"); + return brokerTokens[index]; + } +} diff --git a/test/BrokerTokenFactory.t.sol b/test/BrokerTokenFactory.t.sol new file mode 100644 index 0000000..a5580db --- /dev/null +++ b/test/BrokerTokenFactory.t.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import "../src/BrokerTokenFactory.sol"; +import "../src/BrokerToken.sol"; + +contract BrokerTokenFactoryTest is Test { + BrokerTokenFactory factory; + address owner; + + function setUp() public { + owner = address(this); + vm.deal(owner, 100 ether); + factory = new BrokerTokenFactory(owner); + } + + function testCreateBrokerToken() public { + string memory name = "BrokerToken"; + string memory symbol = "BTK"; + uint8 decimals = 8; + uint256 supply = 1000 * 10 ** 8; + + // Ensure only the owner can create tokens + vm.prank(owner); + factory.createBrokerToken(name, symbol, decimals, supply); + + // Check if the token was created + assertEq(factory.getBrokerTokensCount(), 1); + + address tokenAddress = factory.getBrokerToken(0); + BrokerToken token = BrokerToken(tokenAddress); + + // Validate the token's properties + assertEq(token.name(), name); + assertEq(token.symbol(), symbol); + assertEq(token.totalSupply(), supply); + assertEq(token.decimals(), 8); + } + + function testFailNonOwnerCannotCreateBrokerToken() public { + string memory name = "BrokerToken"; + string memory symbol = "BTK"; + uint8 decimals = 8; + uint256 supply = 1000 * 10 ** 8; + + // Try to create a token with a different address + vm.prank(address(0x1234)); + vm.expectRevert("Ownable: caller is not the owner"); + factory.createBrokerToken(name, symbol, decimals, supply); + } + + function testGetBrokerToken() public { + string memory name = "BrokerToken"; + string memory symbol = "BTK"; + uint8 decimals = 8; + uint256 supply = 1000 * 10 ** 8; + + vm.prank(owner); + factory.createBrokerToken(name, symbol, decimals, supply); + + address tokenAddress = factory.getBrokerToken(0); + BrokerToken token = BrokerToken(tokenAddress); + + // Validate the token address + assertEq(address(token), tokenAddress); + } + + function testBrokerTokensCount() public { + string memory name1 = "BrokerToken1"; + string memory symbol1 = "BTK1"; + uint8 decimals1 = 8; + uint256 supply1 = 1000 * 10 ** 8; + + string memory name2 = "BrokerToken2"; + string memory symbol2 = "BTK2"; + uint8 decimals2 = 8; + uint256 supply2 = 2000 * 10 ** 8; + + vm.prank(owner); + factory.createBrokerToken(name1, symbol1, decimals1, supply1); + vm.prank(owner); + factory.createBrokerToken(name2, symbol2, decimals2, supply2); + + // Validate the number of created tokens + assertEq(factory.getBrokerTokensCount(), 2); + } +}