Skip to content

Commit 9992210

Browse files
andreivladbrgmds1
andauthored
feat: add mock contracts for ERC20 and ERC721 (#470)
* feat: add mock contracts for ERC20 and ERC721 * fix: make mocks compatible with all solc versions * refactor: make isContract function private * feat: add deployERC20Mock function in StdUtils feat: add deployERC721Mock function in StdUtils refactor: remove constructor in mocks and add initialization function * refactor: use "Mock" as a prefix chore: address grammar issues chore: add "I" prefix * test: add mocks tests * chore: fix typo --------- Co-authored-by: Matt Solomon <[email protected]>
1 parent ec55ee9 commit 9992210

File tree

5 files changed

+1616
-0
lines changed

5 files changed

+1616
-0
lines changed

src/StdUtils.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ pragma solidity >=0.6.2 <0.9.0;
44
pragma experimental ABIEncoderV2;
55

66
import {IMulticall3} from "./interfaces/IMulticall3.sol";
7+
import {MockERC20} from "./mocks/MockERC20.sol";
8+
import {MockERC721} from "./mocks/MockERC721.sol";
79
import {VmSafe} from "./Vm.sol";
810

911
abstract contract StdUtils {
@@ -117,6 +119,21 @@ abstract contract StdUtils {
117119
return vm.computeCreate2Address(salt, initCodeHash);
118120
}
119121

122+
/// @dev returns an initialized mock ERC20 contract
123+
function deployMockERC20(string memory name, string memory symbol, uint8 decimals)
124+
internal
125+
returns (MockERC20 mock)
126+
{
127+
mock = new MockERC20();
128+
mock.initialize(name, symbol, decimals);
129+
}
130+
131+
/// @dev returns an initialized mock ERC721 contract
132+
function deployMockERC721(string memory name, string memory symbol) internal returns (MockERC721 mock) {
133+
mock = new MockERC721();
134+
mock.initialize(name, symbol);
135+
}
136+
120137
/// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments
121138
/// @param creationCode the creation code of a contract C, as returned by type(C).creationCode
122139
function hashInitCode(bytes memory creationCode) internal pure returns (bytes32) {

src/mocks/MockERC20.sol

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.6.2 <0.9.0;
3+
4+
/// @notice This is a mock contract of the ERC20 standard for testing purposes only, it SHOULD NOT be used in production.
5+
/// @dev Forked from: https://github.com/transmissions11/solmate/blob/0384dbaaa4fcb5715738a9254a7c0a4cb62cf458/src/tokens/ERC20.sol
6+
contract MockERC20 {
7+
/*//////////////////////////////////////////////////////////////
8+
EVENTS
9+
//////////////////////////////////////////////////////////////*/
10+
11+
event Transfer(address indexed from, address indexed to, uint256 amount);
12+
13+
event Approval(address indexed owner, address indexed spender, uint256 amount);
14+
15+
/*//////////////////////////////////////////////////////////////
16+
METADATA STORAGE
17+
//////////////////////////////////////////////////////////////*/
18+
19+
string public name;
20+
21+
string public symbol;
22+
23+
uint8 public decimals;
24+
25+
/*//////////////////////////////////////////////////////////////
26+
ERC20 STORAGE
27+
//////////////////////////////////////////////////////////////*/
28+
29+
uint256 public totalSupply;
30+
31+
mapping(address => uint256) public balanceOf;
32+
33+
mapping(address => mapping(address => uint256)) public allowance;
34+
35+
/*//////////////////////////////////////////////////////////////
36+
EIP-2612 STORAGE
37+
//////////////////////////////////////////////////////////////*/
38+
39+
uint256 internal INITIAL_CHAIN_ID;
40+
41+
bytes32 internal INITIAL_DOMAIN_SEPARATOR;
42+
43+
mapping(address => uint256) public nonces;
44+
45+
/*//////////////////////////////////////////////////////////////
46+
INITIALIZE
47+
//////////////////////////////////////////////////////////////*/
48+
49+
/// @dev A bool to track whether the contract has been initialized.
50+
bool private initialized;
51+
52+
/// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and
53+
/// syntaxes, we add an initialization function that can be called only once.
54+
function initialize(string memory _name, string memory _symbol, uint8 _decimals) public {
55+
require(!initialized, "ALREADY_INITIALIZED");
56+
57+
name = _name;
58+
symbol = _symbol;
59+
decimals = _decimals;
60+
61+
INITIAL_CHAIN_ID = _pureChainId();
62+
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
63+
64+
initialized = true;
65+
}
66+
67+
/*//////////////////////////////////////////////////////////////
68+
ERC20 LOGIC
69+
//////////////////////////////////////////////////////////////*/
70+
71+
function approve(address spender, uint256 amount) public virtual returns (bool) {
72+
allowance[msg.sender][spender] = amount;
73+
74+
emit Approval(msg.sender, spender, amount);
75+
76+
return true;
77+
}
78+
79+
function transfer(address to, uint256 amount) public virtual returns (bool) {
80+
balanceOf[msg.sender] = _sub(balanceOf[msg.sender], amount);
81+
balanceOf[to] = _add(balanceOf[to], amount);
82+
83+
emit Transfer(msg.sender, to, amount);
84+
85+
return true;
86+
}
87+
88+
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
89+
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
90+
91+
if (allowed != ~uint256(0)) allowance[from][msg.sender] = _sub(allowed, amount);
92+
93+
balanceOf[from] = _sub(balanceOf[from], amount);
94+
balanceOf[to] = _add(balanceOf[to], amount);
95+
96+
emit Transfer(from, to, amount);
97+
98+
return true;
99+
}
100+
101+
/*//////////////////////////////////////////////////////////////
102+
EIP-2612 LOGIC
103+
//////////////////////////////////////////////////////////////*/
104+
105+
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
106+
public
107+
virtual
108+
{
109+
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
110+
111+
address recoveredAddress = ecrecover(
112+
keccak256(
113+
abi.encodePacked(
114+
"\x19\x01",
115+
DOMAIN_SEPARATOR(),
116+
keccak256(
117+
abi.encode(
118+
keccak256(
119+
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
120+
),
121+
owner,
122+
spender,
123+
value,
124+
nonces[owner]++,
125+
deadline
126+
)
127+
)
128+
)
129+
),
130+
v,
131+
r,
132+
s
133+
);
134+
135+
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
136+
137+
allowance[recoveredAddress][spender] = value;
138+
139+
emit Approval(owner, spender, value);
140+
}
141+
142+
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
143+
return _pureChainId() == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
144+
}
145+
146+
function computeDomainSeparator() internal view virtual returns (bytes32) {
147+
return keccak256(
148+
abi.encode(
149+
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
150+
keccak256(bytes(name)),
151+
keccak256("1"),
152+
_pureChainId(),
153+
address(this)
154+
)
155+
);
156+
}
157+
158+
/*//////////////////////////////////////////////////////////////
159+
INTERNAL MINT/BURN LOGIC
160+
//////////////////////////////////////////////////////////////*/
161+
162+
function _mint(address to, uint256 amount) internal virtual {
163+
totalSupply = _add(totalSupply, amount);
164+
balanceOf[to] = _add(balanceOf[to], amount);
165+
166+
emit Transfer(address(0), to, amount);
167+
}
168+
169+
function _burn(address from, uint256 amount) internal virtual {
170+
balanceOf[from] = _sub(balanceOf[from], amount);
171+
totalSupply = _sub(totalSupply, amount);
172+
173+
emit Transfer(from, address(0), amount);
174+
}
175+
176+
/*//////////////////////////////////////////////////////////////
177+
INTERNAL SAFE MATH LOGIC
178+
//////////////////////////////////////////////////////////////*/
179+
180+
function _add(uint256 a, uint256 b) internal pure returns (uint256) {
181+
uint256 c = a + b;
182+
require(c >= a, "ERC20: addition overflow");
183+
return c;
184+
}
185+
186+
function _sub(uint256 a, uint256 b) internal pure returns (uint256) {
187+
require(a >= b, "ERC20: subtraction underflow");
188+
return a - b;
189+
}
190+
191+
/*//////////////////////////////////////////////////////////////
192+
HELPERS
193+
//////////////////////////////////////////////////////////////*/
194+
195+
// We use this complex approach of `_viewChainId` and `_pureChainId` to ensure there are no
196+
// compiler warnings when accessing chain ID in any solidity version supported by forge-std. We
197+
// can't simply access the chain ID in a normal view or pure function because the solc View Pure
198+
// Checker changed `chainid` from pure to view in 0.8.0.
199+
function _viewChainId() private view returns (uint256 chainId) {
200+
// Assembly required since `block.chainid` was introduced in 0.8.0.
201+
assembly {
202+
chainId := chainid()
203+
}
204+
205+
address(this); // Silence warnings in older Solc versions.
206+
}
207+
208+
function _pureChainId() private pure returns (uint256 chainId) {
209+
function() internal view returns (uint256) fnIn = _viewChainId;
210+
function() internal pure returns (uint256) pureChainId;
211+
assembly {
212+
pureChainId := fnIn
213+
}
214+
chainId = pureChainId();
215+
}
216+
}

0 commit comments

Comments
 (0)