## Motivation
1. We should never roll our own crypto. This means we should not come up with our own signature schemes, validation schemes, etc. Previously, we were using our own signature scheme in
2. EIP-712 is the standard for readable signatures in the EVM ecosystem: https://eips.ethereum.org/EIPS/eip-712 This allows users who are signing stuff in Create3 to use their EOAs and to read what they are signing instead of seeing a bunch of meaningless bytes
## Background
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
### 1. Design your Data structures
We need to secure these four things:
- addDeployer
- ```
struct AddDeployer {
address deployer;
uint256 nonce;
}
### 2. Design your domain separator
```
{
name: "FunWallet.Create3Deployer",
version: "1",
chainId: block.chainId,
verifyingContract: address.this,
salt: "0x43efba6b4cc18b6faa2625fe562bdd9a23260359"
}
```
### 3. Write Signing code for your dApp
Offchain stuff, do this later
https://gist.github.com/markodayan/e05f524b915f129c4f8500df816a369b
## 4. Write Authentication code for verifying contract
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()`
### Domain Separator
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
));
```
### addDeployer
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;
}
}
```
### removeDeployer
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;
}
}
```
### setThreshold
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;
}
}
```
### callChild
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;
}
}
```
## Resources
- https://coinsbench.com/understanding-digital-signatures-the-role-of-v-r-s-in-cryptographic-security-and-signature-b9d2b89bbc0c
- https://ethereum.stackexchange.com/questions/45461/verify-ecdsa-signature-in-solidity
- https://gist.github.com/markodayan/e05f524b915f129c4f8500df816a369b