Skip to content

Commit

Permalink
Merge branch 'master' into AA-428-extract-rpc-remove
Browse files Browse the repository at this point in the history
  • Loading branch information
forshtat authored Jan 30, 2025
2 parents 5d2f3bf + 04ce6a2 commit 19ad032
Show file tree
Hide file tree
Showing 23 changed files with 1,496 additions and 80 deletions.
103 changes: 72 additions & 31 deletions ERCS/erc-6120.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
eip: 6120
title: Universal Token Router
description: A single router contract enables tokens to be sent to application contracts in the transfer-and-call pattern instead of approve-then-call.
author: Derivable (@derivable-labs), Zergity (@Zergity), Ngo Quang Anh (@anhnq82), BerlinP (@BerlinP), Khanh Pham (@blackskin18), Hal Blackburn (@h4l)
author: Derion (@derion-io), Zergity (@Zergity), Ngo Quang Anh (@anhnq82), BerlinP (@BerlinP), Khanh Pham (@blackskin18), Hal Blackburn (@h4l)
discussions-to: https://ethereum-magicians.org/t/eip-6120-universal-token-router/12142
status: Review
type: Standards Track
Expand All @@ -21,7 +21,7 @@ The Universal Token Router (UTR) separates the token allowance from the applicat

Tokens approved to the Universal Token Router can only be spent in transactions directly signed by their owner, and they have clearly visible token transfer behavior, including token types (ETH, [ERC-20](./eip-20.md), [ERC-721](./eip-721.md) or [ERC-1155](./eip-1155.md)), `amountIn`, `amountOutMin`, and `recipient`.

The Universal Token Router contract is deployed using the [EIP-1014](./eip-1014.md) SingletonFactory contract at `0x8Bd6072372189A12A2889a56b6ec982fD02b0B87` across all EVM-compatible networks. This enables new token contracts to pre-configure it as a trusted spender, eliminating the need for approval transactions during their interactive usage.
The Universal Token Router contract is deployed using the [EIP-1014](./eip-1014.md) SingletonFactory contract at `0x69c4620b62D99f524c5B4dE45442FE2D7dD59576` across all EVM-compatible networks. This enables new token contracts to pre-configure it as a trusted spender, eliminating the need for approval transactions during their interactive usage.

## Motivation

