Skip to content

Commit

Permalink
Merge pull request #5 from garyghayrat/update-erc-6538-license
Browse files Browse the repository at this point in the history
Update erc 6538 license and more.
  • Loading branch information
garyghayrat authored Mar 5, 2024
2 parents 514378b + 5e207f9 commit 9c2ec7d
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 11 deletions.
34 changes: 23 additions & 11 deletions ERCS/erc-6538.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
---
eip: 6538
title: Stealth Meta-Address Registry
description: A registry to map addresses to stealth meta-addresses
description: A canonical contract for entities to register stealth meta-addresses directly or through a third party using EIP-712 or EIP-1271 signatures.

Check failure on line 4 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

unable to read file `erc-712.md`: Io: JsValue(Error: ENOENT: no such file or directory, open 'ERCS/erc-712.md' Error: ENOENT: no such file or directory, open 'ERCS/erc-712.md')

error[preamble-refs-description]: unable to read file `erc-712.md`: Io: JsValue(Error: ENOENT: no such file or directory, open 'ERCS/erc-712.md' Error: ENOENT: no such file or directory, open 'ERCS/erc-712.md') --> ERCS/erc-6538.md:4:123 | 4 | description: A canonical contract for entities to register stealth meta-addresses directly or through a third party using EIP-712 or EIP-1271 signatures. | ^^^^^^^ referenced here | = help: see https://ethereum.github.io/eipw/preamble-refs-description/

Check failure on line 4 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

references to proposals with a `category` of `ERC` must use a prefix of `ERC`

error[preamble-refs-description]: references to proposals with a `category` of `ERC` must use a prefix of `ERC` --> ERCS/erc-6538.md:4:134 | 4 | description: A canonical contract for entities to register stealth meta-addresses directly or through a third party using EIP-712 or EIP-1271 signatures. | ^^^^^^^^ referenced here |

Check failure on line 4 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

proposals mentioned in preamble header `description` must appear in `requires`

error[preamble-requires-ref-description]: proposals mentioned in preamble header `description` must appear in `requires` --> ERCS/erc-6538.md:4:123 | 4 | description: A canonical contract for entities to register stealth meta-addresses directly or through a third party using EIP-712 or EIP-1271 signatures. | ^^^^^^^ mentioned here | ^^^^^^^^ mentioned here | = help: see https://ethereum.github.io/eipw/preamble-requires-ref-description/
author: Matt Solomon (@mds1), Toni Wahrstätter (@nerolation), Ben DiFrancesco (@apbendi), Vitalik Buterin (@vbuterin), Gary Ghayrat (@garyghayrat)
discussions-to: https://ethereum-magicians.org/t/stealth-meta-address-registry/12888
status: Review
type: Standards Track
category: ERC
created: 2023-01-24
requires: 5564
---

## Abstract

This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md).
This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md). An entity may register their stealth meta-address directly. A third party can also register on behalf of an entity using a valid EIP-712 or EIP-1271 signature. Once registered, the stealth meta-address for the entity can be retrieved by any smart contract or user. One can use the stealth-meta address with `generateStealthAddress` specified in [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) to send assets to the generated stealth address without revealing the entity's address.

Check failure on line 16 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ERCS/erc-6538.md | 16 | This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md). An entity may register their stealth meta-address directly. A third party can also register on behalf of an entity using a valid EIP-712 or EIP-1271 signature. Once registered, the stealth meta-address for the entity can be retrieved by any smart contract or user. One can use the stealth-meta address with `generateStealthAddress` specified in [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) to send assets to the generated stealth address without revealing the entity's address. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+` = help: see https://ethereum.github.io/eipw/markdown-link-first/

Check failure on line 16 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

the first match of the given pattern must be a link

error[markdown-link-first]: the first match of the given pattern must be a link --> ERCS/erc-6538.md | 16 | This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md). An entity may register their stealth meta-address directly. A third party can also register on behalf of an entity using a valid EIP-712 or EIP-1271 signature. Once registered, the stealth meta-address for the entity can be retrieved by any smart contract or user. One can use the stealth-meta address with `generateStealthAddress` specified in [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) to send assets to the generated stealth address without revealing the entity's address. | = info: the pattern in question: `(?i)(?:eip|erc)-[0-9]+`

