-
Notifications
You must be signed in to change notification settings - Fork 121
/
Bundler.sol
179 lines (155 loc) · 6.77 KB
/
Bundler.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import {IdRegistry} from "./IdRegistry.sol";
import {StorageRegistry} from "./StorageRegistry.sol";
import {KeyRegistry} from "./KeyRegistry.sol";
import {IBundler} from "./interfaces/IBundler.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";
import {TransferHelper} from "./lib/TransferHelper.sol";
contract Bundler is IBundler, TrustedCaller {
using TransferHelper for address;
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert if the caller does not have the authority to perform the action.
error Unauthorized();
/// @dev Revert if the caller attempts to rent zero storage units.
error InvalidAmount();
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Contract version specified using Farcaster protocol version scheme.
*/
string public constant VERSION = "2023.08.23";
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
/**
* @dev Address of the IdRegistry contract
*/
IdRegistry public immutable idRegistry;
/**
* @dev Address of the StorageRegistry contract
*/
StorageRegistry public immutable storageRegistry;
/**
* @dev Address of the KeyRegistry contract
*/
KeyRegistry public immutable keyRegistry;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Configure the addresses of the Registry contracts and the trusted caller, which is
* allowed to register users during the bootstrap phase.
*
* @param _idRegistry Address of the IdRegistry contract
* @param _storageRegistry Address of the StorageRegistry contract
* @param _keyRegistry Address of the KeyRegistry contract
* @param _trustedCaller Address that can call trustedRegister and trustedBatchRegister
* @param _initialOwner Address that can set the trusted caller
*/
constructor(
address _idRegistry,
address _storageRegistry,
address _keyRegistry,
address _trustedCaller,
address _initialOwner
) TrustedCaller(_initialOwner) {
idRegistry = IdRegistry(_idRegistry);
storageRegistry = StorageRegistry(_storageRegistry);
keyRegistry = KeyRegistry(_keyRegistry);
_setTrustedCaller(_trustedCaller);
}
/**
* @notice Register an fid, multiple signers, and rent storage to an address in a single transaction.
*
* @param registration Struct containing registration parameters: to, recovery, deadline, and signature.
* @param signers Array of structs containing signer parameters: keyType, key, metadataType,
* metadata, deadline, and signature.
* @param storageUnits Number of storage units to rent
*
*/
function register(
RegistrationParams calldata registration,
SignerParams[] calldata signers,
uint256 storageUnits
) external payable {
if (storageUnits == 0) revert InvalidAmount();
uint256 fid =
idRegistry.registerFor(registration.to, registration.recovery, registration.deadline, registration.sig);
uint256 signersLen = signers.length;
for (uint256 i; i < signersLen;) {
SignerParams calldata signer = signers[i];
keyRegistry.addFor(
registration.to,
signer.keyType,
signer.key,
signer.metadataType,
signer.metadata,
signer.deadline,
signer.sig
);
// We know this will not overflow because it's less than the length of the array, which is a `uint256`.
unchecked {
++i;
}
}
uint256 overpayment = storageRegistry.rent{value: msg.value}(fid, storageUnits);
if (overpayment > 0) {
msg.sender.sendNative(overpayment);
}
}
/**
* @notice Register an fid, add a signer, and credit storage to an address in a single transaction. Can only
* be called by the trustedCaller during the Seedable phase.
*
* @param user UserData struct including to/recovery address, key params, and number of storage units.
*/
function trustedRegister(UserData calldata user) external onlyTrustedCaller {
// Will revert unless IdRegistry is in the Seedable phase
uint256 fid = idRegistry.trustedRegister(user.to, user.recovery);
uint256 signersLen = user.signers.length;
for (uint256 i; i < signersLen;) {
SignerData calldata signer = user.signers[i];
keyRegistry.trustedAdd(user.to, signer.keyType, signer.key, signer.metadataType, signer.metadata);
unchecked {
++i;
}
}
storageRegistry.credit(fid, user.units);
}
/**
* @notice Register fids, keys, and credit storage for multiple users in a single transaction. Can
* only be called by the trustedCaller during the Seedable phase. Will be used when
* migrating across Ethereum networks to bootstrap a new contract with existing data.
*
* @param users Array of UserData structs to register
*/
function trustedBatchRegister(UserData[] calldata users) external onlyTrustedCaller {
// Safety: calls inside a loop are safe since caller is trusted
uint256 usersLen = users.length;
for (uint256 i; i < usersLen;) {
UserData calldata user = users[i];
uint256 fid = idRegistry.trustedRegister(user.to, user.recovery);
uint256 signersLen = user.signers.length;
for (uint256 j; j < signersLen;) {
SignerData calldata signer = user.signers[j];
keyRegistry.trustedAdd(user.to, signer.keyType, signer.key, signer.metadataType, signer.metadata);
unchecked {
++j;
}
}
storageRegistry.credit(fid, user.units);
// We know this will not overflow because it's less than the length of the array, which is a `uint256`.
unchecked {
++i;
}
}
}
// solhint-disable-next-line no-empty-blocks
receive() external payable {
if (msg.sender != address(storageRegistry)) revert Unauthorized();
}
}