Expand Down Expand Up @@ -93,15 +93,19 @@ struct Action {
}
```

The action code contract MUST implement the [ERC-165](./eip-165.md) interface with the ID `0x61206120` in order to be called by the UTR. This interface check prevents direct invocation of token *allowance-spending* functions (e.g., `transferFrom`) by the UTR. Therefore, new token contracts MUST NOT implement this interface ID.
The action code contract MUST implement the `NotToken` contract or the [ERC-165](./eip-165.md) interface with the ID `0x61206120` in order to be called by the UTR. This interface check prevents the direct invocation of token *allowance-spending* functions (e.g., `transferFrom`) by the UTR. Therefore, new token contracts MUST NOT implement this interface ID.

```solidity
abstract contract NotToken is ERC165 {
// IERC165-supportsInterface
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return
interfaceId == 0x61206120 ||
super.supportsInterface(interfaceId);
/**
* This contract will conflict with the ERC20, ERC721, and ERC1155 standards,
* preventing token contracts from accidentally implementing it.
*/
abstract contract NotToken {
function allowance(address, address) external pure returns (string memory) {
return "THIS IS NOT A TOKEN";
}
function isApprovedForAll(address, address) external pure returns (string memory) {
return "THIS IS NOT A TOKEN";
}
}
Expand Down Expand Up @@ -196,6 +200,39 @@ interface IUniversalTokenRouter {

Please refer to the [Discard Payment](#discard-payment-1) section in the **Security Considerations** for an important security note.

##### Sender Authentication

Discarding payment also makes sender authentication possible with a router, which is never achievable with regular routers. By inputting a pseudo payment (not a token payment), the UTR allows the target contract to verify the sender's address for authentication, along with normal token transfers and payments.

```solidity
contract AuthChecker is NotToken {
// must be trusted with a proper implementation of discard function
address immutable UTR;
function actionMustSentBySender(address sender) external {
bytes memory payment = abi.encode(sender, address(this), 0, address(0), 0);
IUniversalTokenRouter(UTR).discard(payment, 1);
}
}
```

```javascript
await utr.exec([], [{
inputs: [{
mode: PAYMENT,
eip: 0,
token: AddressZero,
id: 0,
amountIn: 1,
recipient: paymentTest.address,
}],
code: authChecker.address,
data: (await authChecker.populateTransaction.actionMustSentBySender(owner.address)).data,
}])
```

Please refer to the [Discard Payment](#discard-payment-1) section in the **Security Considerations** for an important security note.

##### Payment Lifetime

Payments are recorded in the UTR storage and intended to be spent by `input.action` external calls only within that transaction. All payment storages will be cleared before the `UTR.exec` ends.
Expand Down Expand Up @@ -469,11 +506,11 @@ Additional helper and adapter contracts might be needed, but they're mostly peri

## Reference Implementation

A reference implementation by Derivable Labs and audited by Hacken.
A reference implementation by Derion Labs and audited by Hacken.

```solidity
/// @title The implemetation of the EIP-6120.
/// @author Derivable Labs
/// @title The implementation of the EIP-6120.
/// @author Derion Labs
contract UniversalTokenRouter is ERC165, IUniversalTokenRouter {
uint256 constant PAYMENT = 0;
uint256 constant TRANSFER = 1;
Expand All @@ -483,14 +520,8 @@ contract UniversalTokenRouter is ERC165, IUniversalTokenRouter {
uint256 constant ERC_721_BALANCE = uint256(keccak256('UniversalTokenRouter.ERC_721_BALANCE'));
/// @dev transient pending payments
mapping(bytes32 => uint256) t_payments;
/// @dev accepting ETH for user execution (e.g. WETH.withdraw)
receive() external payable {}
/// The main entry point of the router
/// @param outputs token behaviour for output verification
/// @param outputs token behavior for output verification
/// @param actions router actions and inputs for execution
function exec(
Output[] memory outputs,
Expand All @@ -506,8 +537,6 @@ contract UniversalTokenRouter is ERC165, IUniversalTokenRouter {
output.amountOutMin = expected;
}
address sender = msg.sender;
for (uint256 i = 0; i < actions.length; ++i) {
Action memory action = actions[i];
uint256 value;
Expand All @@ -519,17 +548,23 @@ contract UniversalTokenRouter is ERC165, IUniversalTokenRouter {
value = input.amountIn;
} else {
if (mode == PAYMENT) {
bytes32 key = keccak256(abi.encode(sender, input.recipient, input.eip, input.token, input.id));
t_payments[key] = input.amountIn;
bytes32 key = keccak256(abi.encode(
msg.sender, input.recipient, input.eip, input.token, input.id
));
uint amountIn = input.amountIn;
assembly {
tstore(key, amountIn)
}
} else if (mode == TRANSFER) {
_transferToken(sender, input.recipient, input.eip, input.token, input.id, input.amountIn);
_transferToken(msg.sender, input.recipient, input.eip, input.token, input.id, input.amountIn);
} else {
revert('UTR: INVALID_MODE');
}
}
}
if (action.code != address(0) || action.data.length > 0 || value > 0) {
require(
TokenChecker.isNotToken(action.code) ||
ERC165Checker.supportsInterface(action.code, 0x61206120),
"UTR: NOT_CALLABLE"
);
Expand All @@ -545,18 +580,20 @@ contract UniversalTokenRouter is ERC165, IUniversalTokenRouter {
Input memory input = action.inputs[j];
if (input.mode == PAYMENT) {
// transient storages
bytes32 key = keccak256(abi.encodePacked(
sender, input.recipient, input.eip, input.token, input.id
bytes32 key = keccak256(abi.encode(
msg.sender, input.recipient, input.eip, input.token, input.id
));
delete t_payments[key];
assembly {
tstore(key, 0)
}
}
}
}
// refund any left-over ETH
uint256 leftOver = address(this).balance;
if (leftOver > 0) {
TransferHelper.safeTransferETH(sender, leftOver);
TransferHelper.safeTransferETH(msg.sender, leftOver);
}
// verify balance changes
Expand Down Expand Up @@ -589,9 +626,13 @@ contract UniversalTokenRouter is ERC165, IUniversalTokenRouter {
/// @param amount token amount to pay with payment
function discard(bytes memory payment, uint256 amount) public virtual override {
bytes32 key = keccak256(payment);
require(t_payments[key] >= amount, 'UTR: INSUFFICIENT_PAYMENT');
unchecked {
t_payments[key] -= amount;
uint256 remain;
assembly {
remain := tload(key)
}
require(remain >= amount, 'UTR: INSUFFICIENT_PAYMENT');
assembly {
tstore(key, sub(remain, amount))
}
}
Expand Down
2 changes: 1 addition & 1 deletion ERCS/erc-7015.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ struct TokenCreation {
Creator attribution is given through a signature verification that MUST be verified by the NFT contract being deployed and an event that MUST be emitted by the NFT contract during the deployment transaction. The event includes all the necessary fields for reconstructing the signed digest and validating the signature to ensure it matches the specified creator. The event name is `CreatorAttribution` and includes the following fields:

- `structHash`: hashed information for deploying the NFT contract (e.g. name, symbol, admins etc). This corresponds to the value `hashStruct` as defined in the [EIP-712 definition of hashStruct](./eip-712.md#definition-of-hashstruct) standard.
- `domainName`: the domain name of the contract verifying the singature (for EIP-712 signature validation).
- `domainName`: the domain name of the contract verifying the signature (for EIP-712 signature validation).
- `version`: the version of the contract verifying the signature (for EIP-712 signature validation)
- `creator`: the creator's account
- `signature`: the creator’s signature
Expand Down
9 changes: 5 additions & 4 deletions ERCS/erc-7208.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
eip: 7208
title: On-Chain Data Containers
description: Interoperability by abstracting logic away from storage
author: Rachid Ajaja <rachid@allianceblock.io>, Alexandros Athanasopulos (@Xaleee), Pavel Rubin (@pash7ka), Sebastian Galimberti Romano (@galimba), Daniel Berbesi (@berbex), Apostolos Mavropoulos (@ApostolosMavro), Barbara Marcano (@Barbara-Marcano), Daniel Ortega (@xdaniortega)
author: Rachid Ajaja (@abrajaja), Matthijs de Vries (@sudomati), Alexandros Athanasopulos (@Xaleee), Pavel Rubin (@pash7ka), Sebastian Galimberti Romano (@galimba), Daniel Berbesi (@berbex), Apostolos Mavropoulos (@ApostolosMavro), Barbara Marcano (@Barbara-Marcano), Daniel Ortega (@xdaniortega)
discussions-to: https://ethereum-magicians.org/t/erc-7208-on-chain-data-container/14778
status: Last Call
last-call-deadline: 2025-01-15
status: Final
type: Standards Track
category: ERC
created: 2023-06-09
Expand Down Expand Up @@ -287,7 +286,9 @@ The access control is separated into three layers:

* **Layer 1**: The **Data Point Registry** allocates for **Data Managers** and manages ownership (admin/write rights) of **Data Points**.
* **Layer 2**: The **Data Index** smart contract implements Access Control by managing Approvals of **Data Managers** to **Data Points**. It uses the **Data Point Registry** to verify who can grant/revoke this access.
* **Layer 3**: Each **Data Manager** exposes functions that can perform `write` operations on the **Data Point** by calling the **Data Index** implementation.
* **Layer 3**: The **DataObject** manages trust relationship between the **DataPoint** and a **DataIndex** implementation and allows a trusted **DataIndex** to execute `write` operations.

A common task for a **Data Object** is to store user-related data, while the **Data Manager** implements the logic for managing such data. Both **Data Objects** and **Data Managers** often require the management of user IDs. A **Data Index** can offer logic for user management (i.e. IDs based on `address`) that are independent of any particular implementation, but care must be taken in selecting these identifiers. If chosen improperly, they could hinder a **Data Manager**'s ability to migrate between different **Data Indexes**.

No further security considerations are derived specifically from this ERC.

Expand Down
3 changes: 1 addition & 2 deletions ERCS/erc-7578.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ title: Physical Asset Redemption
description: Provides the holder of physical asset backed NFTs readily available real-world information on the underlying physical asset.
author: Lee Vidor (@V1d0r), David Tan <david@emergentx.org>, Lee Smith <lee@emergentx.org>, Gabriel Stoica (@gabrielstoica)
discussions-to: https://ethereum-magicians.org/t/erc-7578-physical-asset-redemption/17556
status: Last Call
last-call-deadline: 2025-01-14
status: Final
type: Standards Track
category: ERC
created: 2023-08-01
Expand Down
3 changes: 1 addition & 2 deletions ERCS/erc-7734.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ title: Decentralized Identity Verification (DID)
description: A privacy-preserving method for decentralized identity verification, enabling secure integration of identity management in dApps.
author: Anushka Yadav (@64anushka) <64anushka@gmail.com>
discussions-to: https://ethereum-magicians.org/t/discussion-on-decentralized-identity-verification-did-standard/20392
status: Last Call
last-call-deadline: 2025-01-21
status: Final
type: Standards Track
category: ERC
created: 2024-06-26
Expand Down
31 changes: 23 additions & 8 deletions ERCS/erc-7744.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ title: Code Index
description: Global repository of bytecode, enabling developers, auditors, and researchers to find, analyze, and reuse bytecode efficiently.
author: Tim Pechersky (@peersky) <t@peersky.xyz>
discussions-to: https://ethereum-magicians.org/t/erc-7744-code-index/20569
status: Draft
status: Review
type: Standards Track
category: ERC
created: 2024-07-16
Expand Down Expand Up @@ -34,16 +34,15 @@ Its simplicity and generic nature make it suitable for a wide range of applicati

Ultimately, this feature should be incorporated into EIP standards, as it is a fundamental building block for trustless and secure smart contract development. This standard is a step towards this goal.


## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity =0.8.20;
pragma solidity 0.8.28;
interface ICodeIndex {
interface IERC7744 {
event Indexed(address indexed container, bytes32 indexed codeHash);
error alreadyExists(bytes32 id, address source);
Expand All @@ -58,19 +57,28 @@ interface ICodeIndex {
* @dev This allows to query contracts by their bytecode instead of addresses.
* @author Tim Pechersky (@Peersky)
*/
contract CodeIndex is ICodeIndex {
contract ERC7744 is IERC7744 {
mapping(bytes32 => address) private index;
function isValidContainer(address container) private view returns (bool) {
bytes memory code = container.code;
bytes32 codeHash = container.codehash;
bytes32 eip7702Hash = bytes32(0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329);
// Contract should have non-empty code and valid codehash
return (code.length > 0 && codeHash != bytes32(0) && codeHash != eip7702Hash);
}
/**
* @notice Registers a contract in the index by its bytecode hash
* @param container The contract to register
* @dev `msg.codeHash` will be used
* @dev It will revert if the contract is already indexed
* @dev It will revert if the contract is already indexed or if returns EIP7702 hash
*/
function register(address container) external {
address etalon = index[container.codehash];
require(isValidContainer(container), "Invalid container");
if (etalon != address(0)) {
revert alreadyExists(container.codehash, etalon);
if (isValidContainer(etalon)) revert alreadyExists(container.codehash, container);
}
index[container.codehash] = container;
emit Indexed(container, container.codehash);
Expand All @@ -87,11 +95,12 @@ contract CodeIndex is ICodeIndex {
}
}
```

### Deployment method

The `CodeIndex` contract is deployed at: `0xc0D31d398c5ee86C5f8a23FA253ee8a586dA03Ce` using `CREATE2` via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of `0x220a70730c743a005cfd55180805d2c0d5b8c7695c5496100dcffa91c02befce` is obtained by seeking a vanity address with meaningful name "Code ID (`c0D31d`).
The `CodeIndex` contract is deployed at: `0xC0dE1D2F7662c63796E544B2647b2A94EE658E07` using `CREATE2` via the deterministic deployer at `0x4e59b44847b379578588920ca78fbf26c0b4956c` with a salt of `0x70b27c94ed692bfb60748cee464ef910d4bf768ac1f3a63eeb4c05258f629256` is obtained by seeking a vanity address with meaningful name "Code ID (`0xC0dE1D`).

## Rationale

Expand All @@ -113,6 +122,12 @@ Reference implementation of the Code Index can be found in the assets folder. Th

**Storage contents of registered contracts**: The index only refers to the bytecode of the contract, not the storage contents. This means that the contract state is not indexed and may change over time.

**[EIP-7702]**: The index does not index the EIP-7702 delegated accounts, as it is a special case and its bytecode is not deterministic. During attempt to register, it checks `EXTCODEHASH` for new address and if value is `0xeadcdba66a79ab5dce91622d1d75c8cff5cff0b96944c3bf1072cd08ce018329` (`keccak256(0xef01)`) it will revert.

**Self-Destruct Contracts**: In case of indexed contract storage becomes empty, contracts may be re-indexed, During register function call, iF contract is already indexed, we run `isValidContainer` check on the indexed address. It it fails, re-indexing is allowed with a newly specified address.

[EIP-7702]: ./eip-7702.md

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
Loading

0 comments on commit 19ad032

Please sign in to comment.