Check failure on line 16 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-6538.md | 16 | This specification defines a standardized way of storing and retrieving an entity's stealth meta-address, by extending [ERC-5564](./eip-5564.md). An entity may register their stealth meta-address directly. A third party can also register on behalf of an entity using a valid EIP-712 or EIP-1271 signature. Once registered, the stealth meta-address for the entity can be retrieved by any smart contract or user. One can use the stealth-meta address with `generateStealthAddress` specified in [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) to send assets to the generated stealth address without revealing the entity's address. | = help: see https://ethereum.github.io/eipw/markdown-rel-links/

## Motivation

Expand All @@ -24,10 +25,10 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL

This contract defines an `ERC6538Registry` that stores the stealth meta-address for entities. These entities may be identified by an address, ENS name, or other identifier. This MUST be a singleton contract, with one instance per chain.

The contract is specified below. A one byte integer is used to identify the stealth address scheme. This integer is used to differentiate between different stealth address schemes. A mapping from the scheme ID to it's specification is maintained at [this](../assets/eip-5564/scheme_ids.md) location.
The contract is specified below. A one byte integer is used to identify the stealth address scheme. This integer is used to differentiate between different stealth address schemes. This ERC outlines schemeId `1` as the SECP256k1 curve cryptographic scheme with view tags, as specified in [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564).

Check failure on line 28 in ERCS/erc-6538.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-6538.md | 28 | The contract is specified below. A one byte integer is used to identify the stealth address scheme. This integer is used to differentiate between different stealth address schemes. This ERC outlines schemeId `1` as the SECP256k1 curve cryptographic scheme with view tags, as specified in [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564). |

