Commit 9cbb9fb3 authored by Mark Tyneway's avatar Mark Tyneway

contracts-bedrock: modularize mocks

There are a bunch of contracts mocks used only
for testing. This commit moves the mocks to a particular
directory `test/mocks` so that we can standardize around
that as being the location for mock contracts only used
for testing to go. This commit exists to make
https://github.com/ethereum-optimism/optimism/pull/7928
easier to implement.
parent c24298e7
...@@ -4,7 +4,7 @@ pragma solidity 0.8.15; ...@@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
import { Faucet } from "src/periphery/faucet/Faucet.sol"; import { Faucet } from "src/periphery/faucet/Faucet.sol";
import { FaucetHelper } from "test/Helpers.sol"; import { FaucetHelper } from "test/mocks/FaucetHelper.sol";
/// @title AdminFaucetAuthModuleTest /// @title AdminFaucetAuthModuleTest
/// @notice Tests the AdminFaucetAuthModule contract. /// @notice Tests the AdminFaucetAuthModule contract.
......
...@@ -3,8 +3,8 @@ pragma solidity 0.8.15; ...@@ -3,8 +3,8 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { TestERC20 } from "test/Helpers.sol"; import { TestERC20 } from "test/mocks/TestERC20.sol";
import { TestERC721 } from "test/Helpers.sol"; import { TestERC721 } from "test/mocks/TestERC721.sol";
import { AssetReceiver } from "src/periphery/AssetReceiver.sol"; import { AssetReceiver } from "src/periphery/AssetReceiver.sol";
contract AssetReceiver_Initializer is Test { contract AssetReceiver_Initializer is Test {
......
...@@ -5,7 +5,7 @@ import { Test } from "forge-std/Test.sol"; ...@@ -5,7 +5,7 @@ import { Test } from "forge-std/Test.sol";
import { Drippie } from "src/periphery/drippie/Drippie.sol"; import { Drippie } from "src/periphery/drippie/Drippie.sol";
import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol";
import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol"; import { CheckTrue } from "src/periphery/drippie/dripchecks/CheckTrue.sol";
import { SimpleStorage } from "test/Helpers.sol"; import { SimpleStorage } from "test/mocks/SimpleStorage.sol";
/// @title TestDrippie /// @title TestDrippie
/// @notice This is a wrapper contract around Drippie used for testing. /// @notice This is a wrapper contract around Drippie used for testing.
......
...@@ -4,7 +4,7 @@ pragma solidity 0.8.15; ...@@ -4,7 +4,7 @@ pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { Faucet } from "src/periphery/faucet/Faucet.sol"; import { Faucet } from "src/periphery/faucet/Faucet.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol"; import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
import { FaucetHelper } from "test/Helpers.sol"; import { FaucetHelper } from "test/mocks/FaucetHelper.sol";
contract Faucet_Initializer is Test { contract Faucet_Initializer is Test {
event Drip(string indexed authModule, bytes32 indexed userId, uint256 amount, address indexed recipient); event Drip(string indexed authModule, bytes32 indexed userId, uint256 amount, address indexed recipient);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
contract TestERC20 is ERC20 {
constructor() ERC20("TEST", "TST", 18) { }
function mint(address to, uint256 value) public {
_mint(to, value);
}
}
contract TestERC721 is ERC721 {
constructor() ERC721("TEST", "TST") { }
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}
function tokenURI(uint256) public pure virtual override returns (string memory) { }
}
contract CallRecorder { contract CallRecorder {
struct CallInfo { struct CallInfo {
address sender; address sender;
...@@ -51,171 +24,3 @@ contract Reverter { ...@@ -51,171 +24,3 @@ contract Reverter {
revert("Reverter reverted"); revert("Reverter reverted");
} }
} }
contract SimpleStorage {
mapping(bytes32 => bytes32) public db;
function set(bytes32 _key, bytes32 _value) public payable {
db[_key] = _value;
}
function get(bytes32 _key) public view returns (bytes32) {
return db[_key];
}
}
/// @notice Simple helper contract that helps with testing flow and signature for
/// OptimistInviter contract. Made this a separate contract instead of including
/// in OptimistInviter.t.sol for reusability.
contract OptimistInviterHelper {
/// @notice EIP712 typehash for the ClaimableInvite type.
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH = keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
/// @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @notice Address of OptimistInviter contract we are testing.
OptimistInviter public optimistInviter;
/// @notice OptimistInviter contract name. Used to construct the EIP-712 domain.
string public name;
/// @notice Keeps track of current nonce to generate new nonces for each invite.
uint256 public currentNonce;
constructor(OptimistInviter _optimistInviter, string memory _name) {
optimistInviter = _optimistInviter;
name = _name;
}
/// @notice Returns the hash of the struct ClaimableInvite.
/// @param _claimableInvite ClaimableInvite struct to hash.
/// @return EIP-712 typed struct hash.
function getClaimableInviteStructHash(OptimistInviter.ClaimableInvite memory _claimableInvite)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(CLAIMABLE_INVITE_TYPEHASH, _claimableInvite.issuer, _claimableInvite.nonce));
}
/// @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
/// pseudorandom nonces.
/// @return Nonce that should be used as part of ClaimableInvite.
function consumeNonce() public returns (bytes32) {
return bytes32(keccak256(abi.encode(currentNonce++)));
}
/// @notice Returns a ClaimableInvite with the issuer and current nonce.
/// @param _issuer Issuer to include in the ClaimableInvite.
/// @return ClaimableInvite that can be hashed & signed.
function getClaimableInviteWithNewNonce(address _issuer) public returns (OptimistInviter.ClaimableInvite memory) {
return OptimistInviter.ClaimableInvite(_issuer, consumeNonce());
}
/// @notice Computes the EIP712 digest with default correct parameters.
/// @param _claimableInvite ClaimableInvite struct to hash.
/// @return EIP-712 compatible digest.
function getDigest(OptimistInviter.ClaimableInvite calldata _claimableInvite) public view returns (bytes32) {
return getDigestWithEIP712Domain(
_claimableInvite,
bytes(name),
bytes(optimistInviter.EIP712_VERSION()),
block.chainid,
address(optimistInviter)
);
}
/// @notice Computes the EIP712 digest with the given domain parameters.
/// Used for testing that different domain parameters fail.
/// @param _claimableInvite ClaimableInvite struct to hash.
/// @param _name Contract name to use in the EIP712 domain.
/// @param _version Contract version to use in the EIP712 domain.
/// @param _chainid Chain ID to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @return EIP-712 compatible digest.
function getDigestWithEIP712Domain(
OptimistInviter.ClaimableInvite calldata _claimableInvite,
bytes memory _name,
bytes memory _version,
uint256 _chainid,
address _verifyingContract
)
public
pure
returns (bytes32)
{
bytes32 domainSeparator = keccak256(
abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(_name), keccak256(_version), _chainid, _verifyingContract)
);
return ECDSA.toTypedDataHash(domainSeparator, getClaimableInviteStructHash(_claimableInvite));
}
}
/// @notice Simple ERC1271 wallet that can be used to test the ERC1271 signature checker.
/// @notice https://github.com/OpenZeppelin/openzeppelin-contracts/
/// blob/master/contracts/mocks/ERC1271WalletMock.sol
contract TestERC1271Wallet is Ownable, IERC1271 {
constructor(address originalOwner) {
transferOwnership(originalOwner);
}
function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) {
return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0);
}
}
/// @notice Simple helper contract that helps with testing the Faucet contract.
contract FaucetHelper {
/// @notice EIP712 typehash for the Proof type.
bytes32 public constant PROOF_TYPEHASH = keccak256("Proof(address recipient,bytes32 nonce,bytes32 id)");
/// @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @notice Keeps track of current nonce to generate new nonces for each drip.
uint256 public currentNonce;
/// @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
/// pseudorandom nonces.
/// @return Nonce that should be used as part of drip parameters.
function consumeNonce() public returns (bytes32) {
return bytes32(keccak256(abi.encode(currentNonce++)));
}
/// @notice Returns the hash of the struct Proof.
/// @param _proof Proof struct to hash.
/// @return EIP-712 typed struct hash.
function getProofStructHash(AdminFaucetAuthModule.Proof memory _proof) public pure returns (bytes32) {
return keccak256(abi.encode(PROOF_TYPEHASH, _proof.recipient, _proof.nonce, _proof.id));
}
/// @notice Computes the EIP712 digest with the given domain parameters.
/// Used for testing that different domain parameters fail.
/// @param _proof Proof struct to hash.
/// @param _name Contract name to use in the EIP712 domain.
/// @param _version Contract version to use in the EIP712 domain.
/// @param _chainid Chain ID to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @return EIP-712 compatible digest.
function getDigestWithEIP712Domain(
AdminFaucetAuthModule.Proof memory _proof,
bytes memory _name,
bytes memory _version,
uint256 _chainid,
address _verifyingContract
)
public
pure
returns (bytes32)
{
bytes32 domainSeparator = keccak256(
abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(_name), keccak256(_version), _chainid, _verifyingContract)
);
return ECDSAUpgradeable.toTypedDataHash(domainSeparator, getProofStructHash(_proof));
}
}
...@@ -7,7 +7,7 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol" ...@@ -7,7 +7,7 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol"
import { Optimist } from "src/periphery/op-nft/Optimist.sol"; import { Optimist } from "src/periphery/op-nft/Optimist.sol";
import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol"; import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol";
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { OptimistInviterHelper } from "test/Helpers.sol"; import { OptimistInviterHelper } from "test/mocks/OptimistInviterHelper.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
......
...@@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol"; ...@@ -6,7 +6,7 @@ import { Test } from "forge-std/Test.sol";
import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol"; import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol";
import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol"; import { OptimistAllowlist } from "src/periphery/op-nft/OptimistAllowlist.sol";
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { OptimistInviterHelper } from "test/Helpers.sol"; import { OptimistInviterHelper } from "test/mocks/OptimistInviterHelper.sol";
import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol"; import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol";
contract OptimistAllowlist_Initializer is Test { contract OptimistAllowlist_Initializer is Test {
......
...@@ -7,8 +7,8 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol" ...@@ -7,8 +7,8 @@ import { AttestationStation } from "src/periphery/op-nft/AttestationStation.sol"
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol"; import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { Optimist } from "src/periphery/op-nft/Optimist.sol"; import { Optimist } from "src/periphery/op-nft/Optimist.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { TestERC1271Wallet } from "test/Helpers.sol"; import { TestERC1271Wallet } from "test/mocks/TestERC1271Wallet.sol";
import { OptimistInviterHelper } from "test/Helpers.sol"; import { OptimistInviterHelper } from "test/mocks/OptimistInviterHelper.sol";
import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol"; import { OptimistConstants } from "src/periphery/op-nft/libraries/OptimistConstants.sol";
contract OptimistInviter_Initializer is Test { contract OptimistInviter_Initializer is Test {
......
...@@ -3,8 +3,7 @@ pragma solidity 0.8.15; ...@@ -3,8 +3,7 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { CallRecorder } from "test/Helpers.sol"; import { CallRecorder, Reverter } from "test/mocks/Callers.sol";
import { Reverter } from "test/Helpers.sol";
import { Transactor } from "src/periphery/Transactor.sol"; import { Transactor } from "src/periphery/Transactor.sol";
contract Transactor_Initializer is Test { contract Transactor_Initializer is Test {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CallRecorder {
struct CallInfo {
address sender;
bytes data;
uint256 gas;
uint256 value;
}
CallInfo public lastCall;
function record() public payable {
lastCall.sender = msg.sender;
lastCall.data = msg.data;
lastCall.gas = gasleft();
lastCall.value = msg.value;
}
}
contract Reverter {
function doRevert() public pure {
revert("Reverter reverted");
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import { AdminFaucetAuthModule } from "src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol";
/// @notice Simple helper contract that helps with testing the Faucet contract.
contract FaucetHelper {
/// @notice EIP712 typehash for the Proof type.
bytes32 public constant PROOF_TYPEHASH = keccak256("Proof(address recipient,bytes32 nonce,bytes32 id)");
/// @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @notice Keeps track of current nonce to generate new nonces for each drip.
uint256 public currentNonce;
/// @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
/// pseudorandom nonces.
/// @return Nonce that should be used as part of drip parameters.
function consumeNonce() public returns (bytes32) {
return bytes32(keccak256(abi.encode(currentNonce++)));
}
/// @notice Returns the hash of the struct Proof.
/// @param _proof Proof struct to hash.
/// @return EIP-712 typed struct hash.
function getProofStructHash(AdminFaucetAuthModule.Proof memory _proof) public pure returns (bytes32) {
return keccak256(abi.encode(PROOF_TYPEHASH, _proof.recipient, _proof.nonce, _proof.id));
}
/// @notice Computes the EIP712 digest with the given domain parameters.
/// Used for testing that different domain parameters fail.
/// @param _proof Proof struct to hash.
/// @param _name Contract name to use in the EIP712 domain.
/// @param _version Contract version to use in the EIP712 domain.
/// @param _chainid Chain ID to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @return EIP-712 compatible digest.
function getDigestWithEIP712Domain(
AdminFaucetAuthModule.Proof memory _proof,
bytes memory _name,
bytes memory _version,
uint256 _chainid,
address _verifyingContract
)
public
pure
returns (bytes32)
{
bytes32 domainSeparator = keccak256(
abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(_name), keccak256(_version), _chainid, _verifyingContract)
);
return ECDSAUpgradeable.toTypedDataHash(domainSeparator, getProofStructHash(_proof));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { OptimistInviter } from "src/periphery/op-nft/OptimistInviter.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @notice Simple helper contract that helps with testing flow and signature for
/// OptimistInviter contract. Made this a separate contract instead of including
/// in OptimistInviter.t.sol for reusability.
contract OptimistInviterHelper {
/// @notice EIP712 typehash for the ClaimableInvite type.
bytes32 public constant CLAIMABLE_INVITE_TYPEHASH = keccak256("ClaimableInvite(address issuer,bytes32 nonce)");
/// @notice EIP712 typehash for the EIP712Domain type that is included as part of the signature.
bytes32 public constant EIP712_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @notice Address of OptimistInviter contract we are testing.
OptimistInviter public optimistInviter;
/// @notice OptimistInviter contract name. Used to construct the EIP-712 domain.
string public name;
/// @notice Keeps track of current nonce to generate new nonces for each invite.
uint256 public currentNonce;
constructor(OptimistInviter _optimistInviter, string memory _name) {
optimistInviter = _optimistInviter;
name = _name;
}
/// @notice Returns the hash of the struct ClaimableInvite.
/// @param _claimableInvite ClaimableInvite struct to hash.
/// @return EIP-712 typed struct hash.
function getClaimableInviteStructHash(OptimistInviter.ClaimableInvite memory _claimableInvite)
public
pure
returns (bytes32)
{
return keccak256(abi.encode(CLAIMABLE_INVITE_TYPEHASH, _claimableInvite.issuer, _claimableInvite.nonce));
}
/// @notice Returns a bytes32 nonce that should change everytime. In practice, people should use
/// pseudorandom nonces.
/// @return Nonce that should be used as part of ClaimableInvite.
function consumeNonce() public returns (bytes32) {
return bytes32(keccak256(abi.encode(currentNonce++)));
}
/// @notice Returns a ClaimableInvite with the issuer and current nonce.
/// @param _issuer Issuer to include in the ClaimableInvite.
/// @return ClaimableInvite that can be hashed & signed.
function getClaimableInviteWithNewNonce(address _issuer) public returns (OptimistInviter.ClaimableInvite memory) {
return OptimistInviter.ClaimableInvite(_issuer, consumeNonce());
}
/// @notice Computes the EIP712 digest with default correct parameters.
/// @param _claimableInvite ClaimableInvite struct to hash.
/// @return EIP-712 compatible digest.
function getDigest(OptimistInviter.ClaimableInvite calldata _claimableInvite) public view returns (bytes32) {
return getDigestWithEIP712Domain(
_claimableInvite,
bytes(name),
bytes(optimistInviter.EIP712_VERSION()),
block.chainid,
address(optimistInviter)
);
}
/// @notice Computes the EIP712 digest with the given domain parameters.
/// Used for testing that different domain parameters fail.
/// @param _claimableInvite ClaimableInvite struct to hash.
/// @param _name Contract name to use in the EIP712 domain.
/// @param _version Contract version to use in the EIP712 domain.
/// @param _chainid Chain ID to use in the EIP712 domain.
/// @param _verifyingContract Address to use in the EIP712 domain.
/// @return EIP-712 compatible digest.
function getDigestWithEIP712Domain(
OptimistInviter.ClaimableInvite calldata _claimableInvite,
bytes memory _name,
bytes memory _version,
uint256 _chainid,
address _verifyingContract
)
public
pure
returns (bytes32)
{
bytes32 domainSeparator = keccak256(
abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256(_name), keccak256(_version), _chainid, _verifyingContract)
);
return ECDSA.toTypedDataHash(domainSeparator, getClaimableInviteStructHash(_claimableInvite));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleStorage {
mapping(bytes32 => bytes32) public db;
function set(bytes32 _key, bytes32 _value) public payable {
db[_key] = _value;
}
function get(bytes32 _key) public view returns (bytes32) {
return db[_key];
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
/// @notice Simple ERC1271 wallet that can be used to test the ERC1271 signature checker.
/// @notice https://github.com/OpenZeppelin/openzeppelin-contracts/
/// blob/master/contracts/mocks/ERC1271WalletMock.sol
contract TestERC1271Wallet is Ownable, IERC1271 {
constructor(address originalOwner) {
transferOwnership(originalOwner);
}
function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) {
return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
contract TestERC20 is ERC20 {
constructor() ERC20("TEST", "TST", 18) { }
function mint(address to, uint256 value) public {
_mint(to, value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
contract TestERC721 is ERC721 {
constructor() ERC721("TEST", "TST") { }
function mint(address to, uint256 tokenId) public {
_mint(to, tokenId);
}
function tokenURI(uint256) public pure virtual override returns (string memory) { }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment