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 4 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 {ERC20Mock} from "./mocks/ERC20Mock.sol";
import {ERC721Mock} from "./mocks/ERC721Mock.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 a initilized mock ERC20 contract
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
function deployERC20Mock(string memory name, string memory symbol, uint8 decimals)
internal
returns (ERC20Mock mock)
{
mock = new ERC20Mock();
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
mock.initialize(name, symbol, decimals);
}

/// @dev returns a initilized mock ERC721 contract
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
function deployERC721Mock(string memory name, string memory symbol) internal returns (ERC721Mock mock) {
mock = new ERC721Mock();
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/ERC20Mock.sol
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
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 ERC20Mock {
/*//////////////////////////////////////////////////////////////
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 a initialization function that can be called only once.
andreivladbrg marked this conversation as resolved.
Show resolved Hide resolved
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(b <= a, "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