From 0207992edcc62fb96b22186e59b693bd7fd262a4 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:31:47 +0530 Subject: [PATCH 01/18] mocks: add erc20 base token --- src/mocks/tokens/base/ERC20Base.sol | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/mocks/tokens/base/ERC20Base.sol diff --git a/src/mocks/tokens/base/ERC20Base.sol b/src/mocks/tokens/base/ERC20Base.sol new file mode 100644 index 0000000..24641b2 --- /dev/null +++ b/src/mocks/tokens/base/ERC20Base.sol @@ -0,0 +1,48 @@ +pragma solidity ^0.8.0; + +import "forge-std/interfaces/IERC20.sol"; + +contract ERC20Base is IERC20 { + string public name = "ERC20Token"; + string public symbol = "ERC"; + uint8 public decimals = 18; + + uint256 public totalSupply; + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + function approve(address spender, uint256 amount) public returns (bool) { + _approve(msg.sender, spender, amount); + return true; + } + + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + allowance[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + function transfer(address to, uint256 amount) public virtual returns (bool) { + _transfer(msg.sender, to, amount); + return true; + } + + function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { + _transfer(from, to, amount); + _approve(from, msg.sender, allowance[from][msg.sender] - amount); + return true; + } + + function _transfer(address sender, address recipient, uint256 amount) internal { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + require(balanceOf[sender] >= amount, "ERC20: insufficient balance"); + + balanceOf[sender] -= amount; + balanceOf[recipient] += amount; + + emit Transfer(sender, recipient, amount); + } +} From 05e1fc78c99ea05ee2831ff151591d6878ad60e4 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:32:44 +0530 Subject: [PATCH 02/18] mocks: add Proxy.sol under miscs --- src/mocks/miscs/Proxy.sol | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/mocks/miscs/Proxy.sol diff --git a/src/mocks/miscs/Proxy.sol b/src/mocks/miscs/Proxy.sol new file mode 100644 index 0000000..dff1666 --- /dev/null +++ b/src/mocks/miscs/Proxy.sol @@ -0,0 +1,43 @@ +pragma solidity ^0.8.0; + +contract Proxy { + address public implementation; + address public owner; + + event Upgraded(address implementation); + + modifier onlyImplementation() { + require(msg.sender == implementation, "Proxy: only implementation allowed"); + _; + } + + modifier onlyOwner() { + require(msg.sender == owner, "Proxy: only owner allowed"); + _; + } + + constructor(address initialImplementation) { + implementation = initialImplementation; + owner = msg.sender; + } + + function upgradeTo(address newImplementation) external onlyOwner { + require(newImplementation != address(0), "Proxy: Cannot upgrade to the zero address"); + require(newImplementation != implementation, "Proxy: Cannot upgrade to the same implementation"); + + implementation = newImplementation; + emit Upgraded(newImplementation); + } + + fallback() external payable { + address _impl = implementation; + assembly { + calldatacopy(0, 0, calldatasize()) + let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + switch result + case 0 { revert(0, returndatasize()) } + default { return(0, returndatasize()) } + } + } +} From c72001c5513ce13eb8dc7654ed7c5b86dcd1dfe4 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:38:43 +0530 Subject: [PATCH 03/18] mocks: ERC20 return bool token --- src/mocks/tokens/ERC20-bool.sol | 47 ++++++++++++++++++++++++++++++ test/mocks/tokens/ERC20-bool.t.sol | 38 ++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/mocks/tokens/ERC20-bool.sol create mode 100644 test/mocks/tokens/ERC20-bool.t.sol diff --git a/src/mocks/tokens/ERC20-bool.sol b/src/mocks/tokens/ERC20-bool.sol new file mode 100644 index 0000000..14ec49a --- /dev/null +++ b/src/mocks/tokens/ERC20-bool.sol @@ -0,0 +1,47 @@ +pragma solidity ^0.8.0; + +import "forge-std/interfaces/IERC20.sol"; +import {ERC20Base} from "./base/ERC20Base.sol"; + +contract ERC20Bool is ERC20Base { + constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _initialSupply) { + name = _name; + symbol = _symbol; + decimals = _decimals; + totalSupply = _initialSupply; + balanceOf[msg.sender] = _initialSupply; + + emit Transfer(address(0), msg.sender, _initialSupply); + } + + function transfer(address to, uint256 amount) public override returns (bool) { + unchecked { + if (balanceOf[msg.sender] >= amount && balanceOf[to] + amount >= balanceOf[to]) { + balanceOf[to] += amount; + balanceOf[msg.sender] -= amount; + emit Transfer(msg.sender, to, amount); + return true; + } else { + return false; + } + } + } + + function transferFrom(address from, address to, uint256 amount) public override returns (bool) { + unchecked { + if ( + balanceOf[from] >= amount && allowance[from][msg.sender] >= amount + && balanceOf[to] + amount >= balanceOf[to] + ) { + balanceOf[to] += amount; + balanceOf[from] -= amount; + emit Transfer(from, to, amount); + allowance[from][msg.sender] -= amount; + emit Approval(from, msg.sender, allowance[from][msg.sender]); + return true; + } else { + return false; + } + } + } +} diff --git a/test/mocks/tokens/ERC20-bool.t.sol b/test/mocks/tokens/ERC20-bool.t.sol new file mode 100644 index 0000000..384e0f2 --- /dev/null +++ b/test/mocks/tokens/ERC20-bool.t.sol @@ -0,0 +1,38 @@ +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "forge-std/interfaces/IERC20.sol"; +import {ERC20Bool} from "../../../src/mocks/tokens/ERC20-bool.sol"; + +contract ERC20BoolTest is Test { + IERC20 public boolToken; + uint256 immutable initialSupply = 1000000000; + + address alice = vm.addr(1); + address bob = vm.addr(2); + + function setUp() public { + boolToken = new ERC20Bool("BoolToken","Btoken",18,initialSupply); + assertEq(boolToken.balanceOf(address(this)), initialSupply); + } + + function testBoolTokenTransfer() public { + assertEq(boolToken.balanceOf(alice), 0); + + bool retVal = boolToken.transfer(alice, 100); + assertEq(retVal, true); + + assertEq(boolToken.balanceOf(alice), 100); + assertEq(boolToken.balanceOf(address(this)), initialSupply - 100); + } + + function testBoolTokenTransferFail() public { + assertEq(boolToken.balanceOf(alice), 0); + + vm.startPrank(alice); + bool retVal = boolToken.transfer(bob, 100); + assertEq(retVal, false); + + assertEq(boolToken.balanceOf(bob), 0); + } +} From 680541e353a1d84d6240d2bc206d105573e19ff8 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:50:48 +0530 Subject: [PATCH 04/18] mocks: ERC20 fee on transfer --- src/mocks/tokens/ERC20-feeTransfer.sol | 49 +++++++++++++++++++++++ test/mocks/tokens/ERC20-feeTransfer.t.sol | 32 +++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/mocks/tokens/ERC20-feeTransfer.sol create mode 100644 test/mocks/tokens/ERC20-feeTransfer.t.sol diff --git a/src/mocks/tokens/ERC20-feeTransfer.sol b/src/mocks/tokens/ERC20-feeTransfer.sol new file mode 100644 index 0000000..48048b0 --- /dev/null +++ b/src/mocks/tokens/ERC20-feeTransfer.sol @@ -0,0 +1,49 @@ +pragma solidity ^0.8.0; + +import "forge-std/interfaces/IERC20.sol"; +import {ERC20Base} from "./base/ERC20Base.sol"; + +contract ERC20FeeTransfer is ERC20Base { + uint256 immutable fee; + + constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _initialSupply, uint256 _fee) { + name = _name; + symbol = _symbol; + decimals = _decimals; + totalSupply = _initialSupply; + fee = _fee; + balanceOf[msg.sender] = _initialSupply; + + emit Transfer(address(0), msg.sender, _initialSupply); + } + + function transfer(address to, uint256 amount) public override returns (bool) { + require(balanceOf[msg.sender] >= amount, "ERC20Fee: Insufficient-balance"); + + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount - fee; + balanceOf[address(0)] += fee; + + emit Transfer(msg.sender, to, amount - fee); + emit Transfer(msg.sender, address(0), fee); + + return true; + } + + function transferFrom(address from, address to, uint256 amount) public override returns (bool) { + require(balanceOf[from] >= amount, "ERC20Fee: Insufficient-balance"); + if (from != msg.sender && allowance[from][msg.sender] != type(uint256).max) { + require(allowance[from][msg.sender] >= amount, "ERC20Fee: Insufficient-balance"); + allowance[from][msg.sender] -= amount; + } + + balanceOf[from] -= amount; + balanceOf[to] += amount - fee; + balanceOf[address(0)] += fee; + + emit Transfer(from, to, amount - fee); + emit Transfer(from, address(0), fee); + + return true; + } +} diff --git a/test/mocks/tokens/ERC20-feeTransfer.t.sol b/test/mocks/tokens/ERC20-feeTransfer.t.sol new file mode 100644 index 0000000..eb9a9bb --- /dev/null +++ b/test/mocks/tokens/ERC20-feeTransfer.t.sol @@ -0,0 +1,32 @@ +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "forge-std/interfaces/IERC20.sol"; +import {ERC20FeeTransfer} from "../../../src/mocks/tokens/ERC20-feeTransfer.sol"; + +contract ERC20FeeTest is Test { + IERC20 public feeToken; + uint256 immutable initialSupply = 1000000000; + uint8 immutable fee = 5; + + address alice = vm.addr(1); + address bob = vm.addr(2); + + function setUp() public { + feeToken = new ERC20FeeTransfer("FeeToken","Ftoken",18,initialSupply,fee); + assertEq(feeToken.balanceOf(address(this)), initialSupply); + } + + function testFeeOnTransfer() public { + uint256 amt = 100; + + uint256 balBefore = feeToken.balanceOf(address(this)); + uint256 userBalBefore = feeToken.balanceOf(address(alice)); + feeToken.transfer(address(alice), amt); + uint256 balAfter = feeToken.balanceOf(address(this)); + uint256 userBalAfter = feeToken.balanceOf(address(alice)); + + assertEq(balAfter, balBefore - amt); + assertEq(userBalAfter, userBalBefore + amt - fee); + } +} From 2c3a0263935650c10096c1c1a6ec7ef9074b2f21 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:01:24 +0530 Subject: [PATCH 05/18] mocks: ERC20 rebase token --- src/mocks/tokens/ERC20-rebase.sol | 46 ++++++++++++++++++++++++++++ test/mocks/tokens/ERC20-rebase.t.sol | 40 ++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/mocks/tokens/ERC20-rebase.sol create mode 100644 test/mocks/tokens/ERC20-rebase.t.sol diff --git a/src/mocks/tokens/ERC20-rebase.sol b/src/mocks/tokens/ERC20-rebase.sol new file mode 100644 index 0000000..7323b95 --- /dev/null +++ b/src/mocks/tokens/ERC20-rebase.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.8.0; + +import "forge-std/interfaces/IERC20.sol"; +import {ERC20Base} from "./base/ERC20Base.sol"; + +import "forge-std/Test.sol"; + +contract ERC20rebase is ERC20Base, Test { + uint256 public lastRebaseTimestamp; + uint256 public rebaseInterval = 1 minutes; + uint256 public rebaseAmt = 5; + + constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _initialSupply, uint256 _rebaseInterval, uint256 _rebaseAmt) { + name = _name; + symbol = _symbol; + decimals = _decimals; + totalSupply = _initialSupply; + balanceOf[msg.sender] = _initialSupply; + + lastRebaseTimestamp = block.timestamp; + rebaseInterval = _rebaseInterval; + rebaseAmt = _rebaseAmt; + + + emit Transfer(address(0), msg.sender, _initialSupply); + } + + modifier rebase() { + uint256 timeSinceLastRebase = block.timestamp - lastRebaseTimestamp; + if (timeSinceLastRebase >= rebaseInterval) { + uint256 rebaseMultiplier = timeSinceLastRebase / rebaseInterval; + totalSupply += rebaseAmt * rebaseMultiplier; + balanceOf[msg.sender] += rebaseAmt * rebaseMultiplier; + lastRebaseTimestamp += rebaseInterval * rebaseMultiplier; + } + _; + } + + function transfer(address to, uint256 amount) public rebase override returns (bool) { + super.transfer(to,amount); + } + + function transferFrom(address from, address to, uint256 amount) public rebase override returns (bool) { + super.transferFrom(from,to,amount); + } +} \ No newline at end of file diff --git a/test/mocks/tokens/ERC20-rebase.t.sol b/test/mocks/tokens/ERC20-rebase.t.sol new file mode 100644 index 0000000..db841ad --- /dev/null +++ b/test/mocks/tokens/ERC20-rebase.t.sol @@ -0,0 +1,40 @@ +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "forge-std/interfaces/IERC20.sol"; +import {ERC20rebase} from "../../../src/mocks/tokens/ERC20-rebase.sol"; + +contract ERC20RebasingTest is Test { + IERC20 public rebasingToken; + uint256 immutable initialSupply = 1000000000; + uint256 rebasingInterval = 1 minutes; + uint256 rebasingAmt = 5; + + address alice = vm.addr(1); + address bob = vm.addr(2); + + function setUp() public { + rebasingToken = new ERC20rebase("BoolToken","Btoken",18,initialSupply,rebasingInterval,rebasingAmt); + assertEq(rebasingToken.balanceOf(address(this)), initialSupply); + } + + function testRebasingTransfer() public { + uint256 transferAmt = 100; + assertEq(rebasingToken.totalSupply(),initialSupply); + + uint256 balBefore = rebasingToken.balanceOf(address(this)); + uint256 userBalBefore = rebasingToken.balanceOf(address(alice)); + + vm.warp(block.timestamp + rebasingInterval); + + rebasingToken.transfer(address(alice),transferAmt); + uint256 balAfter = rebasingToken.balanceOf(address(this)); + uint256 userBalAfter = rebasingToken.balanceOf(address(alice)); + + assertEq(rebasingToken.totalSupply(),initialSupply + rebasingAmt); + + assertEq(balAfter, balBefore - transferAmt + rebasingAmt); + assertEq(userBalAfter, userBalBefore + transferAmt); + } + +} \ No newline at end of file From 694e4d0a8f62edad0beb189fa1bc7999d9f78e01 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:02:39 +0530 Subject: [PATCH 06/18] mocks: add README --- src/mocks/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/mocks/README.md diff --git a/src/mocks/README.md b/src/mocks/README.md new file mode 100644 index 0000000..afb0712 --- /dev/null +++ b/src/mocks/README.md @@ -0,0 +1,15 @@ +## Vulnerability Type + +WIP: + +This template category provides a diverse collection of boilerplate contracts which enables the whitehat to seamlessly integrate and conduct swift tests on the target contract. The range encompasses mock tokens adhering to unconventional ERC-20 standards, as well as potentially harmful contracts. + +
+ + + +## Usage + +The following attack contract demonstrate a simple ERC20 token which returns a `bool` on the transfer. + +* [ERC20Test](../../test/examples/ERC20Example.t.sol) \ No newline at end of file From b1665264a4aab2f0f87f72219750650b2ed9d024 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:10:24 +0530 Subject: [PATCH 07/18] mocks: malicious selfdestruct --- src/mocks/malicious/self-destruct.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/mocks/malicious/self-destruct.sol diff --git a/src/mocks/malicious/self-destruct.sol b/src/mocks/malicious/self-destruct.sol new file mode 100644 index 0000000..43c55f7 --- /dev/null +++ b/src/mocks/malicious/self-destruct.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.8.0; + +// Deprecation of selfdestruct +// https://soliditylang.org/blog/2023/02/01/solidity-0.8.18-release-announcement/ + +contract selfDestruct { + constructor() {} + + function attack(address _contractAddr) public { + selfdestruct(payable(_contractAddr)); + } + + function destruct() external { + selfdestruct(payable(msg.sender)); + } +} From c64eef1228eba1deb8dc8b571c6af42d4a03bcdb Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:10:39 +0530 Subject: [PATCH 08/18] mocks: forge fmt on the tests --- src/mocks/tokens/ERC20-rebase.sol | 20 +++++++++++++------- test/mocks/tokens/ERC20-rebase.t.sol | 17 ++++++++--------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/mocks/tokens/ERC20-rebase.sol b/src/mocks/tokens/ERC20-rebase.sol index 7323b95..e7bb2d2 100644 --- a/src/mocks/tokens/ERC20-rebase.sol +++ b/src/mocks/tokens/ERC20-rebase.sol @@ -10,7 +10,14 @@ contract ERC20rebase is ERC20Base, Test { uint256 public rebaseInterval = 1 minutes; uint256 public rebaseAmt = 5; - constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _initialSupply, uint256 _rebaseInterval, uint256 _rebaseAmt) { + constructor( + string memory _name, + string memory _symbol, + uint8 _decimals, + uint256 _initialSupply, + uint256 _rebaseInterval, + uint256 _rebaseAmt + ) { name = _name; symbol = _symbol; decimals = _decimals; @@ -21,7 +28,6 @@ contract ERC20rebase is ERC20Base, Test { rebaseInterval = _rebaseInterval; rebaseAmt = _rebaseAmt; - emit Transfer(address(0), msg.sender, _initialSupply); } @@ -36,11 +42,11 @@ contract ERC20rebase is ERC20Base, Test { _; } - function transfer(address to, uint256 amount) public rebase override returns (bool) { - super.transfer(to,amount); + function transfer(address to, uint256 amount) public override rebase returns (bool) { + super.transfer(to, amount); } - function transferFrom(address from, address to, uint256 amount) public rebase override returns (bool) { - super.transferFrom(from,to,amount); + function transferFrom(address from, address to, uint256 amount) public override rebase returns (bool) { + super.transferFrom(from, to, amount); } -} \ No newline at end of file +} diff --git a/test/mocks/tokens/ERC20-rebase.t.sol b/test/mocks/tokens/ERC20-rebase.t.sol index db841ad..410777d 100644 --- a/test/mocks/tokens/ERC20-rebase.t.sol +++ b/test/mocks/tokens/ERC20-rebase.t.sol @@ -8,7 +8,7 @@ contract ERC20RebasingTest is Test { IERC20 public rebasingToken; uint256 immutable initialSupply = 1000000000; uint256 rebasingInterval = 1 minutes; - uint256 rebasingAmt = 5; + uint256 rebasingAmt = 5; address alice = vm.addr(1); address bob = vm.addr(2); @@ -20,21 +20,20 @@ contract ERC20RebasingTest is Test { function testRebasingTransfer() public { uint256 transferAmt = 100; - assertEq(rebasingToken.totalSupply(),initialSupply); + assertEq(rebasingToken.totalSupply(), initialSupply); uint256 balBefore = rebasingToken.balanceOf(address(this)); uint256 userBalBefore = rebasingToken.balanceOf(address(alice)); - + vm.warp(block.timestamp + rebasingInterval); - - rebasingToken.transfer(address(alice),transferAmt); + + rebasingToken.transfer(address(alice), transferAmt); uint256 balAfter = rebasingToken.balanceOf(address(this)); uint256 userBalAfter = rebasingToken.balanceOf(address(alice)); - assertEq(rebasingToken.totalSupply(),initialSupply + rebasingAmt); - + assertEq(rebasingToken.totalSupply(), initialSupply + rebasingAmt); + assertEq(balAfter, balBefore - transferAmt + rebasingAmt); assertEq(userBalAfter, userBalBefore + transferAmt); } - -} \ No newline at end of file +} From d76f4f6444adfd5ec894dca3bc252e5c6b4666b2 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:35:11 +0530 Subject: [PATCH 09/18] mocks: add ERC777 token interfaces --- .../tokens/interfaces/IERC1820Registry.sol | 95 ++++++++++ src/mocks/tokens/interfaces/IERC777.sol | 172 ++++++++++++++++++ .../tokens/interfaces/IERC777Recipient.sol | 22 +++ src/mocks/tokens/interfaces/IERC777Sender.sol | 22 +++ 4 files changed, 311 insertions(+) create mode 100644 src/mocks/tokens/interfaces/IERC1820Registry.sol create mode 100644 src/mocks/tokens/interfaces/IERC777.sol create mode 100644 src/mocks/tokens/interfaces/IERC777Recipient.sol create mode 100644 src/mocks/tokens/interfaces/IERC777Sender.sol diff --git a/src/mocks/tokens/interfaces/IERC1820Registry.sol b/src/mocks/tokens/interfaces/IERC1820Registry.sol new file mode 100644 index 0000000..ccb236a --- /dev/null +++ b/src/mocks/tokens/interfaces/IERC1820Registry.sol @@ -0,0 +1,95 @@ +pragma solidity ^0.8.0; + +interface IERC1820Registry { + /** + * @dev Sets `newManager` as the manager for `account`. A manager of an + * account is able to set interface implementers for it. + * + * By default, each account is its own manager. Passing a value of `0x0` in + * `newManager` will reset the manager to this initial state. + * + * Emits a `ManagerChanged` event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + */ + function setManager(address account, address newManager) external; + + /** + * @dev Returns the manager for `account`. + * + * See `setManager`. + */ + function getManager(address account) external view returns (address); + + /** + * @dev Sets the `implementer` contract as `account`'s implementer for + * `interfaceHash`. + * + * `account` being the zero address is an alias for the caller's address. + * The zero address can also be used in `implementer` to remove an old one. + * + * See `interfaceHash` to learn how these are created. + * + * Emits an `InterfaceImplementerSet` event. + * + * Requirements: + * + * - the caller must be the current manager for `account`. + * - `interfaceHash` must not be an `IERC165` interface id (i.e. it must not + * end in 28 zeroes). + * - `implementer` must implement `IERC1820Implementer` and return true when + * queried for support, unless `implementer` is the caller. See + * `IERC1820Implementer.canImplementInterfaceForAddress`. + */ + function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external; + + /** + * @dev Returns the implementer of `interfaceHash` for `account`. If no such + * implementer is registered, returns the zero address. + * + * If `interfaceHash` is an `IERC165` interface id (i.e. it ends with 28 + * zeroes), `account` will be queried for support of it. + * + * `account` being the zero address is an alias for the caller's address. + */ + function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address); + + /** + * @dev Returns the interface hash for an `interfaceName`, as defined in the + * corresponding + * [section of the EIP](https://eips.ethereum.org/EIPS/eip-1820#interface-name). + */ + function interfaceHash(string calldata interfaceName) external pure returns (bytes32); + + /** + * @notice Updates the cache with whether the contract implements an ERC165 interface or not. + * @param account Address of the contract for which to update the cache. + * @param interfaceId ERC165 interface for which to update the cache. + */ + function updateERC165Cache(address account, bytes4 interfaceId) external; + + /** + * @notice Checks whether a contract implements an ERC165 interface or not. + * If the result is not cached a direct lookup on the contract address is performed. + * If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling + * 'updateERC165Cache' with the contract address. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account.address()` implements `interfaceId`, false otherwise. + */ + function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool); + + /** + * @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache. + * @param account Address of the contract to check. + * @param interfaceId ERC165 interface to check. + * @return True if `account.address()` implements `interfaceId`, false otherwise. + */ + function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool); + + event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer); + + event ManagerChanged(address indexed account, address indexed newManager); +} diff --git a/src/mocks/tokens/interfaces/IERC777.sol b/src/mocks/tokens/interfaces/IERC777.sol new file mode 100644 index 0000000..e0c0700 --- /dev/null +++ b/src/mocks/tokens/interfaces/IERC777.sol @@ -0,0 +1,172 @@ +pragma solidity ^0.8.0; + +interface IERC777 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the smallest part of the token that is not divisible. This + * means all token operations (creation, movement and destruction) must have + * amounts that are a multiple of this number. + * + * For most token contracts, this value will equal 1. + */ + function granularity() external view returns (uint256); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by an account (`owner`). + */ + function balanceOf(address owner) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * If send or receive hooks are registered for the caller and `recipient`, + * the corresponding functions will be called with `data` and empty + * `operatorData`. See `IERC777Sender` and `IERC777Recipient`. + * + * Emits a `Sent` event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the `tokensReceived` + * interface. + */ + function send(address recipient, uint256 amount, bytes calldata data) external; + + /** + * @dev Destroys `amount` tokens from the caller's account, reducing the + * total supply. + * + * If a send hook is registered for the caller, the corresponding function + * will be called with `data` and empty `operatorData`. See `IERC777Sender`. + * + * Emits a `Burned` event. + * + * Requirements + * + * - the caller must have at least `amount` tokens. + */ + function burn(uint256 amount, bytes calldata data) external; + + /** + * @dev Returns true if an account is an operator of `tokenHolder`. + * Operators can send and burn tokens on behalf of their owners. All + * accounts are their own operator. + * + * See `operatorSend` and `operatorBurn`. + */ + function isOperatorFor(address operator, address tokenHolder) external view returns (bool); + + /** + * @dev Make an account an operator of the caller. + * + * See `isOperatorFor`. + * + * Emits an `AuthorizedOperator` event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function authorizeOperator(address operator) external; + + /** + * @dev Make an account an operator of the caller. + * + * See `isOperatorFor` and `_defaultOperators`. + * + * Emits a `RevokedOperator` event. + * + * Requirements + * + * - `operator` cannot be calling address. + */ + function revokeOperator(address operator) external; + + /** + * @dev Returns the list of default operators. These accounts are operators + * for all token holders, even if `authorizeOperator` was never called on + * them. + * + * This list is immutable, but individual holders may revoke these via + * `revokeOperator`, in which case `isOperatorFor` will return false. + */ + function _defaultOperators() external view returns (address[] memory); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must + * be an operator of `sender`. + * + * If send or receive hooks are registered for `sender` and `recipient`, + * the corresponding functions will be called with `data` and + * `operatorData`. See `IERC777Sender` and `IERC777Recipient`. + * + * Emits a `Sent` event. + * + * Requirements + * + * - `sender` cannot be the zero address. + * - `sender` must have at least `amount` tokens. + * - the caller must be an operator for `sender`. + * - `recipient` cannot be the zero address. + * - if `recipient` is a contract, it must implement the `tokensReceived` + * interface. + */ + function operatorSend( + address sender, + address recipient, + uint256 amount, + bytes calldata data, + bytes calldata operatorData + ) external; + + /** + * @dev Destoys `amount` tokens from `account`, reducing the total supply. + * The caller must be an operator of `account`. + * + * If a send hook is registered for `account`, the corresponding function + * will be called with `data` and `operatorData`. See `IERC777Sender`. + * + * Emits a `Burned` event. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + * - the caller must be an operator for `account`. + */ + function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external; + + event Sent( + address indexed operator, + address indexed from, + address indexed to, + uint256 amount, + bytes data, + bytes operatorData + ); + + event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); + + event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); + + event AuthorizedOperator(address indexed operator, address indexed tokenHolder); + + event RevokedOperator(address indexed operator, address indexed tokenHolder); +} diff --git a/src/mocks/tokens/interfaces/IERC777Recipient.sol b/src/mocks/tokens/interfaces/IERC777Recipient.sol new file mode 100644 index 0000000..9b46a4b --- /dev/null +++ b/src/mocks/tokens/interfaces/IERC777Recipient.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.8.0; + +interface IERC777Recipient { + /** + * @dev Called by an `IERC777` token contract whenever tokens are being + * moved or created into a registered account (`to`). The type of operation + * is conveyed by `from` being the zero address or not. + * + * This call occurs _after_ the token contract's state is updated, so + * `IERC777.balanceOf`, etc., can be used to query the post-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensReceived( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} diff --git a/src/mocks/tokens/interfaces/IERC777Sender.sol b/src/mocks/tokens/interfaces/IERC777Sender.sol new file mode 100644 index 0000000..f1145a9 --- /dev/null +++ b/src/mocks/tokens/interfaces/IERC777Sender.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.8.0; + +interface IERC777Sender { + /** + * @dev Called by an `IERC777` token contract whenever a registered holder's + * (`from`) tokens are about to be moved or destroyed. The type of operation + * is conveyed by `to` being the zero address or not. + * + * This call occurs _before_ the token contract's state is updated, so + * `IERC777.balanceOf`, etc., can be used to query the pre-operation state. + * + * This function may revert to prevent the operation from being executed. + */ + function tokensToSend( + address operator, + address from, + address to, + uint256 amount, + bytes calldata userData, + bytes calldata operatorData + ) external; +} From c10e8da048af1543b87db33746cf6b584f79b29a Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:11:41 +0530 Subject: [PATCH 10/18] mocks: add ERC1820 and ERC777 dependencies --- src/mocks/tokens/ERC777-token.sol | 280 ++++++++++++++++++++++++ src/mocks/tokens/base/ERC1820Client.sol | 22 ++ src/mocks/tokens/interfaces/IERC777.sol | 2 +- 3 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 src/mocks/tokens/ERC777-token.sol create mode 100644 src/mocks/tokens/base/ERC1820Client.sol diff --git a/src/mocks/tokens/ERC777-token.sol b/src/mocks/tokens/ERC777-token.sol new file mode 100644 index 0000000..475e95f --- /dev/null +++ b/src/mocks/tokens/ERC777-token.sol @@ -0,0 +1,280 @@ +pragma solidity ^0.8.0; + +import {IERC777} from "./interfaces/IERC777.sol"; +import {IERC777Recipient} from "./interfaces/IERC777Recipient.sol"; +import {IERC777Sender} from "./interfaces/IERC777Sender.sol"; +import {ERC1820Client} from "./base/ERC1820Client.sol"; + +contract ERC777 is IERC777, ERC1820Client { + string internal mName; + string internal mSymbol; + uint256 internal mGranularity; + uint256 internal mTotalSupply; + + mapping(address => uint) internal mBalances; + address[] internal mDefaultOperators; + mapping(address => bool) internal mIsDefaultOperator; + mapping(address => mapping(address => bool)) + internal mRevokedDefaultOperator; + mapping(address => mapping(address => bool)) internal mAuthorizedOperators; + + constructor( + string memory _name, + string memory _symbol, + uint256 _granularity, + address[] memory _defaultOperators + ) { + mName = _name; + mSymbol = _symbol; + mTotalSupply = 0; + require(_granularity >= 1, "Granularity must be > 1"); + mGranularity = _granularity; + + mDefaultOperators = _defaultOperators; + for (uint256 i = 0; i < mDefaultOperators.length; i++) { + mIsDefaultOperator[mDefaultOperators[i]] = true; + } + + setInterfaceImplementation("ERC777Token", address(this)); + } + + /* -- ERC777 Interface Implementation -- */ + // + /// @return the name of the token + function name() public view returns (string memory) { return mName; } + + /// @return the symbol of the token + function symbol() public view returns (string memory) { return mSymbol; } + + /// @return the granularity of the token + function granularity() public view returns (uint256) { return mGranularity; } + + /// @return the total supply of the token + function totalSupply() public view returns (uint256) { return mTotalSupply; } + + /// @notice Return the account balance of some account + /// @param _tokenHolder Address for which the balance is returned + /// @return the balance of `_tokenAddress`. + function balanceOf(address _tokenHolder) public view returns (uint256) { return mBalances[_tokenHolder]; } + + /// @notice Return the list of default operators + /// @return the list of all the default operators + function defaultOperators() public view returns (address[] memory) { return mDefaultOperators; } + + /// @notice Send `_amount` of tokens to address `_to` passing `_data` to the recipient + /// @param _to The address of the recipient + /// @param _amount The number of tokens to be sent + function send(address _to, uint256 _amount, bytes calldata _data) external { + doSend(msg.sender, msg.sender, _to, _amount, _data, "", true); + } + + /// @notice Authorize a third party `_operator` to manage (send) `msg.sender`'s tokens. + /// @param _operator The operator that wants to be Authorized + function authorizeOperator(address _operator) external { + require(_operator != msg.sender, "Cannot authorize yourself as an operator"); + if (mIsDefaultOperator[_operator]) { + mRevokedDefaultOperator[_operator][msg.sender] = false; + } else { + mAuthorizedOperators[_operator][msg.sender] = true; + } + emit AuthorizedOperator(_operator, msg.sender); + } + + /// @notice Revoke a third party `_operator`'s rights to manage (send) `msg.sender`'s tokens. + /// @param _operator The operator that wants to be Revoked + function revokeOperator(address _operator) external { + require(_operator != msg.sender, "Cannot revoke yourself as an operator"); + if (mIsDefaultOperator[_operator]) { + mRevokedDefaultOperator[_operator][msg.sender] = true; + } else { + mAuthorizedOperators[_operator][msg.sender] = false; + } + emit RevokedOperator(_operator, msg.sender); + } + + /// @notice Check whether the `_operator` address is allowed to manage the tokens held by `_tokenHolder` address. + /// @param _operator address to check if it has the right to manage the tokens + /// @param _tokenHolder address which holds the tokens to be managed + /// @return `true` if `_operator` is authorized for `_tokenHolder` + function isOperatorFor(address _operator, address _tokenHolder) public view returns (bool) { + return (_operator == _tokenHolder // solium-disable-line operator-whitespace + || mAuthorizedOperators[_operator][_tokenHolder] + || (mIsDefaultOperator[_operator] && !mRevokedDefaultOperator[_operator][_tokenHolder])); + } + + /// @notice Send `_amount` of tokens on behalf of the address `from` to the address `to`. + /// @param _from The address holding the tokens being sent + /// @param _to The address of the recipient + /// @param _amount The number of tokens to be sent + /// @param _data Data generated by the user to be sent to the recipient + /// @param _operatorData Data generated by the operator to be sent to the recipient + function operatorSend( + address _from, + address _to, + uint256 _amount, + bytes calldata _data, + bytes calldata _operatorData + ) + external + { + require(isOperatorFor(msg.sender, _from), "Not an operator"); + doSend(msg.sender, _from, _to, _amount, _data, _operatorData, true); + } + + function burn(uint256 _amount, bytes calldata _data) external { + doBurn(msg.sender, msg.sender, _amount, _data, ""); + } + + function operatorBurn( + address _tokenHolder, + uint256 _amount, + bytes calldata _data, + bytes calldata _operatorData + ) + external + { + require(isOperatorFor(msg.sender, _tokenHolder), "Not an operator"); + doBurn(msg.sender, _tokenHolder, _amount, _data, _operatorData); + } + + /* -- Helper Functions -- */ + // + /// @notice Internal function that ensures `_amount` is multiple of the granularity + /// @param _amount The quantity that want's to be checked + function requireMultiple(uint256 _amount) internal view { + require(_amount % mGranularity == 0, "Amount is not a multiple of granualrity"); + } + + /// @notice Check whether an address is a regular address or not. + /// @param _addr Address of the contract that has to be checked + /// @return `true` if `_addr` is a regular address (not a contract) + function isRegularAddress(address _addr) internal view returns(bool) { + if (_addr == address(0)) { return false; } + uint size; + assembly { size := extcodesize(_addr) } // solium-disable-line security/no-inline-assembly + return size == 0; + } + + /// @notice Helper function actually performing the sending of tokens. + /// @param _operator The address performing the send + /// @param _from The address holding the tokens being sent + /// @param _to The address of the recipient + /// @param _amount The number of tokens to be sent + /// @param _data Data generated by the user to be passed to the recipient + /// @param _operatorData Data generated by the operator to be passed to the recipient + /// @param _preventLocking `true` if you want this function to throw when tokens are sent to a contract not + /// implementing `ERC777tokensRecipient`. + /// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer + /// functions SHOULD set this parameter to `false`. + function doSend( + address _operator, + address _from, + address _to, + uint256 _amount, + bytes memory _data, + bytes memory _operatorData, + bool _preventLocking + ) + internal + { + requireMultiple(_amount); + + callSender(_operator, _from, _to, _amount, _data, _operatorData); + + require(_to != address(0), "Cannot send to 0x0"); + require(mBalances[_from] >= _amount, "Not enough funds"); + + mBalances[_from] = mBalances[_from] - _amount; + mBalances[_to] = mBalances[_to] + _amount; + + callRecipient(_operator, _from, _to, _amount, _data, _operatorData, _preventLocking); + + emit Sent(_operator, _from, _to, _amount, _data, _operatorData); + } + + /// @notice Helper function actually performing the burning of tokens. + /// @param _operator The address performing the burn + /// @param _tokenHolder The address holding the tokens being burn + /// @param _amount The number of tokens to be burnt + /// @param _data Data generated by the token holder + /// @param _operatorData Data generated by the operator + function doBurn( + address _operator, + address _tokenHolder, + uint256 _amount, + bytes memory _data, + bytes memory _operatorData + ) + internal + { + callSender(_operator, _tokenHolder, address(0), _amount, _data, _operatorData); + + requireMultiple(_amount); + require(balanceOf(_tokenHolder) >= _amount, "Not enough funds"); + + mBalances[_tokenHolder] = mBalances[_tokenHolder] - _amount; + mTotalSupply = mTotalSupply - _amount; + + emit Burned(_operator, _tokenHolder, _amount, _data, _operatorData); + } + + /// @notice Helper function that checks for ERC777TokensRecipient on the recipient and calls it. + /// May throw according to `_preventLocking` + /// @param _operator The address performing the send or mint + /// @param _from The address holding the tokens being sent + /// @param _to The address of the recipient + /// @param _amount The number of tokens to be sent + /// @param _data Data generated by the user to be passed to the recipient + /// @param _operatorData Data generated by the operator to be passed to the recipient + /// @param _preventLocking `true` if you want this function to throw when tokens are sent to a contract not + /// implementing `ERC777TokensRecipient`. + /// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer + /// functions SHOULD set this parameter to `false`. + function callRecipient( + address _operator, + address _from, + address _to, + uint256 _amount, + bytes memory _data, + bytes memory _operatorData, + bool _preventLocking + ) + internal + { + address recipientImplementation = interfaceAddr(_to, "ERC777TokensRecipient"); + if (recipientImplementation != address(0)) { + IERC777Recipient(recipientImplementation).tokensReceived( + _operator, _from, _to, _amount, _data, _operatorData); + } else if (_preventLocking) { + require(isRegularAddress(_to), "Cannot send to contract without ERC777TokensRecipient"); + } + } + + /// @notice Helper function that checks for ERC777TokensSender on the sender and calls it. + /// May throw according to `_preventLocking` + /// @param _from The address holding the tokens being sent + /// @param _to The address of the recipient + /// @param _amount The amount of tokens to be sent + /// @param _data Data generated by the user to be passed to the recipient + /// @param _operatorData Data generated by the operator to be passed to the recipient + /// implementing `ERC777TokensSender`. + /// ERC777 native Send functions MUST set this parameter to `true`, and backwards compatible ERC20 transfer + /// functions SHOULD set this parameter to `false`. + function callSender( + address _operator, + address _from, + address _to, + uint256 _amount, + bytes memory _data, + bytes memory _operatorData + ) + internal + { + address senderImplementation = interfaceAddr(_from, "ERC777TokensSender"); + if (senderImplementation == address(0)) { return; } + IERC777Sender(senderImplementation).tokensToSend( + _operator, _from, _to, _amount, _data, _operatorData); + } + + +} \ No newline at end of file diff --git a/src/mocks/tokens/base/ERC1820Client.sol b/src/mocks/tokens/base/ERC1820Client.sol new file mode 100644 index 0000000..3979774 --- /dev/null +++ b/src/mocks/tokens/base/ERC1820Client.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.8.0; + +import {IERC1820Registry} from "../interfaces/IERC1820Registry.sol"; + +/// Base client to interact with the registry. +contract ERC1820Client { + IERC1820Registry constant ERC1820REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24); + + function setInterfaceImplementation(string memory _interfaceLabel, address _implementation) internal { + bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); + ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); + } + + function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { + bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); + return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash); + } + + function delegateManagement(address _newManager) internal { + ERC1820REGISTRY.setManager(address(this), _newManager); + } +} \ No newline at end of file diff --git a/src/mocks/tokens/interfaces/IERC777.sol b/src/mocks/tokens/interfaces/IERC777.sol index e0c0700..e52785c 100644 --- a/src/mocks/tokens/interfaces/IERC777.sol +++ b/src/mocks/tokens/interfaces/IERC777.sol @@ -107,7 +107,7 @@ interface IERC777 { * This list is immutable, but individual holders may revoke these via * `revokeOperator`, in which case `isOperatorFor` will return false. */ - function _defaultOperators() external view returns (address[] memory); + function defaultOperators() external view returns (address[] memory); /** * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must From 385edd078db11570c235addfef43705614969432 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:12:02 +0530 Subject: [PATCH 11/18] mocks: fmt check --- src/mocks/tokens/ERC777-token.sol | 93 ++++++++++++------------- src/mocks/tokens/base/ERC1820Client.sol | 4 +- 2 files changed, 45 insertions(+), 52 deletions(-) diff --git a/src/mocks/tokens/ERC777-token.sol b/src/mocks/tokens/ERC777-token.sol index 475e95f..acaea7f 100644 --- a/src/mocks/tokens/ERC777-token.sol +++ b/src/mocks/tokens/ERC777-token.sol @@ -11,19 +11,13 @@ contract ERC777 is IERC777, ERC1820Client { uint256 internal mGranularity; uint256 internal mTotalSupply; - mapping(address => uint) internal mBalances; + mapping(address => uint256) internal mBalances; address[] internal mDefaultOperators; mapping(address => bool) internal mIsDefaultOperator; - mapping(address => mapping(address => bool)) - internal mRevokedDefaultOperator; + mapping(address => mapping(address => bool)) internal mRevokedDefaultOperator; mapping(address => mapping(address => bool)) internal mAuthorizedOperators; - constructor( - string memory _name, - string memory _symbol, - uint256 _granularity, - address[] memory _defaultOperators - ) { + constructor(string memory _name, string memory _symbol, uint256 _granularity, address[] memory _defaultOperators) { mName = _name; mSymbol = _symbol; mTotalSupply = 0; @@ -41,25 +35,37 @@ contract ERC777 is IERC777, ERC1820Client { /* -- ERC777 Interface Implementation -- */ // /// @return the name of the token - function name() public view returns (string memory) { return mName; } + function name() public view returns (string memory) { + return mName; + } /// @return the symbol of the token - function symbol() public view returns (string memory) { return mSymbol; } + function symbol() public view returns (string memory) { + return mSymbol; + } /// @return the granularity of the token - function granularity() public view returns (uint256) { return mGranularity; } + function granularity() public view returns (uint256) { + return mGranularity; + } /// @return the total supply of the token - function totalSupply() public view returns (uint256) { return mTotalSupply; } + function totalSupply() public view returns (uint256) { + return mTotalSupply; + } /// @notice Return the account balance of some account /// @param _tokenHolder Address for which the balance is returned /// @return the balance of `_tokenAddress`. - function balanceOf(address _tokenHolder) public view returns (uint256) { return mBalances[_tokenHolder]; } + function balanceOf(address _tokenHolder) public view returns (uint256) { + return mBalances[_tokenHolder]; + } /// @notice Return the list of default operators /// @return the list of all the default operators - function defaultOperators() public view returns (address[] memory) { return mDefaultOperators; } + function defaultOperators() public view returns (address[] memory) { + return mDefaultOperators; + } /// @notice Send `_amount` of tokens to address `_to` passing `_data` to the recipient /// @param _to The address of the recipient @@ -97,9 +103,11 @@ contract ERC777 is IERC777, ERC1820Client { /// @param _tokenHolder address which holds the tokens to be managed /// @return `true` if `_operator` is authorized for `_tokenHolder` function isOperatorFor(address _operator, address _tokenHolder) public view returns (bool) { - return (_operator == _tokenHolder // solium-disable-line operator-whitespace - || mAuthorizedOperators[_operator][_tokenHolder] - || (mIsDefaultOperator[_operator] && !mRevokedDefaultOperator[_operator][_tokenHolder])); + return ( + _operator == _tokenHolder // solium-disable-line operator-whitespace + || mAuthorizedOperators[_operator][_tokenHolder] + || (mIsDefaultOperator[_operator] && !mRevokedDefaultOperator[_operator][_tokenHolder]) + ); } /// @notice Send `_amount` of tokens on behalf of the address `from` to the address `to`. @@ -114,9 +122,7 @@ contract ERC777 is IERC777, ERC1820Client { uint256 _amount, bytes calldata _data, bytes calldata _operatorData - ) - external - { + ) external { require(isOperatorFor(msg.sender, _from), "Not an operator"); doSend(msg.sender, _from, _to, _amount, _data, _operatorData, true); } @@ -125,12 +131,7 @@ contract ERC777 is IERC777, ERC1820Client { doBurn(msg.sender, msg.sender, _amount, _data, ""); } - function operatorBurn( - address _tokenHolder, - uint256 _amount, - bytes calldata _data, - bytes calldata _operatorData - ) + function operatorBurn(address _tokenHolder, uint256 _amount, bytes calldata _data, bytes calldata _operatorData) external { require(isOperatorFor(msg.sender, _tokenHolder), "Not an operator"); @@ -148,10 +149,12 @@ contract ERC777 is IERC777, ERC1820Client { /// @notice Check whether an address is a regular address or not. /// @param _addr Address of the contract that has to be checked /// @return `true` if `_addr` is a regular address (not a contract) - function isRegularAddress(address _addr) internal view returns(bool) { - if (_addr == address(0)) { return false; } - uint size; - assembly { size := extcodesize(_addr) } // solium-disable-line security/no-inline-assembly + function isRegularAddress(address _addr) internal view returns (bool) { + if (_addr == address(0)) return false; + uint256 size; + assembly { + size := extcodesize(_addr) + } // solium-disable-line security/no-inline-assembly return size == 0; } @@ -174,9 +177,7 @@ contract ERC777 is IERC777, ERC1820Client { bytes memory _data, bytes memory _operatorData, bool _preventLocking - ) - internal - { + ) internal { requireMultiple(_amount); callSender(_operator, _from, _to, _amount, _data, _operatorData); @@ -204,9 +205,7 @@ contract ERC777 is IERC777, ERC1820Client { uint256 _amount, bytes memory _data, bytes memory _operatorData - ) - internal - { + ) internal { callSender(_operator, _tokenHolder, address(0), _amount, _data, _operatorData); requireMultiple(_amount); @@ -238,13 +237,12 @@ contract ERC777 is IERC777, ERC1820Client { bytes memory _data, bytes memory _operatorData, bool _preventLocking - ) - internal - { + ) internal { address recipientImplementation = interfaceAddr(_to, "ERC777TokensRecipient"); if (recipientImplementation != address(0)) { IERC777Recipient(recipientImplementation).tokensReceived( - _operator, _from, _to, _amount, _data, _operatorData); + _operator, _from, _to, _amount, _data, _operatorData + ); } else if (_preventLocking) { require(isRegularAddress(_to), "Cannot send to contract without ERC777TokensRecipient"); } @@ -267,14 +265,9 @@ contract ERC777 is IERC777, ERC1820Client { uint256 _amount, bytes memory _data, bytes memory _operatorData - ) - internal - { + ) internal { address senderImplementation = interfaceAddr(_from, "ERC777TokensSender"); - if (senderImplementation == address(0)) { return; } - IERC777Sender(senderImplementation).tokensToSend( - _operator, _from, _to, _amount, _data, _operatorData); + if (senderImplementation == address(0)) return; + IERC777Sender(senderImplementation).tokensToSend(_operator, _from, _to, _amount, _data, _operatorData); } - - -} \ No newline at end of file +} diff --git a/src/mocks/tokens/base/ERC1820Client.sol b/src/mocks/tokens/base/ERC1820Client.sol index 3979774..eeae9d9 100644 --- a/src/mocks/tokens/base/ERC1820Client.sol +++ b/src/mocks/tokens/base/ERC1820Client.sol @@ -11,7 +11,7 @@ contract ERC1820Client { ERC1820REGISTRY.setInterfaceImplementer(address(this), interfaceHash, _implementation); } - function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns(address) { + function interfaceAddr(address addr, string memory _interfaceLabel) internal view returns (address) { bytes32 interfaceHash = keccak256(abi.encodePacked(_interfaceLabel)); return ERC1820REGISTRY.getInterfaceImplementer(addr, interfaceHash); } @@ -19,4 +19,4 @@ contract ERC1820Client { function delegateManagement(address _newManager) internal { ERC1820REGISTRY.setManager(address(this), _newManager); } -} \ No newline at end of file +} From 508604e960c7a2f1d72f9d3e7901957e687fff22 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:46:29 +0530 Subject: [PATCH 12/18] mocks: malicious returnbomb --- src/mocks/malicious/returnBomb.sol | 9 +++++++++ test/mocks/malicious/returnBomb.t.sol | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/mocks/malicious/returnBomb.sol create mode 100644 test/mocks/malicious/returnBomb.t.sol diff --git a/src/mocks/malicious/returnBomb.sol b/src/mocks/malicious/returnBomb.sol new file mode 100644 index 0000000..e560fcc --- /dev/null +++ b/src/mocks/malicious/returnBomb.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.8.0; + +contract returnBomb { + fallback() external { + assembly { + revert(1, 10000000) + } + } +} diff --git a/test/mocks/malicious/returnBomb.t.sol b/test/mocks/malicious/returnBomb.t.sol new file mode 100644 index 0000000..33a00ca --- /dev/null +++ b/test/mocks/malicious/returnBomb.t.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +import {returnBomb} from "../../../src/mocks/malicious/returnBomb.sol"; + +contract returnBombTest is Test { + returnBomb public attacker; + + function setUp() public { + attacker = new returnBomb(); + } + + function testReturnBomb() public { + (bool success, bytes memory returnData) = address(attacker).call{gas: 3397}(""); + assertEq(success, false); + } +} From 0c56098717acfec64649bb57ee62d79de3ea50b9 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Fri, 1 Dec 2023 12:04:47 +0530 Subject: [PATCH 13/18] mocks: make review changes to ERC20 behaviour tokens --- src/mocks/tokens/ERC20-bool.sol | 42 ++++++++++++-------------- src/mocks/tokens/ERC20-feeTransfer.sol | 6 +++- src/mocks/tokens/ERC20-rebase.sol | 8 +++++ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/mocks/tokens/ERC20-bool.sol b/src/mocks/tokens/ERC20-bool.sol index 14ec49a..a448e2f 100644 --- a/src/mocks/tokens/ERC20-bool.sol +++ b/src/mocks/tokens/ERC20-bool.sol @@ -15,33 +15,29 @@ contract ERC20Bool is ERC20Base { } function transfer(address to, uint256 amount) public override returns (bool) { - unchecked { - if (balanceOf[msg.sender] >= amount && balanceOf[to] + amount >= balanceOf[to]) { - balanceOf[to] += amount; - balanceOf[msg.sender] -= amount; - emit Transfer(msg.sender, to, amount); - return true; - } else { - return false; - } + if (balanceOf[msg.sender] >= amount && balanceOf[to] + amount >= balanceOf[to]) { + balanceOf[to] += amount; + balanceOf[msg.sender] -= amount; + emit Transfer(msg.sender, to, amount); + return true; + } else { + return false; } } function transferFrom(address from, address to, uint256 amount) public override returns (bool) { - unchecked { - if ( - balanceOf[from] >= amount && allowance[from][msg.sender] >= amount - && balanceOf[to] + amount >= balanceOf[to] - ) { - balanceOf[to] += amount; - balanceOf[from] -= amount; - emit Transfer(from, to, amount); - allowance[from][msg.sender] -= amount; - emit Approval(from, msg.sender, allowance[from][msg.sender]); - return true; - } else { - return false; - } + if ( + balanceOf[from] >= amount && allowance[from][msg.sender] >= amount + && balanceOf[to] + amount >= balanceOf[to] + ) { + balanceOf[to] += amount; + balanceOf[from] -= amount; + emit Transfer(from, to, amount); + allowance[from][msg.sender] -= amount; + emit Approval(from, msg.sender, allowance[from][msg.sender]); + return true; + } else { + return false; } } } diff --git a/src/mocks/tokens/ERC20-feeTransfer.sol b/src/mocks/tokens/ERC20-feeTransfer.sol index 48048b0..a03bcc0 100644 --- a/src/mocks/tokens/ERC20-feeTransfer.sol +++ b/src/mocks/tokens/ERC20-feeTransfer.sol @@ -4,7 +4,7 @@ import "forge-std/interfaces/IERC20.sol"; import {ERC20Base} from "./base/ERC20Base.sol"; contract ERC20FeeTransfer is ERC20Base { - uint256 immutable fee; + uint256 public fee; constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _initialSupply, uint256 _fee) { name = _name; @@ -17,6 +17,10 @@ contract ERC20FeeTransfer is ERC20Base { emit Transfer(address(0), msg.sender, _initialSupply); } + function setFee(uint256 _fee) public { + fee = _fee; + } + function transfer(address to, uint256 amount) public override returns (bool) { require(balanceOf[msg.sender] >= amount, "ERC20Fee: Insufficient-balance"); diff --git a/src/mocks/tokens/ERC20-rebase.sol b/src/mocks/tokens/ERC20-rebase.sol index e7bb2d2..d9970bf 100644 --- a/src/mocks/tokens/ERC20-rebase.sol +++ b/src/mocks/tokens/ERC20-rebase.sol @@ -31,6 +31,14 @@ contract ERC20rebase is ERC20Base, Test { emit Transfer(address(0), msg.sender, _initialSupply); } + function setRebaseAmount(uint256 _rebaseAmt) public { + rebaseAmt = _rebaseAmt; + } + + function setRebaseInterval(uint256 _rebaseInterval) public { + rebaseInterval = _rebaseInterval; + } + modifier rebase() { uint256 timeSinceLastRebase = block.timestamp - lastRebaseTimestamp; if (timeSinceLastRebase >= rebaseInterval) { From 8bf9baaa2994d6c19e5ded0e4a5fa49b00b2cefb Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Mon, 4 Dec 2023 18:23:28 +0530 Subject: [PATCH 14/18] mocks: add gasExhaust contract --- src/mocks/malicious/gasExhaust.sol | 16 ++++++++++++++++ test/mocks/malicious/gasExhaust.t.sol | 25 +++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/mocks/malicious/gasExhaust.sol create mode 100644 test/mocks/malicious/gasExhaust.t.sol diff --git a/src/mocks/malicious/gasExhaust.sol b/src/mocks/malicious/gasExhaust.sol new file mode 100644 index 0000000..d09d27d --- /dev/null +++ b/src/mocks/malicious/gasExhaust.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.8.0; + +contract gasExhaust { + uint256 counter; + uint256 gasConsumeLimit; + + function setGasConsumeLimit(uint256 _gasConsume) public { + gasConsumeLimit = _gasConsume; + } + + fallback() external { + while (gasleft() > gasConsumeLimit) { + counter++; + } + } +} diff --git a/test/mocks/malicious/gasExhaust.t.sol b/test/mocks/malicious/gasExhaust.t.sol new file mode 100644 index 0000000..8f51d99 --- /dev/null +++ b/test/mocks/malicious/gasExhaust.t.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; + +import {gasExhaust} from "../../../src/mocks/malicious/gasExhaust.sol"; + +contract gasExhaustTest is Test { + gasExhaust public attacker; + + function setUp() public { + attacker = new gasExhaust(); + } + + function testGasExhaustSuccess() public { + attacker.setGasConsumeLimit(6000); + (bool success, bytes memory returnData) = address(attacker).call{gas: 25000}(""); + assertEq(success, true); + } + + function testGasExhaustFail() public { + attacker.setGasConsumeLimit(100); + (bool success, bytes memory returnData) = address(attacker).call{gas: 25000}(""); + assertEq(success, false); + } +} From ccccbb69bd6602c47bfdfc6bd567a499466a9c84 Mon Sep 17 00:00:00 2001 From: arbazkiraak <13177578+arbazkiraak@users.noreply.github.com> Date: Tue, 12 Dec 2023 21:35:55 +0530 Subject: [PATCH 15/18] mocks: add returnBomb case and test --- src/mocks/malicious/returnBomb.sol | 17 +++++++++++++++-- test/mocks/malicious/returnBomb.t.sol | 21 ++++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/mocks/malicious/returnBomb.sol b/src/mocks/malicious/returnBomb.sol index e560fcc..c321245 100644 --- a/src/mocks/malicious/returnBomb.sol +++ b/src/mocks/malicious/returnBomb.sol @@ -1,9 +1,22 @@ pragma solidity ^0.8.0; +/* + +The returnDataSize value needs careful calibration: it should not be so high that it depletes all the gas, causing a revert, nor should it be so low that the function consumes all the gas yet still returns the data successfully. + +Our goal is to determine an optimal median value for returnDataSize that will ensure the outer call reverts as intended. +*/ + contract returnBomb { - fallback() external { + uint128 public returnDataSize = 10000; // by default + + function setReturnDataSize(uint128 _returnDataSize) external { + returnDataSize = _returnDataSize; + } + + fallback () external { assembly { - revert(1, 10000000) + revert(0,returnDataSize.slot) } } } diff --git a/test/mocks/malicious/returnBomb.t.sol b/test/mocks/malicious/returnBomb.t.sol index 33a00ca..7ab795a 100644 --- a/test/mocks/malicious/returnBomb.t.sol +++ b/test/mocks/malicious/returnBomb.t.sol @@ -11,8 +11,23 @@ contract returnBombTest is Test { attacker = new returnBomb(); } - function testReturnBomb() public { - (bool success, bytes memory returnData) = address(attacker).call{gas: 3397}(""); - assertEq(success, false); + function callSomething() public { + uint256 innerGasVal = gasleft() / 2; + (bool success, bytes memory returnData) = address(attacker).call{gas: innerGasVal}(""); + console.log("returnBomb Innercall success: ", success); + } + + function testReturnBombRevert() public { + vm.expectRevert(); + this.callSomething{gas: 3000}(); + } + + function testReturnBombNotRevert() public { + this.callSomething{gas: 10000}(); + } + + function testSetters() public { + attacker.setReturnDataSize(50000); + assertEq(attacker.returnDataSize(),50000); } } From e83a1bdde66ad793ec2a4b4cd4fe726b4785879e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Mu=C3=B1oz-McDonald?= Date: Thu, 14 Dec 2023 17:14:44 +0000 Subject: [PATCH 16/18] Forge fmt --- src/mocks/malicious/returnBomb.sol | 4 ++-- test/mocks/malicious/returnBomb.t.sol | 2 +- test/mocks/tokens/ERC20-bool.t.sol | 2 +- test/mocks/tokens/ERC20-feeTransfer.t.sol | 2 +- test/mocks/tokens/ERC20-rebase.t.sol | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mocks/malicious/returnBomb.sol b/src/mocks/malicious/returnBomb.sol index c321245..c20861b 100644 --- a/src/mocks/malicious/returnBomb.sol +++ b/src/mocks/malicious/returnBomb.sol @@ -14,9 +14,9 @@ contract returnBomb { returnDataSize = _returnDataSize; } - fallback () external { + fallback() external { assembly { - revert(0,returnDataSize.slot) + revert(0, returnDataSize.slot) } } } diff --git a/test/mocks/malicious/returnBomb.t.sol b/test/mocks/malicious/returnBomb.t.sol index 7ab795a..c77fccd 100644 --- a/test/mocks/malicious/returnBomb.t.sol +++ b/test/mocks/malicious/returnBomb.t.sol @@ -28,6 +28,6 @@ contract returnBombTest is Test { function testSetters() public { attacker.setReturnDataSize(50000); - assertEq(attacker.returnDataSize(),50000); + assertEq(attacker.returnDataSize(), 50000); } } diff --git a/test/mocks/tokens/ERC20-bool.t.sol b/test/mocks/tokens/ERC20-bool.t.sol index 384e0f2..106bc7c 100644 --- a/test/mocks/tokens/ERC20-bool.t.sol +++ b/test/mocks/tokens/ERC20-bool.t.sol @@ -12,7 +12,7 @@ contract ERC20BoolTest is Test { address bob = vm.addr(2); function setUp() public { - boolToken = new ERC20Bool("BoolToken","Btoken",18,initialSupply); + boolToken = new ERC20Bool("BoolToken", "Btoken", 18, initialSupply); assertEq(boolToken.balanceOf(address(this)), initialSupply); } diff --git a/test/mocks/tokens/ERC20-feeTransfer.t.sol b/test/mocks/tokens/ERC20-feeTransfer.t.sol index eb9a9bb..d877872 100644 --- a/test/mocks/tokens/ERC20-feeTransfer.t.sol +++ b/test/mocks/tokens/ERC20-feeTransfer.t.sol @@ -13,7 +13,7 @@ contract ERC20FeeTest is Test { address bob = vm.addr(2); function setUp() public { - feeToken = new ERC20FeeTransfer("FeeToken","Ftoken",18,initialSupply,fee); + feeToken = new ERC20FeeTransfer("FeeToken", "Ftoken", 18, initialSupply, fee); assertEq(feeToken.balanceOf(address(this)), initialSupply); } diff --git a/test/mocks/tokens/ERC20-rebase.t.sol b/test/mocks/tokens/ERC20-rebase.t.sol index 410777d..6415018 100644 --- a/test/mocks/tokens/ERC20-rebase.t.sol +++ b/test/mocks/tokens/ERC20-rebase.t.sol @@ -14,7 +14,7 @@ contract ERC20RebasingTest is Test { address bob = vm.addr(2); function setUp() public { - rebasingToken = new ERC20rebase("BoolToken","Btoken",18,initialSupply,rebasingInterval,rebasingAmt); + rebasingToken = new ERC20rebase("BoolToken", "Btoken", 18, initialSupply, rebasingInterval, rebasingAmt); assertEq(rebasingToken.balanceOf(address(this)), initialSupply); } From bdfd441bf99dcff8d020f3791945434adb10103b Mon Sep 17 00:00:00 2001 From: Arbaz Hussain Date: Fri, 15 Dec 2023 13:05:54 +0530 Subject: [PATCH 17/18] mocks: change erc20-rebase naming convention --- src/mocks/tokens/ERC20-rebase.sol | 2 +- test/mocks/tokens/ERC20-rebase.t.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mocks/tokens/ERC20-rebase.sol b/src/mocks/tokens/ERC20-rebase.sol index d9970bf..88e55bb 100644 --- a/src/mocks/tokens/ERC20-rebase.sol +++ b/src/mocks/tokens/ERC20-rebase.sol @@ -5,7 +5,7 @@ import {ERC20Base} from "./base/ERC20Base.sol"; import "forge-std/Test.sol"; -contract ERC20rebase is ERC20Base, Test { +contract ERC20Rebase is ERC20Base, Test { uint256 public lastRebaseTimestamp; uint256 public rebaseInterval = 1 minutes; uint256 public rebaseAmt = 5; diff --git a/test/mocks/tokens/ERC20-rebase.t.sol b/test/mocks/tokens/ERC20-rebase.t.sol index 6415018..51ff6fc 100644 --- a/test/mocks/tokens/ERC20-rebase.t.sol +++ b/test/mocks/tokens/ERC20-rebase.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import "forge-std/interfaces/IERC20.sol"; -import {ERC20rebase} from "../../../src/mocks/tokens/ERC20-rebase.sol"; +import {ERC20Rebase} from "../../../src/mocks/tokens/ERC20-rebase.sol"; contract ERC20RebasingTest is Test { IERC20 public rebasingToken; @@ -14,7 +14,7 @@ contract ERC20RebasingTest is Test { address bob = vm.addr(2); function setUp() public { - rebasingToken = new ERC20rebase("BoolToken", "Btoken", 18, initialSupply, rebasingInterval, rebasingAmt); + rebasingToken = new ERC20Rebase("BoolToken", "Btoken", 18, initialSupply, rebasingInterval, rebasingAmt); assertEq(rebasingToken.balanceOf(address(this)), initialSupply); } From 0a73a716ca5a03dc00f7a1e9b4c4fe2dbea38694 Mon Sep 17 00:00:00 2001 From: Arbaz Hussain Date: Fri, 15 Dec 2023 13:17:14 +0530 Subject: [PATCH 18/18] mocks: update README --- README.md | 1 + src/mocks/README.md | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 87837da..e2cbefe 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ forge test -vv --match-path test/[test_name] | Reentrancy | [reentrancy](https://github.com/immunefi-team/forge-poc-templates/tree/reentrancy) | [Source](./src/reentrancy/Reentrancy.sol) | [Readme](./src/reentrancy/README.md) | | Flash Loan | [flash_loan](https://github.com/immunefi-team/forge-poc-templates/tree/flash_loan) | [Source](./src/flashloan/FlashLoan.sol) | [Readme](./src/flashloan/README.md) | | Price Manipulation | [price_manipulation](https://github.com/immunefi-team/forge-poc-templates/tree/price_manipulation) | [Source](./src/pricemanipulation/PriceManipulation.sol) | [Readme](./src/pricemanipulation/README.md) | +| Boilterplate Mocks | [mocks](https://github.com/immunefi-team/forge-poc-templates/tree/mocks) | [Source](./src/mocks/) | [Readme](./src/mocks/README.md) | diff --git a/src/mocks/README.md b/src/mocks/README.md index afb0712..297d9b2 100644 --- a/src/mocks/README.md +++ b/src/mocks/README.md @@ -1,15 +1,26 @@ -## Vulnerability Type +# Boilerplate Mock Contracts Repository -WIP: - -This template category provides a diverse collection of boilerplate contracts which enables the whitehat to seamlessly integrate and conduct swift tests on the target contract. The range encompasses mock tokens adhering to unconventional ERC-20 standards, as well as potentially harmful contracts. - -
- +## Introduction +Welcome to the Boilerplate Mock Contracts repository. This collection offers a wide array of mock contracts, specifically tailored for whitehat testing and seamless integration into various blockchain projects. From unconventional ERC-20 tokens to potentially hazardous contracts, our repository provides essential tools for rigorous smart contract testing. +## Directory Structure +The repository is organized into different categories, each containing specific types of contracts: ## Usage +The following templates represent the core of the Boilerplate Mock Contracts. You can quickly integrate and test these templates in your projects. -The following attack contract demonstrate a simple ERC20 token which returns a `bool` on the transfer. +### Templates +* [Malicious Contracts](./malicious) + * [Gas Exhaust](./malicious/gasExhaust.sol) + * [Return Bomb](./malicious/returnBomb.sol) + * [Self-Destruct](./malicious/self-destruct.sol) +* [Miscellaneous Contracts](./miscs) + * [Proxy Contract](./miscs/Proxy.sol) +* [Token Contracts](./tokens) + * [ERC20 Bool](./tokens/ERC20-bool.sol) + * [ERC20 with Fee Transfer](./tokens/ERC20-feeTransfer.sol) + * [ERC20 Rebase](./tokens/ERC20-rebase.sol) + * [ERC777 Token](./tokens/ERC777-token.sol) -* [ERC20Test](../../test/examples/ERC20Example.t.sol) \ No newline at end of file +## Ideas and Contributions +We are always looking for new ideas to expand our collection. If you have suggestions for new templates or improvements, feel free to create a GitHub issue or submit a pull request. Your contributions help make this repository a valuable resource for the blockchain development community. \ No newline at end of file