Skip to content

Commit 9bf6080

Browse files
committed
Make ISM verification upgradable + adjust deploy script
1 parent 50a094e commit 9bf6080

6 files changed

Lines changed: 208 additions & 155 deletions

File tree

base/script/Deploy.s.sol

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Bridge} from "../src/Bridge.sol";
1111

1212
import {CrossChainERC20} from "../src/CrossChainERC20.sol";
1313
import {CrossChainERC20Factory} from "../src/CrossChainERC20Factory.sol";
14+
import {ISMVerification} from "../src/ISMVerification.sol";
1415
import {Twin} from "../src/Twin.sol";
1516
import {HelperConfig} from "./HelperConfig.s.sol";
1617

@@ -27,20 +28,28 @@ contract DeployScript is Script {
2728

2829
vm.startBroadcast(msg.sender);
2930
address twinBeacon = _deployTwinBeacon({cfg: cfg, precomputedBridgeAddress: precomputedBridgeAddress});
31+
address ismVerification = _deployISMVerification({cfg: cfg});
3032
address factory = _deployFactory({cfg: cfg, precomputedBridgeAddress: precomputedBridgeAddress});
31-
address bridge = _deployBridge({cfg: cfg, twinBeacon: twinBeacon, crossChainErc20Factory: factory});
33+
address bridge = _deployBridge({
34+
cfg: cfg,
35+
twinBeacon: twinBeacon,
36+
ismVerification: ismVerification,
37+
crossChainErc20Factory: factory
38+
});
3239
vm.stopBroadcast();
3340

3441
require(address(bridge) == precomputedBridgeAddress, "Bridge address mismatch");
3542

3643
console.log("Deployed TwinBeacon at: %s", twinBeacon);
44+
console.log("Deployed ISMVerification at: %s", ismVerification);
3745
console.log("Deployed Bridge at: %s", bridge);
3846
console.log("Deployed CrossChainERC20Factory at: %s", factory);
3947

4048
string memory obj = "root";
4149
string memory json = vm.serializeAddress({objectKey: obj, valueKey: "Bridge", value: bridge});
4250
json = vm.serializeAddress({objectKey: obj, valueKey: "CrossChainERC20Factory", value: factory});
4351
json = vm.serializeAddress({objectKey: obj, valueKey: "Twin", value: twinBeacon});
52+
json = vm.serializeAddress({objectKey: obj, valueKey: "ISMVerification", value: ismVerification});
4453
vm.writeJson(json, string.concat("deployments/", chain.chainAlias, ".json"));
4554

4655
return (Twin(payable(twinBeacon)), Bridge(bridge), CrossChainERC20Factory(factory), helperConfig);
@@ -54,14 +63,31 @@ contract DeployScript is Script {
5463
return address(new UpgradeableBeacon({initialOwner: cfg.initialOwner, initialImplementation: twinImpl}));
5564
}
5665

57-
function _deployBridge(HelperConfig.NetworkConfig memory cfg, address twinBeacon, address crossChainErc20Factory)
58-
private
59-
returns (address)
60-
{
66+
function _deployISMVerification(HelperConfig.NetworkConfig memory cfg) private returns (address) {
67+
ISMVerification ismImpl = new ISMVerification({
68+
_validators: cfg.initialValidators,
69+
_threshold: cfg.initialThreshold,
70+
_owner: cfg.initialOwner
71+
});
72+
73+
return ERC1967Factory(cfg.erc1967Factory).deployDeterministic({
74+
implementation: address(ismImpl),
75+
admin: cfg.initialOwner,
76+
salt: _salt("ism_verif")
77+
});
78+
}
79+
80+
function _deployBridge(
81+
HelperConfig.NetworkConfig memory cfg,
82+
address twinBeacon,
83+
address ismVerification,
84+
address crossChainErc20Factory
85+
) private returns (address) {
6186
Bridge bridgeImpl = new Bridge({
6287
remoteBridge: cfg.remoteBridge,
6388
trustedRelayer: cfg.trustedRelayer,
6489
twinBeacon: twinBeacon,
90+
ismVerification: ismVerification,
6591
crossChainErc20Factory: crossChainErc20Factory
6692
});
6793

base/script/HelperConfig.s.sol

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ contract HelperConfig is Script {
1414
Pubkey remoteBridge;
1515
address trustedRelayer;
1616
address erc1967Factory;
17+
address[] initialValidators;
18+
uint256 initialThreshold;
1719
}
1820

1921
NetworkConfig private _activeNetworkConfig;
@@ -46,11 +48,18 @@ contract HelperConfig is Script {
4648
// });
4749

4850
// Public version
51+
address[] memory validators = new address[](3);
52+
validators[0] = 0x20624CA8d0dF80B8bd67C25Bc19A9E10AfB67733;
53+
validators[1] = 0x2880a6DcC8c87dD2874bCBB9ad7E627a407Cf3C2;
54+
validators[2] = 0x0e9a877906EBc3b7098DA2404412BF0Ed1A5EFb4;
55+
4956
return NetworkConfig({
5057
initialOwner: 0x20624CA8d0dF80B8bd67C25Bc19A9E10AfB67733,
5158
remoteBridge: Pubkey.wrap(0x9379502b8fd1d9f6feee747094a08cd0f9b79fbbc7e51a36e2da237ee1506460), // AvgDrHpWUeV7fpZYVhDQbWrV2sD7zp9zDB7w97CWknKH
5259
trustedRelayer: 0x2880a6DcC8c87dD2874bCBB9ad7E627a407Cf3C2,
53-
erc1967Factory: ERC1967FactoryConstants.ADDRESS
60+
erc1967Factory: ERC1967FactoryConstants.ADDRESS,
61+
initialValidators: validators,
62+
initialThreshold: 2
5463
});
5564
}
5665

@@ -61,11 +70,18 @@ contract HelperConfig is Script {
6170

6271
ERC1967Factory f = new ERC1967Factory();
6372

73+
address[] memory validators = new address[](3);
74+
validators[0] = makeAddr("validator1");
75+
validators[1] = makeAddr("validator2");
76+
validators[2] = makeAddr("validator3");
77+
6478
return NetworkConfig({
6579
initialOwner: makeAddr("initialOwner"),
6680
remoteBridge: Pubkey.wrap(0xc4c16980efe2a570c1a7599fd2ebb40ca7f85daf897482b9c85d4b8933a61608),
6781
trustedRelayer: makeAddr("trustedRelayer"),
68-
erc1967Factory: address(f)
82+
erc1967Factory: address(f),
83+
initialValidators: validators,
84+
initialThreshold: 2
6985
});
7086
}
7187
}

base/script/actions/CreateToken.s.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,12 @@ contract CreateTokenScript is Script {
3636

3737
function run() public {
3838
vm.startBroadcast();
39-
address token =
40-
crossChainERC20Factory.deploy({remoteToken: REMOTE_TOKEN, name: tokenName, symbol: tokenSymbol, decimals: 9});
39+
address token = crossChainERC20Factory.deploy({
40+
remoteToken: REMOTE_TOKEN,
41+
name: tokenName,
42+
symbol: tokenSymbol,
43+
decimals: 9
44+
});
4145
console.log("Deployed Token at: %s", token);
4246
vm.stopBroadcast();
4347

base/src/Bridge.sol

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import {SVMBridgeLib} from "./libraries/SVMBridgeLib.sol";
1212
import {Ix, Pubkey} from "./libraries/SVMLib.sol";
1313
import {SolanaTokenType, TokenLib, Transfer} from "./libraries/TokenLib.sol";
1414

15-
import {Twin} from "./Twin.sol";
1615
import {ISMVerification} from "./ISMVerification.sol";
16+
import {Twin} from "./Twin.sol";
1717

1818
/// @title Bridge
1919
///
@@ -93,6 +93,9 @@ contract Bridge is ReentrancyGuardTransient {
9393
/// @notice Address of the Twin beacon.
9494
address public immutable TWIN_BEACON;
9595

96+
/// @notice Address of the ISM verification contract.
97+
address public immutable ISM_VERIFICATION;
98+
9699
/// @notice Address of the CrossChainERC20Factory.
97100
address public immutable CROSS_CHAIN_ERC20_FACTORY;
98101

@@ -147,9 +150,6 @@ contract Bridge is ReentrancyGuardTransient {
147150
/// @notice The nonce used for the next incoming message relayed.
148151
uint64 public nextIncomingNonce;
149152

150-
/// @notice The ISM verification contract address.
151-
address public ismVerification;
152-
153153
//////////////////////////////////////////////////////////////
154154
/// Public Functions ///
155155
//////////////////////////////////////////////////////////////
@@ -159,11 +159,19 @@ contract Bridge is ReentrancyGuardTransient {
159159
/// @param remoteBridge The pubkey of the remote bridge on Solana.
160160
/// @param trustedRelayer The address of the trusted relayer.
161161
/// @param twinBeacon The address of the Twin beacon.
162+
/// @param ismVerification The address of the ISM verification contract.
162163
/// @param crossChainErc20Factory The address of the CrossChainERC20Factory.
163-
constructor(Pubkey remoteBridge, address trustedRelayer, address twinBeacon, address crossChainErc20Factory) {
164+
constructor(
165+
Pubkey remoteBridge,
166+
address trustedRelayer,
167+
address twinBeacon,
168+
address ismVerification,
169+
address crossChainErc20Factory
170+
) {
164171
REMOTE_BRIDGE = remoteBridge;
165172
TRUSTED_RELAYER = trustedRelayer;
166173
TWIN_BEACON = twinBeacon;
174+
ISM_VERIFICATION = ismVerification;
167175
CROSS_CHAIN_ERC20_FACTORY = crossChainErc20Factory;
168176
}
169177

@@ -260,7 +268,7 @@ contract Bridge is ReentrancyGuardTransient {
260268
function relayMessages(IncomingMessage[] calldata messages, bytes calldata ismData) external nonReentrant {
261269
bool isTrustedRelayer = msg.sender == TRUSTED_RELAYER;
262270
if (isTrustedRelayer) {
263-
ISMVerification(ismVerification).verifyISM({messages: messages, ismData: ismData});
271+
ISMVerification(ISM_VERIFICATION).verifyISM({messages: messages, ismData: ismData});
264272
}
265273

266274
for (uint256 i; i < messages.length; i++) {

base/src/ISMVerification.sol

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity 0.8.28;
33

4-
import {Ownable} from "solady/auth/Ownable.sol";
54
import {IncomingMessage} from "./libraries/MessageLib.sol";
5+
import {Ownable} from "solady/auth/Ownable.sol";
66

77
contract ISMVerification is Ownable {
88
//////////////////////////////////////////////////////////////
@@ -60,7 +60,9 @@ contract ISMVerification is Ownable {
6060

6161
/// @notice Constructs the ISMVerification contract.
6262
///
63+
/// @param _validators Array of validator addresses.
6364
/// @param _threshold The ISM verification threshold.
65+
/// @param _owner The owner of the contract.
6466
constructor(address[] memory _validators, uint256 _threshold, address _owner) {
6567
require(_threshold > 0 && _threshold <= _validators.length, "Invalid threshold");
6668

@@ -69,7 +71,7 @@ contract ISMVerification is Ownable {
6971
}
7072
validatorCount = _validators.length;
7173
threshold = _threshold;
72-
74+
7375
_initializeOwner(_owner);
7476
}
7577

@@ -81,7 +83,7 @@ contract ISMVerification is Ownable {
8183
/// @return True if the ISM is verified, false otherwise.
8284
function verifyISM(IncomingMessage[] memory messages, bytes calldata ismData) public view returns (bool) {
8385
require(threshold > 0, ThresholdIsZero());
84-
86+
8587
// Decode only signatures (addresses recovered from signatures)
8688
(bytes memory signatures) = abi.decode(ismData, (bytes));
8789

@@ -90,36 +92,36 @@ contract ISMVerification is Ownable {
9092

9193
// Check that the provided signature data is not too short
9294
require(signatures.length >= threshold * 65, InvalidSignatureLength());
93-
95+
9496
// There cannot be a validator with address 0
9597
address lastValidator = address(0);
96-
98+
9799
// Verify M-of-N signatures
98100
for (uint256 i = 0; i < threshold; i++) {
99101
(uint8 v, bytes32 r, bytes32 s) = signatureSplit(signatures, i);
100-
102+
101103
// Standard ECDSA signature recovery
102104
address currentValidator = ecrecover(messageHash, v, r, s);
103-
105+
104106
// Verify recovered address is valid
105107
require(currentValidator != address(0), InvalidSigner());
106-
108+
107109
// Check for duplicate signers
108110
if (currentValidator == lastValidator) {
109111
revert DuplicateSigner();
110112
}
111-
113+
112114
// Ensure ascending order
113115
if (currentValidator < lastValidator) {
114116
revert InvalidSignatureOrder();
115117
}
116-
118+
117119
// Verify signer is a registered validator
118120
require(validators[currentValidator], SignerNotValidator());
119-
121+
120122
lastValidator = currentValidator;
121123
}
122-
124+
123125
return true;
124126
}
125127

@@ -128,10 +130,10 @@ contract ISMVerification is Ownable {
128130
* @param signatures Concatenated signatures
129131
* @param pos Position of signature to split (0-indexed)
130132
*/
131-
function signatureSplit(bytes memory signatures, uint256 pos)
132-
internal
133-
pure
134-
returns (uint8 v, bytes32 r, bytes32 s)
133+
function signatureSplit(bytes memory signatures, uint256 pos)
134+
internal
135+
pure
136+
returns (uint8 v, bytes32 r, bytes32 s)
135137
{
136138
assembly {
137139
let signaturePos := mul(0x41, pos)
@@ -168,4 +170,4 @@ contract ISMVerification is Ownable {
168170
validators[validator] = false;
169171
validatorCount--;
170172
}
171-
}
173+
}

0 commit comments

Comments
 (0)