```solidity
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.23;
/// @notice `ERC6538Registry` contract to map accounts to their stealth meta-address. See
Expand Down Expand Up @@ -71,6 +72,11 @@ contract ERC6538Registry {
address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress
);
/// @notice Emitted when a registrant increments their nonce.
/// @param registrant The account that incremented the nonce.
/// @param newNonce The new nonce value.
event NonceIncremented(address indexed registrant, uint256 newNonce);
constructor() {
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
Expand Down Expand Up @@ -147,6 +153,7 @@ contract ERC6538Registry {
unchecked {
nonceOf[msg.sender]++;
}
emit NonceIncremented(msg.sender, nonceOf[msg.sender]);
}
/// @notice Returns the domain separator used in this contract.
Expand Down Expand Up @@ -188,7 +195,7 @@ interface IERC1271 {
The interface for this contract is defined below:

```solidity
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.23;
/// @dev Interface for calling the `ERC6538Registry` contract to map accounts to their stealth
Expand All @@ -199,7 +206,7 @@ interface IERC6538Registry {
/// @dev Emitted when a registrant updates their stealth meta-address.
/// @param registrant The account that registered the stealth meta-address.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 0 for
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address.
/// [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) bases the format for stealth
Expand All @@ -212,15 +219,20 @@ interface IERC6538Registry {
address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress
);
/// @notice Emitted when a registrant increments their nonce.
/// @param registrant The account that incremented the nonce.
/// @param newNonce The new nonce value.
event NonceIncremented(address indexed registrant, uint256 newNonce);
/// @notice Sets the caller's stealth meta-address for the given scheme ID.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 0 for
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address to register.
function registerKeys(uint256 schemeId, bytes calldata stealthMetaAddress) external;
/// @notice Sets the `registrant`'s stealth meta-address for the given scheme ID.
/// @param registrant Address of the registrant.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 0 for
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param signature A signature from the `registrant` authorizing the registration.
/// @param stealthMetaAddress The stealth meta-address to register.
Expand Down Expand Up @@ -256,7 +268,7 @@ interface IERC6538Registry {

### Deployment Method

This contract is going to be deployed using CREATE2 via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of TODO.
The `ERC6538Registry` contract is deployed at `0x65385Cebb86e6742F29868BDdE0D1980060a6538` using `CREATE2` via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of `0x0883790c70e6bce521db2517ae9873b2627580f5945fb2026117ab9d15ba9387`.

## Rationale

Expand All @@ -276,11 +288,11 @@ This EIP is fully backward compatible.

## Reference Implementation

You can find an implementation of this standard above.
You can find an implementation of the `ERC6538Registry` contract [here](../assets/erc-6538/contracts/ERC6538Registry.sol) and the interface `IERC6538Registry.sol` [here](../assets/erc-6538/contracts/interfaces/IERC6538Registry.sol).

## Security Considerations

In the event of a compromised private key, the registrant should promptly un-register from the stealth key registry to prevent loss of future funds sent to the compromised account. To do this without getting the ETH needed drained by bots, the registrant should use a private mempool service such as Flashbots or create the signature offline and submit it to the registry contract through a relayer.
In the event of a compromised private key, the registrant should promptly un-register from the stealth key registry to prevent loss of future funds sent to the compromised account.

## Copyright

Expand Down
161 changes: 161 additions & 0 deletions assets/erc-6538/contracts/ERC6538Registry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.23;

/// @notice `ERC6538Registry` contract to map accounts to their stealth meta-address. See
/// [ERC-6538](https://eips.ethereum.org/EIPS/eip-6538) to learn more.
contract ERC6538Registry {
/// @notice Emitted when an invalid signature is provided to `registerKeysOnBehalf`.
error ERC6538Registry__InvalidSignature();

/// @notice Next nonce expected from `user` to use when signing for `registerKeysOnBehalf`.
/// @dev `registrant` may be a standard 160-bit address or any other identifier.
/// @dev `schemeId` is an integer identifier for the stealth address scheme.
mapping(address registrant => mapping(uint256 schemeId => bytes)) public stealthMetaAddressOf;

/// @notice A nonce used to ensure a signature can only be used once.
/// @dev `registrant` is the user address.
/// @dev `nonce` will be incremented after each valid `registerKeysOnBehalf` call.
mapping(address registrant => uint256) public nonceOf;

/// @notice The EIP-712 type hash used in `registerKeysOnBehalf`.
bytes32 public constant ERC6538REGISTRY_ENTRY_TYPE_HASH =
keccak256("Erc6538RegistryEntry(uint256 schemeId,bytes stealthMetaAddress,uint256 nonce)");

/// @notice The chain ID where this contract is initially deployed.
uint256 internal immutable INITIAL_CHAIN_ID;

/// @notice The domain separator used in this contract.
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

/// @notice Emitted when a registrant updates their stealth meta-address.
/// @param registrant The account that registered the stealth meta-address.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address.
/// [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) bases the format for stealth
/// meta-addresses on [ERC-3770](https://eips.ethereum.org/EIPS/eip-3770) and specifies them as:
/// st:<shortName>:0x<spendingPubKey>:<viewingPubKey>
/// The chain (`shortName`) is implicit based on the chain the `ERC6538Registry` is deployed on,
/// therefore this `stealthMetaAddress` is just the compressed `spendingPubKey` and
/// `viewingPubKey` concatenated.
event StealthMetaAddressSet(
address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress
);

/// @notice Emitted when a registrant increments their nonce.
/// @param registrant The account that incremented the nonce.
/// @param newNonce The new nonce value.
event NonceIncremented(address indexed registrant, uint256 newNonce);

constructor() {
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}

/// @notice Sets the caller's stealth meta-address for the given scheme ID.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address to register.
function registerKeys(uint256 schemeId, bytes calldata stealthMetaAddress) external {
stealthMetaAddressOf[msg.sender][schemeId] = stealthMetaAddress;
emit StealthMetaAddressSet(msg.sender, schemeId, stealthMetaAddress);
}

/// @notice Sets the `registrant`'s stealth meta-address for the given scheme ID.
/// @param registrant Address of the registrant.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param signature A signature from the `registrant` authorizing the registration.
/// @param stealthMetaAddress The stealth meta-address to register.
/// @dev Supports both EOA signatures and EIP-1271 signatures.
/// @dev Reverts if the signature is invalid.
function registerKeysOnBehalf(
address registrant,
uint256 schemeId,
bytes memory signature,
bytes calldata stealthMetaAddress
) external {
bytes32 dataHash;
address recoveredAddress;

unchecked {
dataHash = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
ERC6538REGISTRY_ENTRY_TYPE_HASH, schemeId, stealthMetaAddress, nonceOf[registrant]++
)
)
)
);
}

if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
assembly ("memory-safe") {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
recoveredAddress = ecrecover(dataHash, v, r, s);
}

if (
(
(recoveredAddress == address(0) || recoveredAddress != registrant)
&& (
IERC1271(registrant).isValidSignature(dataHash, signature)
!= IERC1271.isValidSignature.selector
)
)
) revert ERC6538Registry__InvalidSignature();

