Skip to content

Commit e57706e

Browse files
authored
feat: add addKeys to Bundler (#475)
* chore: version Bundler * feat: add addKeys to Bundler * chore: add Bundler deploy script
1 parent 7b3d72e commit e57706e

16 files changed

+976
-34
lines changed

script/DeployL2.s.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {IdGateway} from "../src/IdGateway.sol";
77
import {KeyRegistry} from "../src/KeyRegistry.sol";
88
import {KeyGateway} from "../src/KeyGateway.sol";
99
import {SignedKeyRequestValidator} from "../src/validators/SignedKeyRequestValidator.sol";
10-
import {Bundler, IBundler} from "../src/Bundler.sol";
10+
import {BundlerV1, IBundlerV1} from "../src/BundlerV1.sol";
1111
import {RecoveryProxy} from "../src/RecoveryProxy.sol";
1212
import {IMetadataValidator} from "../src/interfaces/IMetadataValidator.sol";
1313
import {console, ImmutableCreate2Deployer} from "./abstract/ImmutableCreate2Deployer.sol";
@@ -67,7 +67,7 @@ contract DeployL2 is ImmutableCreate2Deployer {
6767
KeyRegistry keyRegistry;
6868
KeyGateway keyGateway;
6969
SignedKeyRequestValidator signedKeyRequestValidator;
70-
Bundler bundler;
70+
BundlerV1 bundler;
7171
RecoveryProxy recoveryProxy;
7272
}
7373

@@ -130,7 +130,7 @@ contract DeployL2 is ImmutableCreate2Deployer {
130130
abi.encode(addrs.idRegistry, params.initialValidatorOwner)
131131
);
132132
addrs.bundler = register(
133-
"Bundler", params.salts.bundler, type(Bundler).creationCode, abi.encode(addrs.idGateway, addrs.keyGateway)
133+
"Bundler", params.salts.bundler, type(BundlerV1).creationCode, abi.encode(addrs.idGateway, addrs.keyGateway)
134134
);
135135
addrs.recoveryProxy = register(
136136
"RecoveryProxy",
@@ -148,7 +148,7 @@ contract DeployL2 is ImmutableCreate2Deployer {
148148
keyRegistry: KeyRegistry(addrs.keyRegistry),
149149
keyGateway: KeyGateway(payable(addrs.keyGateway)),
150150
signedKeyRequestValidator: SignedKeyRequestValidator(addrs.signedKeyRequestValidator),
151-
bundler: Bundler(payable(addrs.bundler)),
151+
bundler: BundlerV1(payable(addrs.bundler)),
152152
recoveryProxy: RecoveryProxy(addrs.recoveryProxy)
153153
});
154154
}

