Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mock contracts for ERC20 and ERC721 #470

Merged
merged 7 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/StdUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;

import {IMulticall3} from "./interfaces/IMulticall3.sol";
import {MockERC20} from "./mocks/MockERC20.sol";
import {MockERC721} from "./mocks/MockERC721.sol";
import {VmSafe} from "./Vm.sol";

abstract contract StdUtils {
Expand Down Expand Up @@ -133,6 +135,21 @@ abstract contract StdUtils {
return computeCreate2Address(salt, initCodeHash, CREATE2_FACTORY);
}

/// @dev returns an initialized mock ERC20 contract
function deployMockERC20(string memory name, string memory symbol, uint8 decimals)
internal
returns (MockERC20 mock)
{
mock = new MockERC20();
mock.initialize(name, symbol, decimals);
}

/// @dev returns an initialized mock ERC721 contract
function deployMockERC721(string memory name, string memory symbol) internal returns (MockERC721 mock) {
mock = new MockERC721();
mock.initialize(name, symbol);
}

/// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments
/// @param creationCode the creation code of a contract C, as returned by type(C).creationCode
function hashInitCode(bytes memory creationCode) internal pure returns (bytes32) {
Expand Down
216 changes: 216 additions & 0 deletions src/mocks/MockERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;

/// @notice This is a mock contract of the ERC20 standard for testing purposes only, it SHOULD NOT be used in production.
/// @dev Forked from: https://github.com/transmissions11/solmate/blob/0384dbaaa4fcb5715738a9254a7c0a4cb62cf458/src/tokens/ERC20.sol
contract MockERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/

event Transfer(address indexed from, address indexed to, uint256 amount);

event Approval(address indexed owner, address indexed spender, uint256 amount);

/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/

string public name;

string public symbol;

uint8 public decimals;

/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/

uint256 public totalSupply;

mapping(address => uint256) public balanceOf;

mapping(address => mapping(address => uint256)) public allowance;

/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/

uint256 internal INITIAL_CHAIN_ID;

bytes32 internal INITIAL_DOMAIN_SEPARATOR;

mapping(address => uint256) public nonces;

/*//////////////////////////////////////////////////////////////
INITIALIZE
//////////////////////////////////////////////////////////////*/

/// @dev A bool to track whether the contract has been initialized.
bool private initialized;

/// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and
/// syntaxes, we add an initialization function that can be called only once.
function initialize(string memory _name, string memory _symbol, uint8 _decimals) public {
require(!initialized, "ALREADY_INITIALIZED");

name = _name;
symbol = _symbol;
decimals = _decimals;

INITIAL_CHAIN_ID = _pureChainId();
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();

initialized = true;
}

/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/

function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;

emit Approval(msg.sender, spender, amount);

return true;
}

function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] = _sub(balanceOf[msg.sender], amount);
balanceOf[to] = _add(balanceOf[to], amount);

emit Transfer(msg.sender, to, amount);

return true;
}

function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

if (allowed != ~uint256(0)) allowance[from][msg.sender] = _sub(allowed, amount);

balanceOf[from] = _sub(balanceOf[from], amount);
balanceOf[to] = _add(balanceOf[to], amount);

emit Transfer(from, to, amount);

return true;
}

/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/

function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
public
virtual
{
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);

require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

allowance[recoveredAddress][spender] = value;

emit Approval(owner, spender, value);
}

function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return _pureChainId() == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}

function computeDomainSeparator() internal view virtual returns (bytes32) {
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
_pureChainId(),
address(this)
)
);
}

/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/

function _mint(address to, uint256 amount) internal virtual {
totalSupply = _add(totalSupply, amount);
balanceOf[to] = _add(balanceOf[to], amount);

emit Transfer(address(0), to, amount);
}

function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] = _sub(balanceOf[from], amount);
totalSupply = _sub(totalSupply, amount);

emit Transfer(from, address(0), amount);
}

/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MATH LOGIC
//////////////////////////////////////////////////////////////*/

function _add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "ERC20: addition overflow");
return c;
}

function _sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(a >= b, "ERC20: subtraction underflow");
return a - b;
}

/*//////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////*/

// We use this complex approach of `_viewChainId` and `_pureChainId` to ensure there are no
// compiler warnings when accessing chain ID in any solidity version supported by forge-std. We
// can't simply access the chain ID in a normal view or pure function because the solc View Pure
// Checker changed `chainid` from pure to view in 0.8.0.
function _viewChainId() private view returns (uint256 chainId) {
// Assembly required since `block.chainid` was introduced in 0.8.0.
assembly {
chainId := chainid()
}

address(this); // Silence warnings in older Solc versions.
}

function _pureChainId() private pure returns (uint256 chainId) {
function() internal view returns (uint256) fnIn = _viewChainId;
function() internal pure returns (uint256) pureChainId;
assembly {
pureChainId := fnIn
}
chainId = pureChainId();
}
}
Loading