stealthMetaAddressOf[registrant][schemeId] = stealthMetaAddress;
emit StealthMetaAddressSet(registrant, schemeId, stealthMetaAddress);
}

/// @notice Increments the nonce of the sender to invalidate existing signatures.
function incrementNonce() external {
unchecked {
nonceOf[msg.sender]++;
}
emit NonceIncremented(msg.sender, nonceOf[msg.sender]);
}

/// @notice Returns the domain separator used in this contract.
/// @dev The domain separator is re-computed if there's a chain fork.
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : _computeDomainSeparator();
}

/// @notice Computes the domain separator for this contract.
function _computeDomainSeparator() internal view returns (bytes32) {
return keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256("ERC6538Registry"),
keccak256("1.0"),
block.chainid,
address(this)
)
);
}
}

/// @notice Interface of the ERC1271 standard signature validation method for contracts as defined
/// in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
interface IERC1271 {
/// @notice Should return whether the signature provided is valid for the provided data
/// @param hash Hash of the data to be signed
/// @param signature Signature byte array associated with _data
function isValidSignature(bytes32 hash, bytes memory signature)
external
view
returns (bytes4 magicValue);
}
68 changes: 68 additions & 0 deletions assets/erc-6538/contracts/interfaces/IERC6538Registry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: CC0-1.0
pragma solidity 0.8.23;

/// @dev Interface for calling the `ERC6538Registry` contract to map accounts to their stealth
/// meta-address. See [ERC-6538](https://eips.ethereum.org/EIPS/eip-6538) to learn more.
interface IERC6538Registry {
/// @notice Emitted when an invalid signature is provided to `registerKeysOnBehalf`.
error ERC6538Registry__InvalidSignature();

/// @dev Emitted when a registrant updates their stealth meta-address.
/// @param registrant The account that registered the stealth meta-address.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address.
/// [ERC-5564](https://eips.ethereum.org/EIPS/eip-5564) bases the format for stealth
/// meta-addresses on [ERC-3770](https://eips.ethereum.org/EIPS/eip-3770) and specifies them as:
/// st:<shortName>:0x<spendingPubKey>:<viewingPubKey>
/// The chain (`shortName`) is implicit based on the chain the `ERC6538Registry` is deployed on,
/// therefore this `stealthMetaAddress` is just the `spendingPubKey` and `viewingPubKey`
/// concatenated.
event StealthMetaAddressSet(
address indexed registrant, uint256 indexed schemeId, bytes stealthMetaAddress
);

/// @notice Emitted when a registrant increments their nonce.
/// @param registrant The account that incremented the nonce.
/// @param newNonce The new nonce value.
event NonceIncremented(address indexed registrant, uint256 newNonce);

/// @notice Sets the caller's stealth meta-address for the given scheme ID.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param stealthMetaAddress The stealth meta-address to register.
function registerKeys(uint256 schemeId, bytes calldata stealthMetaAddress) external;

/// @notice Sets the `registrant`'s stealth meta-address for the given scheme ID.
/// @param registrant Address of the registrant.
/// @param schemeId Identifier corresponding to the applied stealth address scheme, e.g. 1 for
/// secp256k1, as specified in ERC-5564.
/// @param signature A signature from the `registrant` authorizing the registration.
/// @param stealthMetaAddress The stealth meta-address to register.
/// @dev Supports both EOA signatures and EIP-1271 signatures.
/// @dev Reverts if the signature is invalid.
function registerKeysOnBehalf(
address registrant,
uint256 schemeId,
bytes memory signature,
bytes calldata stealthMetaAddress
) external;

/// @notice Increments the nonce of the sender to invalidate existing signatures.
function incrementNonce() external;

/// @notice Returns the domain separator used in this contract.
function DOMAIN_SEPARATOR() external view returns (bytes32);

/// @notice Returns the stealth meta-address for the given `registrant` and `schemeId`.
function stealthMetaAddressOf(address registrant, uint256 schemeId)
external
view
returns (bytes memory);

/// @notice Returns the EIP-712 type hash used in `registerKeysOnBehalf`.
function ERC6538REGISTRY_ENTRY_TYPE_HASH() external view returns (bytes32);

/// @notice Returns the nonce of the given `registrant`.
function nonceOf(address registrant) external view returns (uint256);
}

0 comments on commit 9c2ec7d

Please sign in to comment.