Skip to content

Commit 094ae4f

Browse files
committed
modularise half-bridge creations
1 parent 86a9e05 commit 094ae4f

File tree

2 files changed

+97
-60
lines changed

2 files changed

+97
-60
lines changed

contracts/crosschain/ERC7802Bridge.sol

Lines changed: 95 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
1616
import {InteroperableAddress} from "@openzeppelin/contracts/utils/draft-InteroperableAddress.sol";
1717
import {IndirectCall} from "../utils/IndirectCall.sol";
1818

19-
contract ERC7802Bridge is ERC721("ERC7802Bridge", "ERC7802Bridge"), IERC7786Receiver {
19+
abstract contract ERC7802Bridge is ERC721("ERC7802Bridge", "ERC7802Bridge"), IERC7786Receiver {
2020
using BitMaps for BitMaps.BitMap;
2121
using InteroperableAddress for bytes;
2222

@@ -72,76 +72,39 @@ contract ERC7802Bridge is ERC721("ERC7802Bridge", "ERC7802Bridge"), IERC7786Rece
7272
}
7373

7474
// ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
75-
// │ Bridge creation and administration
75+
// │ Bridge management
7676
// └─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
77-
struct Foreign {
78-
bytes32 id;
79-
address gateway;
80-
bytes remote;
81-
}
82-
83-
function createBridge(
84-
address token,
85-
address admin,
86-
bool isCustodial,
87-
Foreign[] calldata foreign
88-
) public returns (bytes32) {
89-
bytes32[] memory ids = new bytes32[](foreign.length + 1);
90-
bytes32[] memory links = new bytes32[](foreign.length);
91-
for (uint256 i = 0; i < foreign.length; ++i) {
92-
require(foreign[i].gateway != address(0) && foreign[i].remote.length > 0);
93-
ids[i] = foreign[i].id;
94-
links[i] = keccak256(
95-
abi.encode(InteroperableAddress.formatEvmV1(block.chainid, foreign[i].gateway), foreign[i].remote)
96-
);
97-
}
98-
ids[foreign.length] = keccak256(
99-
abi.encode(
100-
InteroperableAddress.formatEvmV1(block.chainid, token), // bytes token
101-
bytes32(bytes20(admin)) | bytes32(SafeCast.toUint(isCustodial)), // bytes32 tokenOptions
102-
Arrays.sort(links)
103-
)
104-
);
105-
106-
bytes32 bridgeId = keccak256(abi.encodePacked(Arrays.sort(ids)));
107-
108-
// Should we check for collision. I don't think that is necessary
109-
BridgeMetadata storage details = _bridges[bridgeId];
110-
details.token = token;
111-
details.isCustodial = isCustodial;
112-
113-
_safeMint(admin == address(0) ? address(1) : admin, uint256(bridgeId));
114-
115-
for (uint256 i = 0; i < foreign.length; ++i) {
116-
(bytes2 chainType, bytes memory chainReference, ) = foreign[i].remote.parseV1();
117-
bytes memory chain = InteroperableAddress.formatV1(chainType, chainReference, "");
118-
require(details.gateway[chain] == address(0));
119-
details.gateway[chain] = foreign[i].gateway;
120-
details.remote[chain] = foreign[i].remote;
121-
122-
emit BridgeLinkSet(bridgeId, foreign[i].gateway, foreign[i].remote);
123-
}
124-
125-
return bridgeId;
126-
}
12777

12878
function setPaused(bytes32 bridgeId, bool isPaused) public bridgeAdminRestricted(bridgeId) {
129-
_bridges[bridgeId].isPaused = isPaused;
130-
emit BridgePaused(bridgeId, isPaused);
79+
_setPaused(bridgeId, isPaused);
13180
}
13281

13382
function updateGateway(
13483
bytes32 bridgeId,
135-
bytes calldata chain,
84+
bytes memory chain,
13685
address gateway,
137-
bytes calldata remote
138-
) public bridgeAdminRestricted(bridgeId) {
139-
require(gateway != address(0) && remote.length > 0);
86+
bytes memory remote
87+
) public virtual bridgeAdminRestricted(bridgeId) {
88+
_setGateway(bridgeId, chain, gateway, remote);
89+
}
90+
91+
function _setBridge(bytes32 bridgeId, address token, address admin, bool isCustodial) internal {
92+
_safeMint(admin == address(0) ? address(1) : admin, uint256(bridgeId));
93+
_bridges[bridgeId].token = token;
94+
_bridges[bridgeId].isCustodial = isCustodial;
95+
}
96+
97+
function _setGateway(bytes32 bridgeId, bytes memory chain, address gateway, bytes memory remote) internal {
14098
_bridges[bridgeId].gateway[chain] = gateway;
14199
_bridges[bridgeId].remote[chain] = remote;
142100
emit BridgeLinkSet(bridgeId, gateway, remote);
143101
}
144102

103+
function _setPaused(bytes32 bridgeId, bool isPaused) internal {
104+
_bridges[bridgeId].isPaused = isPaused;
105+
emit BridgePaused(bridgeId, isPaused);
106+
}
107+
145108
// ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
146109
// │ Send / Receive tokens │
147110
// └─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
@@ -254,3 +217,77 @@ contract ERC7802Bridge is ERC721("ERC7802Bridge", "ERC7802Bridge"), IERC7786Rece
254217
return token;
255218
}
256219
}
220+
221+
contract ERC7802BridgeLinks is ERC7802Bridge {
222+
function createBridge(address token, address admin, bool isCustodial, bytes32 salt) public returns (bytes32) {
223+
bytes32 bridgeId = keccak256(abi.encodePacked(msg.sender, salt));
224+
225+
_setBridge(bridgeId, token, admin, isCustodial);
226+
227+
return bridgeId;
228+
}
229+
}
230+
231+
contract ERC7802BridgeCounterfactual is ERC7802Bridge {
232+
using InteroperableAddress for bytes;
233+
234+
struct Foreign {
235+
bytes32 id;
236+
address gateway;
237+
bytes remote;
238+
}
239+
240+
function createBridge(
241+
address token,
242+
address admin,
243+
bool isCustodial,
244+
Foreign[] calldata foreign
245+
) public returns (bytes32) {
246+
bytes32 bridgeId = _counterfactualBridgeId(
247+
token,
248+
bytes32(bytes20(admin)) | bytes32(SafeCast.toUint(isCustodial)),
249+
foreign
250+
);
251+
252+
_setBridge(bridgeId, token, admin, isCustodial);
253+
for (uint256 i = 0; i < foreign.length; ++i) {
254+
(bytes2 chainType, bytes memory chainReference, ) = foreign[i].remote.parseV1Calldata();
255+
bytes memory chain = InteroperableAddress.formatV1(chainType, chainReference, "");
256+
_setGateway(bridgeId, chain, foreign[i].gateway, foreign[i].remote);
257+
}
258+
259+
return bridgeId;
260+
}
261+
262+
function updateGateway(
263+
bytes32 bridgeId,
264+
bytes memory chain,
265+
address gateway,
266+
bytes memory remote
267+
) public virtual override {
268+
require(gateway != address(0) && remote.length > 0);
269+
// super call is bridgeAdminRestricted(bridgeId)
270+
super.updateGateway(bridgeId, chain, gateway, remote);
271+
}
272+
273+
function _counterfactualBridgeId(
274+
address token,
275+
bytes32 opts,
276+
Foreign[] calldata foreign
277+
) private view returns (bytes32) {
278+
bytes32[] memory ids = new bytes32[](foreign.length + 1);
279+
bytes32[] memory links = new bytes32[](foreign.length);
280+
for (uint256 i = 0; i < foreign.length; ++i) {
281+
require(foreign[i].gateway != address(0) && foreign[i].remote.length > 0);
282+
ids[i] = foreign[i].id;
283+
links[i] = keccak256(
284+
abi.encode(InteroperableAddress.formatEvmV1(block.chainid, foreign[i].gateway), foreign[i].remote)
285+
);
286+
}
287+
ids[foreign.length] = keccak256(
288+
abi.encode(InteroperableAddress.formatEvmV1(block.chainid, token), opts, Arrays.sort(links))
289+
);
290+
291+
return keccak256(abi.encodePacked(Arrays.sort(ids)));
292+
}
293+
}

test/crosschain/ERC7802Bridge.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ async function fixture() {
3636
const { chain, gatewayA, gatewayB } = await AxelarHelper.deploy(admin);
3737

3838
// On chain A, we have a "legacy" token
39-
const bridgeA = await ethers.deployContract('$ERC7802Bridge');
39+
const bridgeA = await ethers.deployContract('$ERC7802BridgeCounterfactual');
4040
const tokenA = await ethers.deployContract('$ERC20', ['Token A', 'TA']);
4141

4242
// On chain B we have a bridgeable token
43-
const bridgeB = await ethers.deployContract('$ERC7802Bridge');
43+
const bridgeB = await ethers.deployContract('$ERC7802BridgeCounterfactual');
4444
const tokenB = await ethers.deployContract('$ERC20BridgeableMock', ['Token B', 'TB', admin]);
4545

4646
// Compute bridge identifier and local hashes

0 commit comments

Comments
 (0)