Skip to content

Commit a060e93

Browse files
Amxxernestognw
andauthored
Add RelayedCall library to hide the msg.sender from a target contract (#5630)
Co-authored-by: Ernesto García <[email protected]>
1 parent 8dbd6bc commit a060e93

File tree

5 files changed

+400
-42
lines changed

5 files changed

+400
-42
lines changed

.changeset/clear-tools-refuse.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': minor
3+
---
4+
5+
`RelayedCall`: Add a library to perform indirect calls through minimal and predictable relayers.

contracts/mocks/Stateless.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {NoncesKeyed} from "../utils/NoncesKeyed.sol";
4141
import {P256} from "../utils/cryptography/P256.sol";
4242
import {Packing} from "../utils/Packing.sol";
4343
import {Panic} from "../utils/Panic.sol";
44+
import {RelayedCall} from "../utils/RelayedCall.sol";
4445
import {RSA} from "../utils/cryptography/RSA.sol";
4546
import {SafeCast} from "../utils/math/SafeCast.sol";
4647
import {SafeERC20} from "../token/ERC20/utils/SafeERC20.sol";

contracts/utils/README.adoc

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,42 @@ Miscellaneous contracts and libraries containing utility functions you can use t
77

88
* {Math}, {SignedMath}: Implementation of various arithmetic functions.
99
* {SafeCast}: Checked downcasting functions to avoid silent truncation.
10-
* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions.
11-
* {ReentrancyGuardTransient}: Variant of {ReentrancyGuard} that uses transient storage (https://eips.ethereum.org/EIPS/eip-1153[EIP-1153]).
12-
* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending.
1310
* {Nonces}: Utility for tracking and verifying address nonces that only increment.
1411
* {NoncesKeyed}: Alternative to {Nonces}, that support keyed nonces following https://eips.ethereum.org/EIPS/eip-4337#semi-abstracted-nonce-support[ERC-4337 specifications].
12+
* {Pausable}: A common emergency response mechanism that can pause functionality while a remediation is pending.
13+
* {ReentrancyGuard}: A modifier that can prevent reentrancy during certain functions.
14+
* {ReentrancyGuardTransient}: Variant of {ReentrancyGuard} that uses transient storage (https://eips.ethereum.org/EIPS/eip-1153[EIP-1153]).
1515
* {ERC165}, {ERC165Checker}: Utilities for inspecting interfaces supported by contracts.
1616
* {BitMaps}: A simple library to manage boolean value mapped to a numerical index in an efficient way.
17+
* {Checkpoints}: A data structure to store values mapped to a strictly increasing key. Can be used for storing and accessing values over time.
18+
* {CircularBuffer}: A data structure to store the last N values pushed to it.
19+
* {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.
1720
* {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`).
1821
* {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.
19-
* {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.
20-
* {CircularBuffer}: A data structure to store the last N values pushed to it.
21-
* {Checkpoints}: A data structure to store values mapped to a strictly increasing key. Can be used for storing and accessing values over time.
2222
* {Heap}: A library that implements a https://en.wikipedia.org/wiki/Binary_heap[binary heap] in storage.
2323
* {MerkleTree}: A library with https://wikipedia.org/wiki/Merkle_Tree[Merkle Tree] data structures and helper functions.
24-
* {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.
2524
* {Address}: Collection of functions for overloading Solidity's https://docs.soliditylang.org/en/latest/types.html#address[`address`] type.
2625
* {Arrays}: Collection of functions that operate on https://docs.soliditylang.org/en/latest/types.html#arrays[`arrays`].
2726
* {Base64}: On-chain base64 and base64URL encoding according to https://datatracker.ietf.org/doc/html/rfc4648[RFC-4648].
27+
* {Blockhash}: A library for accessing historical block hashes beyond the standard 256 block limit utilizing EIP-2935's historical blockhash functionality.
2828
* {Bytes}: Common operations on bytes objects.
29+
* {CAIP2}, {CAIP10}: Libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers.
2930
* {Calldata}: Helpers for manipulating calldata.
30-
* {Strings}: Common operations for strings formatting.
31+
* {Comparators}: A library that contains comparator functions to use with the {Heap} library.
32+
* {Context}: A utility for abstracting the sender and calldata in the current execution context.
33+
* {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.
34+
* {InteroperableAddress}: Library for formatting and parsing ERC-7930 interoperable addresses.
35+
* {Memory}: A utility library to manipulate memory.
36+
* {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.
37+
* {Packing}: A library for packing and unpacking multiple values into bytes32.
38+
* {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].
39+
* {RelayedCall}: A library for performing calls that use minimal and predictable relayers to hide the sender.
3140
* {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.
3241
* {SlotDerivation}: Methods for deriving storage slot from ERC-7201 namespaces as well as from constructions such as mapping and arrays.
3342
* {StorageSlot}: Methods for accessing specific storage slots formatted as common primitive types.
34-
* {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported).
35-
* {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.
36-
* {Context}: A utility for abstracting the sender and calldata in the current execution context.
37-
* {Packing}: A library for packing and unpacking multiple values into bytes32
38-
* {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].
39-
* {Comparators}: A library that contains comparator functions to use with the {Heap} library.
40-
* {CAIP2}, {CAIP10}: Libraries for formatting and parsing CAIP-2 and CAIP-10 identifiers.
41-
* {Memory}: A utility library to manipulate memory.
42-
* {InteroperableAddress}: Library for formatting and parsing ERC-7930 interoperable addresses.
43-
* {Blockhash}: A library for accessing historical block hashes beyond the standard 256 block limit utilizing EIP-2935's historical blockhash functionality.
43+
* {Strings}: Common operations for strings formatting.
4444
* {Time}: A library that provides helpers for manipulating time-related objects, including a `Delay` type.
45+
* {TransientSlot}: Primitives for reading from and writing to transient storage (only value types are currently supported).
4546

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

5960
== Security
6061

61-
{{ReentrancyGuard}}
62+
{{Nonces}}
6263

63-
{{ReentrancyGuardTransient}}
64+
{{NoncesKeyed}}
6465

6566
{{Pausable}}
6667

67-
{{Nonces}}
68+
{{ReentrancyGuard}}
6869

69-
{{NoncesKeyed}}
70+
{{ReentrancyGuardTransient}}
7071

7172
== Introspection
7273

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

8586
{{BitMaps}}
8687

87-
{{EnumerableMap}}
88+
{{Checkpoints}}
8889

89-
{{EnumerableSet}}
90+
{{CircularBuffer}}
9091

9192
{{DoubleEndedQueue}}
9293

93-
{{CircularBuffer}}
94+
{{EnumerableMap}}
9495

95-
{{Checkpoints}}
96+
{{EnumerableSet}}
9697

9798
{{Heap}}
9899

99100
{{MerkleTree}}
100101

101102
== Libraries
102103

103-
{{Create2}}
104-
105104
{{Address}}
106105

107106
{{Arrays}}
108107

109108
{{Base64}}
110109

110+
{{Blockhash}}
111+
111112
{{Bytes}}
112113

114+
{{CAIP10}}
115+
116+
{{CAIP2}}
117+
113118
{{Calldata}}
114119

115-
{{Strings}}
120+
{{Comparators}}
116121

117-
{{ShortStrings}}
122+
{{Context}}
118123

119-
{{SlotDerivation}}
124+
{{Create2}}
120125

121-
{{StorageSlot}}
126+
{{InteroperableAddress}}
122127

123-
{{TransientSlot}}
128+
{{Memory}}
124129

125130
{{Multicall}}
126131

127-
{{Context}}
128-
129132
{{Packing}}
130133

131134
{{Panic}}
132135

133-
{{Comparators}}
134-
135-
{{CAIP2}}
136+
{{RelayedCall}}
136137

137-
{{CAIP10}}
138+
{{ShortStrings}}
138139

139-
{{Memory}}
140+
{{SlotDerivation}}
140141

141-
{{InteroperableAddress}}
142+
{{StorageSlot}}
142143

143-
{{Blockhash}}
144+
{{Strings}}
144145

145146
{{Time}}
147+
148+
{{TransientSlot}}

contracts/utils/RelayedCall.sol

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
/**
6+
* @dev Library for performing external calls through dynamically deployed relay contracts that hide the original
7+
* caller's address from the target contract. This pattern is used in ERC-4337's EntryPoint for account factory
8+
* calls and ERC-6942 for safe factory interactions.
9+
*
10+
* When privileged contracts need to make arbitrary external calls based on user input, calling the target directly
11+
* can be risky because the target sees the privileged contract as `msg.sender` and could exploit this trust
12+
* relationship. This library solves this by deploying minimal relay contracts that act as intermediaries, ensuring
13+
* the target only sees the unprivileged relay address as `msg.sender`.
14+
*
15+
* For example, instead of `target.call(data)` where the target sees this contract as `msg.sender`, use
16+
* {relayCall} where the target sees a relay address as `msg.sender`.
17+
*/
18+
library RelayedCall {
19+
/// @dev Relays a call to the target contract through a dynamically deployed relay contract.
20+
function relayCall(address target, bytes memory data) internal returns (bool, bytes memory) {
21+
return relayCall(target, 0, data);
22+
}
23+
24+
/// @dev Same as {relayCall} but with a value.
25+
function relayCall(address target, uint256 value, bytes memory data) internal returns (bool, bytes memory) {
26+
return relayCall(target, value, data, bytes32(0));
27+
}
28+
29+
/// @dev Same as {relayCall} but with a salt.
30+
function relayCall(address target, bytes memory data, bytes32 salt) internal returns (bool, bytes memory) {
31+
return relayCall(target, 0, data, salt);
32+
}
33+
34+
/// @dev Same as {relayCall} but with a salt and a value.
35+
function relayCall(
36+
address target,
37+
uint256 value,
38+
bytes memory data,
39+
bytes32 salt
40+
) internal returns (bool, bytes memory) {
41+
return getRelayer(salt).call{value: value}(abi.encodePacked(target, data));
42+
}
43+
44+
/// @dev Same as {getRelayer} but with a `bytes32(0)` default salt.
45+
function getRelayer() internal returns (address) {
46+
return getRelayer(bytes32(0));
47+
}
48+
49+
/// @dev Returns the relayer address for a given salt.
50+
function getRelayer(bytes32 salt) internal returns (address relayer) {
51+
// [Relayer details]
52+
//
53+
// deployment prefix: 60475f8160095f39f3
54+
// deployed bytecode: 73<addr>331460133611166022575f5ffd5b6014360360145f375f5f601436035f345f3560601c5af13d5f5f3e5f3d91604557fd5bf3
55+
//
56+
// offset | bytecode | opcode | stack
57+
// -------|-------------|----------------|--------
58+
// 0x0000 | 73<factory> | push20 <addr> | <factory>
59+
// 0x0015 | 33 | address | <caller> <factory>
60+
// 0x0016 | 14 | eq | access
61+
// 0x0017 | 6013 | push1 0x13 | 0x13 access
62+
// 0x0019 | 36 | calldatasize | cds 0x13 access
63+
// 0x001a | 11 | gt | (cds>0x13) access
64+
// 0x001b | 16 | and | (cds>0x13 && access)
65+
// 0x001c | 6022 | push1 0x22 | 0x22 (cds>0x13 && access)
66+
// 0x001e | 57 | jumpi |
67+
// 0x001f | 5f | push0 | 0
68+
// 0x0020 | 5f | push0 | 0 0
69+
// 0x0021 | fd | revert |
70+
// 0x0022 | 5b | jumpdest |
71+
// 0x0023 | 6014 | push1 0x14 | 0x14
72+
// 0x0025 | 36 | calldatasize | cds 0x14
73+
// 0x0026 | 03 | sub | (cds-0x14)
74+
// 0x0027 | 6014 | push1 0x14 | 0x14 (cds-0x14)
75+
// 0x0029 | 5f | push0 | 0 0x14 (cds-0x14)
76+
// 0x002a | 37 | calldatacopy |
77+
// 0x002b | 5f | push0 | 0
78+
// 0x002c | 5f | push0 | 0 0
79+
// 0x002d | 6014 | push1 0x14 | 0x14 0 0
80+
// 0x002f | 36 | calldatasize | cds 0x14 0 0
81+
// 0x0030 | 03 | sub | (cds-0x14) 0 0
82+
// 0x0031 | 5f | push0 | 0 (cds-0x14) 0 0
83+
// 0x0032 | 34 | callvalue | value 0 (cds-0x14) 0 0
84+
// 0x0033 | 5f | push0 | 0 value 0 (cds-0x14) 0 0
85+
// 0x0034 | 35 | calldataload | cd[0] value 0 (cds-0x14) 0 0
86+
// 0x0035 | 6060 | push1 0x60 | 0x60 cd[0] value 0 (cds-0x14) 0 0
87+
// 0x0037 | 1c | shr | target value 0 (cds-0x14) 0 0
88+
// 0x0038 | 5a | gas | gas target value 0 (cds-0x14) 0 0
89+
// 0x0039 | f1 | call | suc
90+
// 0x003a | 3d | returndatasize | rds suc
91+
// 0x003b | 5f | push0 | 0 rds suc
92+
// 0x003c | 5f | push0 | 0 0 rds suc
93+
// 0x003d | 3e | returndatacopy | suc
94+
// 0x003e | 5f | push0 | 0 suc
95+
// 0x003f | 3d | returndatasize | rds 0 suc
96+
// 0x0040 | 91 | swap2 | suc 0 rds
97+
// 0x0041 | 6045 | push1 0x45 | 0x45 suc 0 rds
98+
// 0x0043 | 57 | jumpi | 0 rds
99+
// 0x0044 | fd | revert |
100+
// 0x0045 | 5b | jumpdest | 0 rds
101+
// 0x0046 | f3 | return |
102+
103+
assembly ("memory-safe") {
104+
let fmp := mload(0x40)
105+
106+
// build initcode at FMP
107+
mstore(add(fmp, 0x46), 0x60145f375f5f601436035f345f3560601c5af13d5f5f3e5f3d91604557fd5bf3)
108+
mstore(add(fmp, 0x26), 0x331460133611166022575f5ffd5b60143603)
109+
mstore(add(fmp, 0x14), address())
110+
mstore(add(fmp, 0), 0x60475f8160095f39f373)
111+
let initcodehash := keccak256(add(fmp, 0x16), 0x50)
112+
113+
// compute create2 address
114+
mstore(0x40, initcodehash)
115+
mstore(0x20, salt)
116+
mstore(0x00, address())
117+
mstore8(0x0b, 0xff)
118+
relayer := and(keccak256(0x0b, 0x55), shr(96, not(0)))
119+
120+
// is relayer not yet deployed, deploy it
121+
if iszero(extcodesize(relayer)) {
122+
if iszero(create2(0, add(fmp, 0x16), 0x50, salt)) {
123+
returndatacopy(fmp, 0, returndatasize())
124+
revert(fmp, returndatasize())
125+
}
126+
}
127+
128+
// cleanup fmp space used as scratch
129+
mstore(0x40, fmp)
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)