script/UpgradeBundler.s.sol

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.21;
3+
4+
import {Test} from "forge-std/Test.sol";
5+
import {StorageRegistry} from "../src/StorageRegistry.sol";
6+
import {IdRegistry} from "../src/IdRegistry.sol";
7+
import {IdGateway} from "../src/IdGateway.sol";
8+
import {KeyRegistry, IKeyRegistry} from "../src/KeyRegistry.sol";
9+
import {KeyGateway} from "../src/KeyGateway.sol";
10+
import {SignedKeyRequestValidator} from "../src/validators/SignedKeyRequestValidator.sol";
11+
import {Bundler, IBundler} from "../src/Bundler.sol";
12+
import {RecoveryProxy} from "../src/RecoveryProxy.sol";
13+
import {console, ImmutableCreate2Deployer} from "./abstract/ImmutableCreate2Deployer.sol";
14+
15+
contract UpgradeBundler is ImmutableCreate2Deployer, Test {
16+
struct Salts {
17+
bytes32 bundler;
18+
}
19+
20+
struct DeploymentParams {
21+
Salts salts;
22+
}
23+
24+
struct Addresses {
25+
address storageRegistry;
26+
address idRegistry;
27+
address idGateway;
28+
address keyRegistry;
29+
address keyGateway;
30+
address signedKeyRequestValidator;
31+
address bundler;
32+
address recoveryProxy;
33+
}
34+
35+
struct Contracts {
36+
StorageRegistry storageRegistry;
37+
IdRegistry idRegistry;
38+
IdGateway idGateway;
39+
KeyRegistry keyRegistry;
40+
KeyGateway keyGateway;
41+
SignedKeyRequestValidator signedKeyRequestValidator;
42+
Bundler bundler;
43+
RecoveryProxy recoveryProxy;
44+
}
45+
46+
function run() public {
47+
runSetup(runDeploy(loadDeploymentParams()));
48+
}
49+
50+
function runDeploy(
51+
DeploymentParams memory params
52+
) public returns (Contracts memory) {
53+
return runDeploy(params, true);
54+
}
55+
56+
function runDeploy(DeploymentParams memory params, bool broadcast) public returns (Contracts memory) {
57+
Addresses memory addrs;
58+
59+
// No changes
60+
addrs.storageRegistry = address(0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D);
61+
addrs.idRegistry = address(0x00000000Fc6c5F01Fc30151999387Bb99A9f489b);
62+
addrs.idGateway = payable(address(0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69));
63+
addrs.keyRegistry = address(0x00000000Fc1237824fb747aBDE0FF18990E59b7e);
64+
addrs.keyGateway = address(0x00000000fC56947c7E7183f8Ca4B62398CaAdf0B);
65+
addrs.signedKeyRequestValidator = address(0x00000000FC700472606ED4fA22623Acf62c60553);
66+
addrs.recoveryProxy = address(0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7);
67+
68+
// Deploy new Bundler
69+
addrs.bundler = register(
70+
"Bundler", params.salts.bundler, type(Bundler).creationCode, abi.encode(addrs.idGateway, addrs.keyGateway)
71+
);
72+
deploy(broadcast);
73+
74+
return Contracts({
75+
storageRegistry: StorageRegistry(addrs.storageRegistry),
76+
idRegistry: IdRegistry(addrs.idRegistry),
77+
idGateway: IdGateway(payable(addrs.idGateway)),
78+
keyRegistry: KeyRegistry(addrs.keyRegistry),
79+
keyGateway: KeyGateway(payable(addrs.keyGateway)),
80+
signedKeyRequestValidator: SignedKeyRequestValidator(addrs.signedKeyRequestValidator),
81+
bundler: Bundler(payable(addrs.bundler)),
82+
recoveryProxy: RecoveryProxy(addrs.recoveryProxy)
83+
});
84+
}
85+
86+
function runSetup(Contracts memory contracts, DeploymentParams memory, bool) public {
87+
if (deploymentChanged()) {
88+
console.log("Running setup");
89+
90+
// Check bundler deploy parameters
91+
assertEq(address(contracts.bundler.idGateway()), address(contracts.idGateway));
92+
assertEq(address(contracts.bundler.keyGateway()), address(contracts.keyGateway));
93+
} else {
94+
console.log("No changes, skipping setup");
95+
}
96+
}
97+
98+
function runSetup(
99+
Contracts memory contracts
100+
) public {
101+
DeploymentParams memory params = loadDeploymentParams();
102+
runSetup(contracts, params, true);
103+
}
104+
105+
function loadDeploymentParams() internal returns (DeploymentParams memory) {
106+
return DeploymentParams({salts: Salts({bundler: vm.envOr("BUNDLER_CREATE2_SALT", bytes32(0))})});
107+
}
108+
}

