-
Notifications
You must be signed in to change notification settings - Fork 357
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: melancholicxxx <[email protected]> Co-authored-by: 0xlgtm <[email protected]>
- Loading branch information
1 parent
e1cb33a
commit 2d85958
Showing
85 changed files
with
22,538 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
# ERC1271 | ||
|
||
ERC1271 mixin with nested EIP-712 approach. | ||
|
||
|
||
|
||
|
||
<b>Inherits:</b> | ||
|
||
- `utils/EIP712.sol` | ||
|
||
|
||
<!-- customintro:start --><!-- customintro:end --> | ||
|
||
## Constants | ||
|
||
### _PERSONAL_SIGN_TYPEHASH | ||
|
||
```solidity | ||
bytes32 internal constant _PERSONAL_SIGN_TYPEHASH = | ||
0x983e65e5148e570cd828ead231ee759a8d7958721a768f93bc4483ba005c32de | ||
``` | ||
|
||
`keccak256("PersonalSign(bytes prefixed)")`. | ||
|
||
## ERC1271 Operations | ||
|
||
### isValidSignature(bytes32,bytes) | ||
|
||
```solidity | ||
function isValidSignature(bytes32 hash, bytes calldata signature) | ||
public | ||
view | ||
virtual | ||
returns (bytes4 result) | ||
``` | ||
|
||
Validates the signature with ERC1271 return, | ||
so that this account can also be used as a signer. | ||
|
||
### _erc1271IsValidSignatureNowCalldata(bytes32,bytes) | ||
|
||
```solidity | ||
function _erc1271IsValidSignatureNowCalldata( | ||
bytes32 hash, | ||
bytes calldata signature | ||
) internal view virtual returns (bool) | ||
``` | ||
|
||
Returns whether the `hash` and `signature` are valid. | ||
Override if you need non-ECDSA logic. | ||
|
||
### _erc1271UnwrapSignature(bytes) | ||
|
||
```solidity | ||
function _erc1271UnwrapSignature(bytes calldata signature) | ||
internal | ||
view | ||
virtual | ||
returns (bytes calldata result) | ||
``` | ||
|
||
Unwraps and returns the signature. | ||
|
||
### _erc1271IsValidSignature(bytes32,bytes) | ||
|
||
```solidity | ||
function _erc1271IsValidSignature(bytes32 hash, bytes calldata signature) | ||
internal | ||
view | ||
virtual | ||
returns (bool) | ||
``` | ||
|
||
Returns whether the `signature` is valid for the `hash. | ||
|
||
### _erc1271IsValidSignatureViaSafeCaller(bytes32,bytes) | ||
|
||
```solidity | ||
function _erc1271IsValidSignatureViaSafeCaller( | ||
bytes32 hash, | ||
bytes calldata signature | ||
) internal view virtual returns (bool result) | ||
``` | ||
|
||
Performs the signature validation without nested EIP-712 if the caller is | ||
a safe caller. A safe caller must include the address of this account in the hash. | ||
|
||
### _erc1271IsValidSignatureViaNestedEIP712(bytes32,bytes) | ||
|
||
```solidity | ||
function _erc1271IsValidSignatureViaNestedEIP712( | ||
bytes32 hash, | ||
bytes calldata signature | ||
) internal view virtual returns (bool result) | ||
``` | ||
|
||
ERC1271 signature validation (Nested EIP-712 workflow). | ||
This uses ECDSA recovery by default (see: `_erc1271IsValidSignatureNowCalldata`). | ||
It also uses a nested EIP-712 approach to prevent signature replays when a single EOA | ||
owns multiple smart contract accounts, | ||
while still enabling wallet UIs (e.g. Metamask) to show the EIP-712 values. | ||
Crafted for phishing resistance, efficiency, flexibility. | ||
__________________________________________________________________________________________ | ||
Glossary: | ||
- `APP_DOMAIN_SEPARATOR`: The domain separator of the `hash` passed in by the application. | ||
Provided by the front end. Intended to be the domain separator of the contract | ||
that will call `isValidSignature` on this account. | ||
- `ACCOUNT_DOMAIN_SEPARATOR`: The domain separator of this account. | ||
See: `EIP712._domainSeparator()`. | ||
__________________________________________________________________________________________ | ||
For the `TypedDataSign` workflow, the final hash will be: | ||
```solidity | ||
keccak256(\x19\x01 β APP_DOMAIN_SEPARATOR β | ||
hashStruct(TypedDataSign({ | ||
contents: hashStruct(originalStruct), | ||
name: keccak256(bytes(eip712Domain().name)), | ||
version: keccak256(bytes(eip712Domain().version)), | ||
chainId: eip712Domain().chainId, | ||
verifyingContract: eip712Domain().verifyingContract, | ||
salt: eip712Domain().salt | ||
})) | ||
) | ||
``` | ||
where `β` denotes the concatenation operator for bytes. | ||
The order of the fields is important: `contents` comes before `name`. | ||
The signature will be `r β s β v β APP_DOMAIN_SEPARATOR β | ||
contents β contentsDescription β uint16(contentsDescription.length)`, | ||
where: | ||
- `contents` is the bytes32 struct hash of the original struct. | ||
- `contentsDescription` can be either: | ||
a) `contentsType` (implicit mode) | ||
where `contentsType` starts with `contentsName`. | ||
b) `contentsType β contentsName` (explicit mode) | ||
where `contentsType` may not necessarily start with `contentsName`. | ||
The `APP_DOMAIN_SEPARATOR` and `contents` will be used to verify if `hash` is indeed correct. | ||
__________________________________________________________________________________________ | ||
For the `PersonalSign` workflow, the final hash will be: | ||
```solidity | ||
keccak256(\x19\x01 β ACCOUNT_DOMAIN_SEPARATOR β | ||
hashStruct(PersonalSign({ | ||
prefixed: keccak256(bytes(\x19Ethereum Signed Message:\n β | ||
base10(bytes(someString).length) β someString)) | ||
})) | ||
) | ||
``` | ||
where `β` denotes the concatenation operator for bytes. | ||
The `PersonalSign` type hash will be `keccak256("PersonalSign(bytes prefixed)")`. | ||
The signature will be `r β s β v`. | ||
__________________________________________________________________________________________ | ||
For demo and typescript code, see: | ||
- https://github.com/junomonster/nested-eip-712 | ||
- https://github.com/frangio/eip712-wrapper-for-eip1271 | ||
Their nomenclature may differ from ours, although the high-level idea is similar. | ||
Of course, if you have control over the codebase of the wallet client(s) too, | ||
you can choose a more minimalistic signature scheme like | ||
`keccak256(abi.encode(address(this), hash))` instead of all these acrobatics. | ||
All these are just for widespread out-of-the-box compatibility with other wallet clients. | ||
We want to create bazaars, not walled castles. | ||
And we'll use push the Turing Completeness of the EVM to the limits to do so. | ||
|
||
### _erc1271IsValidSignatureViaRPC(bytes32,bytes) | ||
|
||
```solidity | ||
function _erc1271IsValidSignatureViaRPC( | ||
bytes32 hash, | ||
bytes calldata signature | ||
) internal view virtual returns (bool result) | ||
``` | ||
|
||
Performs the signature validation without nested EIP-712 to allow for easy sign ins. | ||
This function must always return false or revert if called on-chain. |
Oops, something went wrong.