If you don't know what ecrecover
is(like me before starting this fix), you should read this:
https://medium.com/mycrypto/the-magic-of-digital-signatures-on-ethereum-98fe184dc9c7
EIP-712 doesn't provide replay protection, as there is no nonce inbuilt into the EIP-712. This guide explains how to implement nonces: https://medium.com/immunefi/intro-to-cryptography-and-signatures-in-ethereum-2025b6a4a33d
This change was mostly guided from this EIP-712 step by step guide: https://gist.github.com/markodayan/e05f524b915f129c4f8500df816a369b
We need to secure these four things:
โโโโโโ struct AddDeployer {
โโโโโโ address deployer;
โโโโโโ uint256 nonce;
โโโโโโ }
โโโโโโ
{
name: "FunWallet.Create3Deployer",
version: "1",
chainId: block.chainId,
verifyingContract: address.this,
salt: "0x43efba6b4cc18b6faa2625fe562bdd9a23260359"
}
Offchain stuff, do this later
https://gist.github.com/markodayan/e05f524b915f129c4f8500df816a369b
The purpose of the below code is: given a list of v,r,s signatures from some offchain source, determine whether the v,r,s signatures are valid deployers who signed the specific messages to call addDeployer()
, removeDeployer()
, setThreshold()
, or callChild()
This is to adhere to the Eip712 standard
const name = "FunWallet.Create3Deployer"
const version = "1"
const salt = 0x43efba6b4cc18b6faa2625fe562bdd9a23260359;
string private constant EIP712_DOMAIN = "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)";
bytes32 private constant DOMAIN_SEPARATOR = keccak256(abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(name),
keccak256(version),
block.chainid,
address(this),
salt
));
TYPEHASH:
string private constant ADD_DEPLOYER_TYPEHASH = "AddDeployer(address deployer, uint256 nonce)"
Hash function:
function hashAddDeployer(AddDeployer addDeployer) private pure returns(bytes32){
return keccak256(abi.encode(
ADD_DEPLOYER_TYPEHASH,
addDeployer.deployer,
addDeployer.nonce
));
}
Verify Function:
function numValidAddDeployerHashes(AddDeployer addDeployer, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) public view returns(uint256 numValidHashes){
for(uint256 i; i < v.length; i++){
address signer = ecrecover(hashAddDeployer(addDeployer), v[i], r[i], s[i]);
numValidHashes += validDeployer[signer] ? 1:0;
}
}
TYPEHASH:
string private constant REMOVE_DEPLOYER_TYPEHASH = "RemoveDeployer(address deployer, uint256 nonce)"
Hash function:
function hashRemoveDeployer(RemoveDeployer removeDeployer) private pure returns(bytes32){
return keccak256(abi.encode(
REMOVE_DEPLOYER_TYPEHASH,
removeDeployer.deployer,
removeDeployer.nonce
));
}
Verify Function:
function numValidRemoveDeployerHashes(RemoveDeployer removeDeployer, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) public view returns(uint256 numValidHashes){
for(uint256 i; i < v.length; i++){
address signer = ecrecover(hashRemoveDeployer(removeDeployer), v[i], r[i], s[i]);
numValidHashes += validDeployer[signer] ? 1:0;
}
}
TYPEHASH:
string private constant SET_THRESHOLD_TYPEHASH = "SetThreshold(uint256 threshold, uint256 nonce)"
Hash function:
function hashSetThreshold(SetThreshold setThreshold) private pure returns(bytes32){
return keccak256(abi.encode(
SET_THRESHOLD_TYPEHASH,
setThreshold.threshold,
setThreshold.nonce
));
}
Verify Function:
function numValidSetThresholdHashes(SetThreshold setThreshold, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) public view returns(uint256 numValidHashes){
for(uint256 i; i < v.length; i++){
address signer = ecrecover(hashSetThreshold(setThreshold), v[i], r[i], s[i]);
numValidHashes += validDeployer[signer] ? 1:0;
}
}
TYPEHASH:
string private constant CALL_CHILD_TYPEHASH = "CallChild(address child, bytes data, uint256 nonce)"
Hash function:
function hashCallChild(CallChild callChild) private pure returns(bytes32){
return keccak256(abi.encode(
CALL_CHILD_TYPEHASH,
callChild.child,
callChild.data,
callChild.nonce
));
}
Verify Function:
function numValidCallChildHashes(CallChild callChild, uint8[] memory v, bytes32[] memory r, bytes32[] memory s) public view returns(uint256 numValidHashes){
for(uint256 i; i < v.length; i++){
address signer = ecrecover(hashCallChild(callChild), v[i], r[i], s[i]);
numValidHashes += validDeployer[signer] ? 1:0;
}
}