script/UpgradeL2.s.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {IdGateway} from "../src/IdGateway.sol";
88
import {KeyRegistry, IKeyRegistry} from "../src/KeyRegistry.sol";
99
import {KeyGateway} from "../src/KeyGateway.sol";
1010
import {SignedKeyRequestValidator} from "../src/validators/SignedKeyRequestValidator.sol";
11-
import {Bundler, IBundler} from "../src/Bundler.sol";
11+
import {BundlerV1, IBundlerV1} from "../src/BundlerV1.sol";
1212
import {RecoveryProxy} from "../src/RecoveryProxy.sol";
1313
import {IMetadataValidator} from "../src/interfaces/IMetadataValidator.sol";
1414
import {console, ImmutableCreate2Deployer} from "./abstract/ImmutableCreate2Deployer.sol";
@@ -61,7 +61,7 @@ contract UpgradeL2 is ImmutableCreate2Deployer, Test {
6161
KeyRegistry keyRegistry;
6262
KeyGateway keyGateway;
6363
SignedKeyRequestValidator signedKeyRequestValidator;
64-
Bundler bundler;
64+
BundlerV1 bundler;
6565
RecoveryProxy recoveryProxy;
6666
}
6767

@@ -108,7 +108,7 @@ contract UpgradeL2 is ImmutableCreate2Deployer, Test {
108108
abi.encode(addrs.keyRegistry, params.initialKeyRegistryOwner)
109109
);
110110
addrs.bundler = register(
111-
"Bundler", params.salts.bundler, type(Bundler).creationCode, abi.encode(addrs.idGateway, addrs.keyGateway)
111+
"Bundler", params.salts.bundler, type(BundlerV1).creationCode, abi.encode(addrs.idGateway, addrs.keyGateway)
112112
);
113113
addrs.recoveryProxy = register(
114114
"RecoveryProxy",
@@ -126,7 +126,7 @@ contract UpgradeL2 is ImmutableCreate2Deployer, Test {
126126
keyRegistry: KeyRegistry(addrs.keyRegistry),
127127
keyGateway: KeyGateway(payable(addrs.keyGateway)),
128128
signedKeyRequestValidator: SignedKeyRequestValidator(addrs.signedKeyRequestValidator),
129-
bundler: Bundler(payable(addrs.bundler)),
129+
bundler: BundlerV1(payable(addrs.bundler)),
130130
recoveryProxy: RecoveryProxy(addrs.recoveryProxy)
131131
});
132132
}

