Skip to content

Commit 2edf9c7

Browse files
committed
fix bug commit from IndirectCall items not having access control + make bridge NFTs
1 parent 3c1cf8c commit 2edf9c7

File tree

3 files changed

+112
-168
lines changed

3 files changed

+112
-168
lines changed

contracts/crosschain/ERC7802Bridge.sol

Lines changed: 104 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,72 +8,106 @@ import {IERC7802} from "@openzeppelin/contracts/interfaces/draft-IERC7802.sol";
88
import {IERC7786GatewaySource, IERC7786Receiver} from "../interfaces/IERC7786.sol";
99

1010
// Utilities
11+
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
12+
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
1113
import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol";
1214
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
1315
import {Bytes} from "@openzeppelin/contracts/utils/Bytes.sol";
1416
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
1517
import {InteroperableAddress} from "@openzeppelin/contracts/utils/draft-InteroperableAddress.sol";
16-
import {IndirectCall} from "../utils/IndirectCall.sol";
1718

18-
contract ERC7802Bridge is IERC7786Receiver {
19+
contract ERC7802BridgeSatellite {
20+
address private immutable _bridge = msg.sender;
21+
22+
fallback(bytes calldata data) external payable returns (bytes memory) {
23+
require(msg.sender == _bridge && data.length >= 20);
24+
25+
(bool success, bytes memory returndata) = address(bytes20(data)).call{value: msg.value}(data[20:]);
26+
27+
if (!success) {
28+
assembly ("memory-safe") {
29+
revert(add(returndata, 0x20), mload(returndata))
30+
}
31+
} else {
32+
return returndata;
33+
}
34+
}
35+
}
36+
37+
contract ERC7802Bridge is ERC721("ERC7802Bridge", "ERC7802Bridge"), IERC7786Receiver {
1938
using BitMaps for BitMaps.BitMap;
2039
using InteroperableAddress for bytes;
21-
2240
struct BridgeMetadata {
2341
address token;
42+
bool isPaused;
2443
bool isCustodial;
2544
mapping(bytes chain => address) gateway;
2645
mapping(bytes chain => bytes) remote;
2746
}
2847

48+
address private immutable _satellite = address(new ERC7802BridgeSatellite());
2949
mapping(bytes32 bridgeId => BridgeMetadata) private _bridges;
3050
BitMaps.BitMap private _processed;
3151

3252
event Sent(address token, address from, bytes to, uint256 amount);
3353
event Received(address token, bytes from, address to, uint256 amount);
34-
event NewBridge(bytes32 indexed bridgeId, address indexed token);
35-
event NewBridgeLink(bytes32 indexed bridgeId, address gateway, bytes remote);
54+
event BridgePaused(bytes32 indexed bridgeId, bool isPaused);
55+
event BridgeLinkSet(bytes32 indexed bridgeId, address gateway, bytes remote);
3656

57+
error ERC7802BridgePaused(bytes32 bridgeId);
3758
error ERC7802BridgeInvalidBidgeId(bytes32 bridgeId);
3859
error ERC7802BridgeMissingGateway(bytes32 bridgeId, bytes chain);
3960
error ERC7802BridgeMissingRemote(bytes32 bridgeId, bytes chain);
4061
error ERC7802BridgeDuplicate();
4162
error ERC7802BridgeInvalidGateway();
4263
error ERC7802BridgeInvalidSender();
4364

44-
function getBridgeEndpoint(bytes32 bridgeId) public returns (address) {
45-
return IndirectCall.getRelayer(bridgeId);
65+
modifier bridgeAdminRestricted(bytes32 bridgeId) {
66+
_checkAuthorized(ownerOf(uint256(bridgeId)), msg.sender, uint256(bridgeId));
67+
_;
68+
}
69+
70+
// ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
71+
// │ Getters │
72+
// └─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
73+
function getBridgeEndpoint(bytes32 bridgeId) public view returns (address) {
74+
return Clones.predictDeterministicAddress(_satellite, bridgeId);
4675
}
4776

4877
function getBridgeToken(bytes32 bridgeId) public view returns (address token, bool isCustodial) {
49-
token = _bridges[bridgeId].token;
50-
isCustodial = _bridges[bridgeId].isCustodial;
51-
if (token == address(0)) revert ERC7802BridgeInvalidBidgeId(bridgeId);
78+
_requireOwned(uint256(bridgeId));
79+
return (_bridges[bridgeId].token, _bridges[bridgeId].isCustodial);
5280
}
5381

5482
function getBridgeGateway(bytes32 bridgeId, bytes memory chain) public view returns (address) {
55-
address result = _bridges[bridgeId].gateway[chain];
56-
if (result == address(0)) revert ERC7802BridgeMissingGateway(bridgeId, chain);
57-
return result;
83+
_requireOwned(uint256(bridgeId));
84+
return _bridges[bridgeId].gateway[chain];
5885
}
5986

6087
function getBridgeRemote(bytes32 bridgeId, bytes memory chain) public view returns (bytes memory) {
61-
bytes memory result = _bridges[bridgeId].remote[chain];
62-
if (result.length == 0) revert ERC7802BridgeMissingRemote(bridgeId, chain);
63-
return result;
88+
_requireOwned(uint256(bridgeId));
89+
return _bridges[bridgeId].remote[chain];
6490
}
6591

92+
// ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
93+
// │ Bridge creation and administration │
94+
// └─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
6695
struct Foreign {
6796
bytes32 id;
6897
address gateway;
6998
bytes remote;
7099
}
71-
function createBridge(address token, bool isCustodial, Foreign[] calldata foreign) public returns (bytes32) {
100+
101+
function createBridge(
102+
address token,
103+
address admin,
104+
bool isCustodial,
105+
Foreign[] calldata foreign
106+
) public returns (bytes32) {
72107
bytes32[] memory ids = new bytes32[](foreign.length + 1);
73108
bytes32[] memory links = new bytes32[](foreign.length);
74109
for (uint256 i = 0; i < foreign.length; ++i) {
75-
require(foreign[i].gateway != address(0));
76-
require(foreign[i].remote.length > 0);
110+
require(foreign[i].gateway != address(0) && foreign[i].remote.length > 0);
77111
ids[i] = foreign[i].id;
78112
links[i] = keccak256(
79113
abi.encode(InteroperableAddress.formatEvmV1(block.chainid, foreign[i].gateway), foreign[i].remote)
@@ -82,7 +116,7 @@ contract ERC7802Bridge is IERC7786Receiver {
82116
ids[foreign.length] = keccak256(
83117
abi.encode(
84118
InteroperableAddress.formatEvmV1(block.chainid, token), // bytes token
85-
bytes32(SafeCast.toUint(isCustodial)), // bytes32 tokenOptions
119+
bytes32(bytes20(admin)) | bytes32(SafeCast.toUint(isCustodial)), // bytes32 tokenOptions
86120
Arrays.sort(links)
87121
)
88122
);
@@ -94,7 +128,8 @@ contract ERC7802Bridge is IERC7786Receiver {
94128
details.token = token;
95129
details.isCustodial = isCustodial;
96130

97-
emit NewBridge(bridgeId, token);
131+
Clones.cloneDeterministic(_satellite, bridgeId);
132+
_safeMint(admin == address(0) ? address(1) : admin, uint256(bridgeId));
98133

99134
for (uint256 i = 0; i < foreign.length; ++i) {
100135
(bytes2 chainType, bytes memory chainReference, ) = foreign[i].remote.parseV1();
@@ -103,27 +138,51 @@ contract ERC7802Bridge is IERC7786Receiver {
103138
details.gateway[chain] = foreign[i].gateway;
104139
details.remote[chain] = foreign[i].remote;
105140

106-
emit NewBridgeLink(bridgeId, foreign[i].gateway, foreign[i].remote);
141+
emit BridgeLinkSet(bridgeId, foreign[i].gateway, foreign[i].remote);
107142
}
108143

109144
return bridgeId;
110145
}
111146

147+
function setPaused(bytes32 bridgeId, bool isPaused) public bridgeAdminRestricted(bridgeId) {
148+
_bridges[bridgeId].isPaused = isPaused;
149+
emit BridgePaused(bridgeId, isPaused);
150+
}
151+
152+
function updateGateway(
153+
bytes32 bridgeId,
154+
bytes calldata chain,
155+
address gateway,
156+
bytes calldata remote
157+
) public bridgeAdminRestricted(bridgeId) {
158+
require(gateway != address(0) && remote.length > 0);
159+
_bridges[bridgeId].gateway[chain] = gateway;
160+
_bridges[bridgeId].remote[chain] = remote;
161+
emit BridgeLinkSet(bridgeId, gateway, remote);
162+
}
163+
164+
// ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
165+
// │ Send / Receive tokens │
166+
// └─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
112167
function send(
113168
bytes32 bridgeId,
114169
bytes memory to,
115170
uint256 amount,
116171
bytes[] memory attributes
117172
) public payable virtual returns (bytes32) {
173+
_requireOwned(uint256(bridgeId));
174+
175+
require(!_bridges[bridgeId].isPaused, ERC7802BridgePaused(bridgeId));
176+
118177
address token = _fetchTokens(bridgeId, msg.sender, amount);
119178

120179
// identify destination chain
121180
(bytes2 chainType, bytes memory chainReference, bytes memory recipient) = to.parseV1();
122181
bytes memory destChain = InteroperableAddress.formatV1(chainType, chainReference, "");
123182

124183
// get details for that bridge: gateway, remote bridge, remote token
125-
address gateway = getBridgeGateway(bridgeId, destChain);
126-
bytes memory bridge = getBridgeRemote(bridgeId, destChain);
184+
address gateway = _bridges[bridgeId].gateway[destChain];
185+
bytes memory bridge = _bridges[bridgeId].remote[destChain];
127186

128187
// prepare payload
129188
bytes memory payload = abi.encode(
@@ -156,12 +215,14 @@ contract ERC7802Bridge is IERC7786Receiver {
156215
(bytes32, bytes, bytes, uint256)
157216
);
158217

218+
_requireOwned(uint256(bridgeId));
219+
159220
// identify source chain and validate corresponding gateway
160221
(bytes2 chainType, bytes memory chainReference, ) = from.parseV1();
161222
bytes memory srcChain = InteroperableAddress.formatV1(chainType, chainReference, "");
162223

163-
require(msg.sender == getBridgeGateway(bridgeId, srcChain), ERC7802BridgeInvalidGateway());
164-
require(Bytes.equal(sender, getBridgeRemote(bridgeId, srcChain)), ERC7802BridgeInvalidSender());
224+
require(msg.sender == _bridges[bridgeId].gateway[srcChain], ERC7802BridgeInvalidGateway());
225+
require(Bytes.equal(sender, _bridges[bridgeId].remote[srcChain]), ERC7802BridgeInvalidSender());
165226

166227
// get recipient
167228
address to = address(bytes20(recipient));
@@ -173,40 +234,35 @@ contract ERC7802Bridge is IERC7786Receiver {
173234
return IERC7786Receiver.executeMessage.selector;
174235
}
175236

176-
function _fetchTokens(bytes32 bridgeId, address from, uint256 amount) internal virtual returns (address) {
177-
(address token, bool isCustodial) = getBridgeToken(bridgeId);
178-
if (isCustodial) {
179-
(bool success, bytes memory returndata) = IndirectCall.indirectCall(
180-
token,
181-
abi.encodeCall(IERC20.transferFrom, (from, getBridgeEndpoint(bridgeId), amount)),
182-
bridgeId
237+
function _fetchTokens(bytes32 bridgeId, address from, uint256 amount) private returns (address) {
238+
address token = _bridges[bridgeId].token;
239+
if (_bridges[bridgeId].isCustodial) {
240+
(bool success, bytes memory returndata) = getBridgeEndpoint(bridgeId).call(
241+
abi.encodePacked(
242+
token,
243+
abi.encodeCall(IERC20.transferFrom, (from, getBridgeEndpoint(bridgeId), amount))
244+
)
183245
);
184246
require(success && (returndata.length == 0 ? token.code.length == 0 : uint256(bytes32(returndata)) == 1));
185247
} else {
186-
(bool success, ) = IndirectCall.indirectCall(
187-
token,
188-
abi.encodeCall(IERC7802.crosschainBurn, (from, amount)),
189-
bridgeId
248+
(bool success, ) = getBridgeEndpoint(bridgeId).call(
249+
abi.encodePacked(token, abi.encodeCall(IERC7802.crosschainBurn, (from, amount)))
190250
);
191251
require(success);
192252
}
193253
return token;
194254
}
195255

196-
function _distributeTokens(bytes32 bridgeId, address to, uint256 amount) internal virtual returns (address) {
197-
(address token, bool isCustodial) = getBridgeToken(bridgeId);
198-
if (isCustodial) {
199-
(bool success, bytes memory returndata) = IndirectCall.indirectCall(
200-
token,
201-
abi.encodeCall(IERC20.transfer, (to, amount)),
202-
bridgeId
256+
function _distributeTokens(bytes32 bridgeId, address to, uint256 amount) private returns (address) {
257+
address token = _bridges[bridgeId].token;
258+
if (_bridges[bridgeId].isCustodial) {
259+
(bool success, bytes memory returndata) = getBridgeEndpoint(bridgeId).call(
260+
abi.encodePacked(token, abi.encodeCall(IERC20.transfer, (to, amount)))
203261
);
204262
require(success && (returndata.length == 0 ? token.code.length == 0 : uint256(bytes32(returndata)) == 1));
205263
} else {
206-
(bool success, ) = IndirectCall.indirectCall(
207-
token,
208-
abi.encodeCall(IERC7802.crosschainMint, (to, amount)),
209-
bridgeId
264+
(bool success, ) = getBridgeEndpoint(bridgeId).call(
265+
abi.encodePacked(token, abi.encodeCall(IERC7802.crosschainMint, (to, amount)))
210266
);
211267
require(success);
212268
}

contracts/utils/IndirectCall.sol

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)