Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clear-tools-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`RelayedCall`: Add a library to perform indirect calls through minimal and predictable relayers.
1 change: 1 addition & 0 deletions contracts/mocks/Stateless.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {NoncesKeyed} from "../utils/NoncesKeyed.sol";
import {P256} from "../utils/cryptography/P256.sol";
import {Packing} from "../utils/Packing.sol";
import {Panic} from "../utils/Panic.sol";
import {RelayedCall} from "../utils/RelayedCall.sol";
import {RSA} from "../utils/cryptography/RSA.sol";
import {SafeCast} from "../utils/math/SafeCast.sol";
import {SafeERC20} from "../token/ERC20/utils/SafeERC20.sol";
Expand Down
87 changes: 45 additions & 42 deletions contracts/utils/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,42 @@ Miscellaneous contracts and libraries containing utility functions you can use t

* {Math}, {SignedMath}: Implementation of various arithmetic functions.
* {SafeCast}: Checked downcasting functions to avoid silent truncation.
* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions.
* {ReentrancyGuardTransient}: Variant of {ReentrancyGuard} that uses transient storage (https://eips.ethereum.org/EIPS/eip-1153[EIP-1153]).
* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending.
* {Nonces}: Utility for tracking and verifying address nonces that only increment.
* {NoncesKeyed}: Alternative to {Nonces}, that support keyed nonces following https://eips.ethereum.org/EIPS/eip-4337#semi-abstracted-nonce-support[ERC-4337 specifications].
* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending.
* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions.
* {ReentrancyGuardTransient}: Variant of {ReentrancyGuard} that uses transient storage (https://eips.ethereum.org/EIPS/eip-1153[EIP-1153]).
* {ERC165}, {ERC165Checker}: Utilities for inspecting interfaces supported by contracts.
* {BitMaps}: A simple library to manage boolean value mapped to a numerical index in an efficient way.
* {Checkpoints}: A data structure to store values mapped to a strictly increasing key. Can be used for storing and accessing values over time.
* {CircularBuffer}: A data structure to store the last N values pushed to it.
* {DoubleEndedQueue}: An implementation of a https://en.wikipedia.org/wiki/Double-ended_queue[double ended queue] whose values can be added or removed from both sides. Useful for FIFO and LIFO structures.
* {EnumerableMap}: A type like Solidity's https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`], but with key-value _enumeration_: this will let you know how many entries a mapping has, and iterate over them (which is not possible with `mapping`).
* {EnumerableSet}: Like {EnumerableMap}, but for https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets]. Can be used to store privileged accounts, issued IDs, etc.
* {DoubleEndedQueue}: An implementation of a https://en.wikipedia.org/wiki/Double-ended_queue[double ended queue] whose values can be added or removed from both sides. Useful for FIFO and LIFO structures.
* {CircularBuffer}: A data structure to store the last N values pushed to it.
* {Checkpoints}: A data structure to store values mapped to a strictly increasing key. Can be used for storing and accessing values over time.
* {Heap}: A library that implements a https://en.wikipedia.org/wiki/Binary_heap[binary heap] in storage.
* {MerkleTree}: A library with https://wikipedia.org/wiki/Merkle_Tree[Merkle Tree] data structures and helper functions.
* {Create2}: Wrapper around the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode] for safe use without having to deal with low-level assembly.
* {Address}: Collection of functions for overloading Solidity's https://docs.soliditylang.org/en/latest/types.html#address[`address`] type.
* {Arrays}: Collection of functions that operate on https://docs.soliditylang.org/en/latest/types.html#arrays[`arrays`].
* {Base64}: On-chain base64 and base64URL encoding according to https://datatracker.ietf.org/doc/html/rfc4648[RFC-4648].
* {Blockhash}: A library for accessing historical block hashes beyond the standard 256 block limit utilizing EIP-2935's historical blockhash functionality.
* {Bytes}: Common operations on bytes objects.
* {CAIP2}, {CAIP10}: Libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers.
* {Calldata}: Helpers for manipulating calldata.
* {Strings}: Common operations for strings formatting.
* {Comparators}: A library that contains comparator functions to use with the {Heap} library.
* {Context}: A utility for abstracting the sender and calldata in the current execution context.
* {Create2}: Wrapper around the https://blog.openzeppelin.com/getting-the-most-out-of-create2/[`CREATE2` EVM opcode] for safe use without having to deal with low-level assembly.
* {InteroperableAddress}: Library for formatting and parsing ERC-7930 interoperable addresses.
* {Memory}: A utility library to manipulate memory.
* {Multicall}: Abstract contract with a utility to allow batching together multiple calls in a single transaction. Useful for allowing EOAs to perform multiple operations at once.
* {Packing}: A library for packing and unpacking multiple values into bytes32.
* {Panic}: A library to revert with https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require[Solidity panic codes].
* {RelayedCall}: A library for performing calls that use minimal and predictable relayers to hide the sender.
* {ShortStrings}: Library to encode (and decode) short strings into (or from) a single bytes32 slot for optimizing costs. Short strings are limited to 31 characters.
* {SlotDerivation}: Methods for deriving storage slot from ERC-7201 namespaces as well as from constructions such as mapping and arrays.
* {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types.
* {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported).
* {Multicall}: Abstract contract with a utility to allow batching together multiple calls in a single transaction. Useful for allowing EOAs to perform multiple operations at once.
* {Context}: A utility for abstracting the sender and calldata in the current execution context.
* {Packing}: A library for packing and unpacking multiple values into bytes32
* {Panic}: A library to revert with https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require[Solidity panic codes].
* {Comparators}: A library that contains comparator functions to use with the {Heap} library.
* {CAIP2}, {CAIP10}: Libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers.
* {Memory}: A utility library to manipulate memory.
* {InteroperableAddress}: Library for formatting and parsing ERC-7930 interoperable addresses.
* {Blockhash}: A library for accessing historical block hashes beyond the standard 256 block limit utilizing EIP-2935's historical blockhash functionality.
* {Strings}: Common operations for strings formatting.
* {Time}: A library that provides helpers for manipulating time-related objects, including a `Delay` type.
* {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported).

[NOTE]
====
Expand All @@ -58,15 +59,15 @@ Because Solidity does not support generic types, {EnumerableMap} and {Enumerable

== Security

{{ReentrancyGuard}}
{{Nonces}}

{{ReentrancyGuardTransient}}
{{NoncesKeyed}}

{{Pausable}}

{{Nonces}}
{{ReentrancyGuard}}

{{NoncesKeyed}}
{{ReentrancyGuardTransient}}

== Introspection

Expand All @@ -84,62 +85,64 @@ Ethereum contracts have no native concept of an interface, so applications must

{{BitMaps}}

{{EnumerableMap}}
{{Checkpoints}}

{{EnumerableSet}}
{{CircularBuffer}}

{{DoubleEndedQueue}}

{{CircularBuffer}}
{{EnumerableMap}}

{{Checkpoints}}
{{EnumerableSet}}

{{Heap}}

{{MerkleTree}}

== Libraries

{{Create2}}

{{Address}}

{{Arrays}}

{{Base64}}

{{Blockhash}}

{{Bytes}}

{{CAIP10}}

{{CAIP2}}

{{Calldata}}

{{Strings}}
{{Comparators}}

{{ShortStrings}}
{{Context}}

{{SlotDerivation}}
{{Create2}}

{{StorageSlot}}
{{InteroperableAddress}}

{{TransientSlot}}
{{Memory}}

{{Multicall}}

{{Context}}

{{Packing}}

{{Panic}}

{{Comparators}}

{{CAIP2}}
{{RelayedCall}}

{{CAIP10}}
{{ShortStrings}}

{{Memory}}
{{SlotDerivation}}

{{InteroperableAddress}}
{{StorageSlot}}

{{Blockhash}}
{{Strings}}

{{Time}}

{{TransientSlot}}
132 changes: 132 additions & 0 deletions contracts/utils/RelayedCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
* @dev Library for performing external calls through dynamically deployed relay contracts that hide the original
* caller's address from the target contract. This pattern is used in ERC-4337's EntryPoint for account factory
* calls and ERC-6942 for safe factory interactions.
*
* When privileged contracts need to make arbitrary external calls based on user input, calling the target directly
* can be risky because the target sees the privileged contract as `msg.sender` and could exploit this trust
* relationship. This library solves this by deploying minimal relay contracts that act as intermediaries, ensuring
* the target only sees the unprivileged relay address as `msg.sender`.
*
* For example, instead of `target.call(data)` where the target sees this contract as `msg.sender`, use
* {relayCall} where the target sees a relay address as `msg.sender`.
*/
library RelayedCall {
/// @dev Relays a call to the target contract through a dynamically deployed relay contract.
function relayCall(address target, bytes memory data) internal returns (bool, bytes memory) {
return relayCall(target, 0, data);
}

/// @dev Same as {relayCall} but with a value.
function relayCall(address target, uint256 value, bytes memory data) internal returns (bool, bytes memory) {
return relayCall(target, value, data, bytes32(0));
}

/// @dev Same as {relayCall} but with a salt.
function relayCall(address target, bytes memory data, bytes32 salt) internal returns (bool, bytes memory) {
return relayCall(target, 0, data, salt);
}

/// @dev Same as {relayCall} but with a salt and a value.
function relayCall(
address target,
uint256 value,
bytes memory data,
bytes32 salt
) internal returns (bool, bytes memory) {
return getRelayer(salt).call{value: value}(abi.encodePacked(target, data));
}

/// @dev Same as {getRelayer} but with a `bytes32(0)` default salt.
function getRelayer() internal returns (address) {
return getRelayer(bytes32(0));
}

/// @dev Returns the relayer address for a given salt.
function getRelayer(bytes32 salt) internal returns (address relayer) {
// [Relayer details]
//
// deployment prefix: 60475f8160095f39f3
// deployed bytecode: 73<addr>331460133611166022575f5ffd5b6014360360145f375f5f601436035f345f3560601c5af13d5f5f3e5f3d91604557fd5bf3
//
// offset | bytecode | opcode | stack
// -------|-------------|----------------|--------
// 0x0000 | 73<factory> | push20 <addr> | <factory>
// 0x0015 | 33 | address | <caller> <factory>
// 0x0016 | 14 | eq | access
// 0x0017 | 6013 | push1 0x13 | 0x13 access
// 0x0019 | 36 | calldatasize | cds 0x13 access
// 0x001a | 11 | gt | (cds>0x13) access
// 0x001b | 16 | and | (cds>0x13 && access)
// 0x001c | 6022 | push1 0x22 | 0x22 (cds>0x13 && access)
// 0x001e | 57 | jumpi |
// 0x001f | 5f | push0 | 0
// 0x0020 | 5f | push0 | 0 0
// 0x0021 | fd | revert |
// 0x0022 | 5b | jumpdest |
// 0x0023 | 6014 | push1 0x14 | 0x14
// 0x0025 | 36 | calldatasize | cds 0x14
// 0x0026 | 03 | sub | (cds-0x14)
// 0x0027 | 6014 | push1 0x14 | 0x14 (cds-0x14)
// 0x0029 | 5f | push0 | 0 0x14 (cds-0x14)
// 0x002a | 37 | calldatacopy |
// 0x002b | 5f | push0 | 0
// 0x002c | 5f | push0 | 0 0
// 0x002d | 6014 | push1 0x14 | 0x14 0 0
// 0x002f | 36 | calldatasize | cds 0x14 0 0
// 0x0030 | 03 | sub | (cds-0x14) 0 0
// 0x0031 | 5f | push0 | 0 (cds-0x14) 0 0
// 0x0032 | 34 | callvalue | value 0 (cds-0x14) 0 0
// 0x0033 | 5f | push0 | 0 value 0 (cds-0x14) 0 0
// 0x0034 | 35 | calldataload | cd[0] value 0 (cds-0x14) 0 0
// 0x0035 | 6060 | push1 0x60 | 0x60 cd[0] value 0 (cds-0x14) 0 0
// 0x0037 | 1c | shr | target value 0 (cds-0x14) 0 0
// 0x0038 | 5a | gas | gas target value 0 (cds-0x14) 0 0
// 0x0039 | f1 | call | suc
// 0x003a | 3d | returndatasize | rds suc
// 0x003b | 5f | push0 | 0 rds suc
// 0x003c | 5f | push0 | 0 0 rds suc
// 0x003d | 3e | returndatacopy | suc
// 0x003e | 5f | push0 | 0 suc
// 0x003f | 3d | returndatasize | rds 0 suc
// 0x0040 | 91 | swap2 | suc 0 rds
// 0x0041 | 6045 | push1 0x45 | 0x45 suc 0 rds
// 0x0043 | 57 | jumpi | 0 rds
// 0x0044 | fd | revert |
// 0x0045 | 5b | jumpdest | 0 rds
// 0x0046 | f3 | return |

assembly ("memory-safe") {
let fmp := mload(0x40)

// build initcode at FMP
mstore(add(fmp, 0x46), 0x60145f375f5f601436035f345f3560601c5af13d5f5f3e5f3d91604557fd5bf3)
mstore(add(fmp, 0x26), 0x331460133611166022575f5ffd5b60143603)
mstore(add(fmp, 0x14), address())
mstore(add(fmp, 0), 0x60475f8160095f39f373)
let initcodehash := keccak256(add(fmp, 0x16), 0x50)

// compute create2 address
mstore(0x40, initcodehash)
mstore(0x20, salt)
mstore(0x00, address())
mstore8(0x0b, 0xff)
relayer := and(keccak256(0x0b, 0x55), shr(96, not(0)))

// is relayer not yet deployed, deploy it
if iszero(extcodesize(relayer)) {
if iszero(create2(0, add(fmp, 0x16), 0x50, salt)) {
returndatacopy(fmp, 0, returndatasize())
revert(fmp, returndatasize())
}
}

// cleanup fmp space used as scratch
mstore(0x40, fmp)
}
}
}
Loading