src/Bundler.sol

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ contract Bundler is IBundler {
2323
/**
2424
* @inheritdoc IBundler
2525
*/
26-
string public constant VERSION = "2023.11.15";
26+
string public constant VERSION = "2025.06.13";
2727

2828
/*//////////////////////////////////////////////////////////////
2929
IMMUTABLES
@@ -74,27 +74,31 @@ contract Bundler is IBundler {
7474
(uint256 fid, uint256 overpayment) = idGateway.registerFor{value: msg.value}(
7575
registerParams.to, registerParams.recovery, registerParams.deadline, registerParams.sig, extraStorage
7676
);
77+
_addKeys(registerParams.to, signerParams);
78+
if (overpayment > 0) msg.sender.sendNative(overpayment);
79+
return fid;
80+
}
81+
82+
/**
83+
* @inheritdoc IBundler
84+
*/
85+
function addKeys(address fidOwner, SignerParams[] calldata signerParams) external {
86+
_addKeys(fidOwner, signerParams);
87+
}
7788

89+
function _addKeys(address fidOwner, SignerParams[] calldata signerParams) internal {
7890
uint256 signersLen = signerParams.length;
7991
for (uint256 i; i < signersLen;) {
8092
SignerParams calldata signer = signerParams[i];
8193
keyGateway.addFor(
82-
registerParams.to,
83-
signer.keyType,
84-
signer.key,
85-
signer.metadataType,
86-
signer.metadata,
87-
signer.deadline,
88-
signer.sig
94+
fidOwner, signer.keyType, signer.key, signer.metadataType, signer.metadata, signer.deadline, signer.sig
8995
);
9096

9197
// Safety: i can be incremented unchecked since it is bound by signerParams.length.
9298
unchecked {
9399
++i;
94100
}
95101
}
96-
if (overpayment > 0) msg.sender.sendNative(overpayment);
97-
return fid;
98102
}
99103

100104
receive() external payable {

src/BundlerV1.sol

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.21;
3+
4+
import {IBundlerV1} from "./interfaces/IBundlerV1.sol";
5+
import {IIdGateway} from "./interfaces/IIdGateway.sol";
6+
import {IKeyGateway} from "./interfaces/IKeyGateway.sol";
7+
import {TransferHelper} from "./libraries/TransferHelper.sol";
8+
9+
/**
10+
* @title Farcaster Bundler
11+
*
12+
* @notice See https://github.com/farcasterxyz/contracts/blob/v3.1.0/docs/docs.md for an overview.
13+
*
14+
* @custom:security-contact security@merklemanufactory.com
15+
*/
16+
contract BundlerV1 is IBundlerV1 {
17+
using TransferHelper for address;
18+
19+
/*//////////////////////////////////////////////////////////////
20+
CONSTANTS
21+
//////////////////////////////////////////////////////////////*/
22+
23+
/**
24+
* @inheritdoc IBundlerV1
25+
*/
26+
string public constant VERSION = "2023.11.15";
27+
28+
/*//////////////////////////////////////////////////////////////
29+
IMMUTABLES
30+
//////////////////////////////////////////////////////////////*/
31+
32+
/**
33+
* @inheritdoc IBundlerV1
34+
*/
35+
IIdGateway public immutable idGateway;
36+
37+
/**
38+
* @inheritdoc IBundlerV1
39+
*/
40+
IKeyGateway public immutable keyGateway;
41+
42+
/*//////////////////////////////////////////////////////////////
43+
CONSTRUCTOR
44+
//////////////////////////////////////////////////////////////*/
45+
46+
/**
47+
* @notice Configure the addresses of the IdGateway and KeyGateway contracts.
48+
*
49+
* @param _idGateway Address of the IdGateway contract
50+
* @param _keyGateway Address of the KeyGateway contract
51+
*/
52+
constructor(address _idGateway, address _keyGateway) {
53+
idGateway = IIdGateway(payable(_idGateway));
54+
keyGateway = IKeyGateway(payable(_keyGateway));
55+
}
56+
57+
/**
58+
* @inheritdoc IBundlerV1
59+
*/
60+
function price(
61+
uint256 extraStorage
62+
) external view returns (uint256) {
63+
return idGateway.price(extraStorage);
64+
}
65+
66+
/**
67+
* @inheritdoc IBundlerV1
68+
*/
69+
function register(
70+
RegistrationParams calldata registerParams,
71+
SignerParams[] calldata signerParams,
72+
uint256 extraStorage
73+
) external payable returns (uint256) {
74+
(uint256 fid, uint256 overpayment) = idGateway.registerFor{value: msg.value}(
75+
registerParams.to, registerParams.recovery, registerParams.deadline, registerParams.sig, extraStorage
76+
);
77+
78+
uint256 signersLen = signerParams.length;
79+
for (uint256 i; i < signersLen;) {
80+
SignerParams calldata signer = signerParams[i];
81+
keyGateway.addFor(
82+
registerParams.to,
83+
signer.keyType,
84+
signer.key,
85+
signer.metadataType,
86+
signer.metadata,
87+
signer.deadline,
88+
signer.sig
89+
);
90+
91+
// Safety: i can be incremented unchecked since it is bound by signerParams.length.
92+
unchecked {
93+
++i;
94+
}
95+
}
96+
if (overpayment > 0) msg.sender.sendNative(overpayment);
97+
return fid;
98+
}
99+
100+
receive() external payable {
101+
if (msg.sender != address(idGateway)) revert Unauthorized();
102+
}
103+
}

src/interfaces/IBundler.sol

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,14 @@ interface IBundler {
8989
SignerParams[] calldata signerParams,
9090
uint256 extraStorage
9191
) external payable returns (uint256 fid);
92+
93+
/**
94+
* @notice Add multiple keys in a single transaction.
95+
*
96+
* @param fidOwner The fid owner address.
97+
* @param signerParams Array of structs containing signer parameters: keyType, key, metadataType,
98+
* metadata, deadline, and signature.
99+
*
100+
*/
101+
function addKeys(address fidOwner, SignerParams[] calldata signerParams) external;
92102
}

0 commit comments

Comments
 (0)