@across-protocol/contracts 5.0.6-alpha.0 → 5.0.6-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/contracts/test/AcrossMessageHandlerMock.sol +14 -0
- package/contracts/test/ArbitrumMocks.sol +169 -0
- package/contracts/test/ExpandedERC20WithBlacklist.sol +19 -0
- package/contracts/test/LineaMocks.sol +83 -0
- package/contracts/test/MerkleLibTest.sol +65 -0
- package/contracts/test/MockBedrockStandardBridge.sol +115 -0
- package/contracts/test/MockBlastUsdYieldManager.sol +20 -0
- package/contracts/test/MockCCTP.sol +212 -0
- package/contracts/test/MockCaller.sol +20 -0
- package/contracts/test/MockERC1271.sol +21 -0
- package/contracts/test/MockERC20.sol +87 -0
- package/contracts/test/MockEndpoint.sol +16 -0
- package/contracts/test/MockHubPool.sol +100 -0
- package/contracts/test/MockOFTMessenger.sol +88 -0
- package/contracts/test/MockOptimism_SpokePool.sol +22 -0
- package/contracts/test/MockPermit2.sol +188 -0
- package/contracts/test/MockSpokePool.sol +263 -0
- package/contracts/test/MockSpokePoolV2.sol +27 -0
- package/contracts/test/MockZkStackBridgeHub.sol +43 -0
- package/contracts/test/PolygonERC20Test.sol +16 -0
- package/contracts/test/PolygonMocks.sol +82 -0
- package/contracts/test/PolygonZkEVMMocks.sol +72 -0
- package/contracts/test/ScrollMocks.sol +109 -0
- package/contracts/test/V2MerkleLib.sol +18 -0
- package/contracts/test/ZkSyncMocks.sol +78 -0
- package/contracts/test/interfaces/IHyperCoreFlowExecutor.sol +189 -0
- package/contracts/test/interfaces/MockV2SpokePoolInterface.sol +38 -0
- package/package.json +3 -3
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
//SPDX-License-Identifier: Unlicense
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts-upgradeable-v4/access/OwnableUpgradeable.sol";
|
|
5
|
+
import "../spoke-pools/SpokePool.sol";
|
|
6
|
+
import "./interfaces/MockV2SpokePoolInterface.sol";
|
|
7
|
+
import "./V2MerkleLib.sol";
|
|
8
|
+
import { AddressToBytes32, Bytes32ToAddress } from "../libraries/AddressConverters.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @title MockSpokePool
|
|
12
|
+
* @notice Implements abstract contract for testing.
|
|
13
|
+
*/
|
|
14
|
+
contract MockSpokePool is SpokePool, MockV2SpokePoolInterface, OwnableUpgradeable {
|
|
15
|
+
using SafeERC20Upgradeable for IERC20Upgradeable;
|
|
16
|
+
using AddressToBytes32 for address;
|
|
17
|
+
using Bytes32ToAddress for bytes32;
|
|
18
|
+
|
|
19
|
+
uint256 private chainId_;
|
|
20
|
+
uint256 private currentTime;
|
|
21
|
+
mapping(bytes32 => uint256) private relayFills;
|
|
22
|
+
|
|
23
|
+
uint256 public constant SLOW_FILL_MAX_TOKENS_TO_SEND = 1e40;
|
|
24
|
+
|
|
25
|
+
bytes32 public constant UPDATE_DEPOSIT_DETAILS_HASH =
|
|
26
|
+
keccak256(
|
|
27
|
+
"UpdateDepositDetails(uint256 depositId,uint256 originChainId,int64 updatedRelayerFeePct,address updatedRecipient,bytes updatedMessage)"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
event BridgedToHubPool(uint256 amount, address token);
|
|
31
|
+
event PreLeafExecuteHook(bytes32 token);
|
|
32
|
+
|
|
33
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
34
|
+
constructor(address _wrappedNativeTokenAddress) SpokePool(_wrappedNativeTokenAddress, 1 hours, 9 hours, 0, 0) {} // solhint-disable-line no-empty-blocks
|
|
35
|
+
|
|
36
|
+
function initialize(uint32 _initialDepositId, address _crossDomainAdmin, address _hubPool) public initializer {
|
|
37
|
+
__Ownable_init();
|
|
38
|
+
__SpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool);
|
|
39
|
+
currentTime = block.timestamp; // solhint-disable-line not-rely-on-time
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function setCurrentTime(uint256 time) external {
|
|
43
|
+
currentTime = time;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function distributeRelayerRefunds(
|
|
47
|
+
uint256 _chainId,
|
|
48
|
+
uint256 amountToReturn,
|
|
49
|
+
uint256[] memory refundAmounts,
|
|
50
|
+
uint32 leafId,
|
|
51
|
+
address l2TokenAddress,
|
|
52
|
+
address[] memory refundAddresses
|
|
53
|
+
) external {
|
|
54
|
+
_distributeRelayerRefunds(_chainId, amountToReturn, refundAmounts, leafId, l2TokenAddress, refundAddresses);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _verifyUpdateDepositMessage(
|
|
58
|
+
address depositor,
|
|
59
|
+
uint256 depositId,
|
|
60
|
+
uint256 originChainId,
|
|
61
|
+
int64 updatedRelayerFeePct,
|
|
62
|
+
bytes32 updatedRecipient,
|
|
63
|
+
bytes memory updatedMessage,
|
|
64
|
+
bytes memory depositorSignature
|
|
65
|
+
) internal view {
|
|
66
|
+
bytes32 expectedTypedDataV4Hash = _hashTypedDataV4(
|
|
67
|
+
// EIP-712 compliant hash struct: https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct
|
|
68
|
+
keccak256(
|
|
69
|
+
abi.encode(
|
|
70
|
+
UPDATE_DEPOSIT_DETAILS_HASH,
|
|
71
|
+
depositId,
|
|
72
|
+
originChainId,
|
|
73
|
+
updatedRelayerFeePct,
|
|
74
|
+
updatedRecipient,
|
|
75
|
+
keccak256(updatedMessage)
|
|
76
|
+
)
|
|
77
|
+
),
|
|
78
|
+
// By passing in the origin chain id, we enable the verification of the signature on a different chain
|
|
79
|
+
originChainId
|
|
80
|
+
);
|
|
81
|
+
_verifyDepositorSignature(depositor, expectedTypedDataV4Hash, depositorSignature);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function verifyUpdateV3DepositMessageBytes32(
|
|
85
|
+
bytes32 depositor,
|
|
86
|
+
uint256 depositId,
|
|
87
|
+
uint256 originChainId,
|
|
88
|
+
uint256 updatedOutputAmount,
|
|
89
|
+
bytes32 updatedRecipient,
|
|
90
|
+
bytes memory updatedMessage,
|
|
91
|
+
bytes memory depositorSignature
|
|
92
|
+
) public view {
|
|
93
|
+
return
|
|
94
|
+
_verifyUpdateV3DepositMessage(
|
|
95
|
+
depositor.toAddress(),
|
|
96
|
+
depositId,
|
|
97
|
+
originChainId,
|
|
98
|
+
updatedOutputAmount,
|
|
99
|
+
updatedRecipient,
|
|
100
|
+
updatedMessage,
|
|
101
|
+
depositorSignature,
|
|
102
|
+
UPDATE_BYTES32_DEPOSIT_DETAILS_HASH
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function verifyUpdateV3DepositMessage(
|
|
107
|
+
address depositor,
|
|
108
|
+
uint256 depositId,
|
|
109
|
+
uint256 originChainId,
|
|
110
|
+
uint256 updatedOutputAmount,
|
|
111
|
+
address updatedRecipient,
|
|
112
|
+
bytes memory updatedMessage,
|
|
113
|
+
bytes memory depositorSignature
|
|
114
|
+
) public view {
|
|
115
|
+
return
|
|
116
|
+
_verifyUpdateV3DepositMessage(
|
|
117
|
+
depositor,
|
|
118
|
+
depositId,
|
|
119
|
+
originChainId,
|
|
120
|
+
updatedOutputAmount,
|
|
121
|
+
updatedRecipient.toBytes32(),
|
|
122
|
+
updatedMessage,
|
|
123
|
+
depositorSignature,
|
|
124
|
+
UPDATE_BYTES32_DEPOSIT_DETAILS_HASH
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function fillRelayV3Internal(
|
|
129
|
+
V3RelayExecutionParams memory relayExecution,
|
|
130
|
+
bytes32 relayer,
|
|
131
|
+
bool isSlowFill
|
|
132
|
+
) external {
|
|
133
|
+
_fillRelayV3(relayExecution, relayer, isSlowFill);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// This function is nonReentrant in order to allow caller to test whether a different function
|
|
137
|
+
// is reentrancy protected or not.
|
|
138
|
+
function callback(bytes memory data) external payable nonReentrant {
|
|
139
|
+
(bool success, bytes memory result) = address(this).call{ value: msg.value }(data);
|
|
140
|
+
require(success, string(result));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function setFillStatus(bytes32 relayHash, FillType fillType) external {
|
|
144
|
+
fillStatuses[relayHash] = uint256(fillType);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getCurrentTime() public view override returns (uint256) {
|
|
148
|
+
return currentTime;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function _preExecuteLeafHook(address token) internal override {
|
|
152
|
+
emit PreLeafExecuteHook(token.toBytes32());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function _bridgeTokensToHubPool(uint256 amount, address token) internal override {
|
|
156
|
+
emit BridgedToHubPool(amount, token);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function _requireAdminSender() internal override onlyOwner {} // solhint-disable-line no-empty-blocks
|
|
160
|
+
|
|
161
|
+
function chainId() public view override(SpokePool) returns (uint256) {
|
|
162
|
+
// If chainId_ is set then return it, else do nothing and return the parent chainId().
|
|
163
|
+
return chainId_ == 0 ? super.chainId() : chainId_;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function setChainId(uint256 _chainId) public {
|
|
167
|
+
chainId_ = _chainId;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function _computeAmountPreFees(uint256 amount, int64 feesPct) private pure returns (uint256) {
|
|
171
|
+
return (1e18 * amount) / uint256((int256(1e18) - feesPct));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function __computeAmountPostFees(uint256 amount, int256 feesPct) private pure returns (uint256) {
|
|
175
|
+
return (amount * uint256(int256(1e18) - feesPct)) / 1e18;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function _getRelayHash(MockV2SpokePoolInterface.RelayData memory relayData) private pure returns (bytes32) {
|
|
179
|
+
return keccak256(abi.encode(relayData));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function _fillRelay(RelayExecution memory relayExecution) internal returns (uint256 fillAmountPreFees) {
|
|
183
|
+
MockV2SpokePoolInterface.RelayData memory relayData = relayExecution.relay;
|
|
184
|
+
|
|
185
|
+
require(relayFills[relayExecution.relayHash] < relayData.amount, "relay filled");
|
|
186
|
+
|
|
187
|
+
fillAmountPreFees = _computeAmountPreFees(
|
|
188
|
+
relayExecution.maxTokensToSend,
|
|
189
|
+
(relayData.realizedLpFeePct + relayExecution.updatedRelayerFeePct)
|
|
190
|
+
);
|
|
191
|
+
require(fillAmountPreFees > 0, "fill amount pre fees is 0");
|
|
192
|
+
|
|
193
|
+
uint256 amountRemainingInRelay = relayData.amount - relayFills[relayExecution.relayHash];
|
|
194
|
+
if (amountRemainingInRelay < fillAmountPreFees) {
|
|
195
|
+
fillAmountPreFees = amountRemainingInRelay;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
uint256 amountToSend = __computeAmountPostFees(
|
|
199
|
+
fillAmountPreFees,
|
|
200
|
+
relayData.realizedLpFeePct + relayExecution.updatedRelayerFeePct
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (relayExecution.payoutAdjustmentPct != 0) {
|
|
204
|
+
require(relayExecution.payoutAdjustmentPct >= -1e18, "payoutAdjustmentPct too small");
|
|
205
|
+
require(relayExecution.payoutAdjustmentPct <= 100e18, "payoutAdjustmentPct too large");
|
|
206
|
+
|
|
207
|
+
amountToSend = __computeAmountPostFees(amountToSend, -relayExecution.payoutAdjustmentPct);
|
|
208
|
+
require(amountToSend <= relayExecution.maxTokensToSend, "Somehow hit maxTokensToSend!");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
bool localRepayment = relayExecution.repaymentChainId == relayExecution.relay.destinationChainId;
|
|
212
|
+
require(
|
|
213
|
+
localRepayment || relayExecution.relay.amount == fillAmountPreFees || relayExecution.slowFill,
|
|
214
|
+
"invalid repayment chain"
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
relayFills[relayExecution.relayHash] += fillAmountPreFees;
|
|
218
|
+
|
|
219
|
+
if (msg.sender.toBytes32() == relayExecution.updatedRecipient && !relayExecution.slowFill) {
|
|
220
|
+
return fillAmountPreFees;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (relayData.destinationToken == address(wrappedNativeToken).toBytes32()) {
|
|
224
|
+
if (!relayExecution.slowFill) {
|
|
225
|
+
IERC20Upgradeable(relayData.destinationToken.toAddress()).safeTransferFrom(
|
|
226
|
+
msg.sender,
|
|
227
|
+
address(this),
|
|
228
|
+
amountToSend
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
_unwrapwrappedNativeTokenTo(payable(relayExecution.updatedRecipient.toAddress()), amountToSend);
|
|
232
|
+
} else {
|
|
233
|
+
if (!relayExecution.slowFill) {
|
|
234
|
+
IERC20Upgradeable(relayData.destinationToken.toAddress()).safeTransferFrom(
|
|
235
|
+
msg.sender,
|
|
236
|
+
relayExecution.updatedRecipient.toAddress(),
|
|
237
|
+
amountToSend
|
|
238
|
+
);
|
|
239
|
+
} else {
|
|
240
|
+
IERC20Upgradeable(relayData.destinationToken.toAddress()).safeTransfer(
|
|
241
|
+
relayExecution.updatedRecipient.toAddress(),
|
|
242
|
+
amountToSend
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function _verifySlowFill(
|
|
249
|
+
RelayExecution memory relayExecution,
|
|
250
|
+
uint32 rootBundleId,
|
|
251
|
+
bytes32[] memory proof
|
|
252
|
+
) internal view {
|
|
253
|
+
SlowFill memory slowFill = SlowFill({
|
|
254
|
+
relayData: relayExecution.relay,
|
|
255
|
+
payoutAdjustmentPct: relayExecution.payoutAdjustmentPct
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
require(
|
|
259
|
+
V2MerkleLib.verifySlowRelayFulfillment(rootBundles[rootBundleId].slowRelayRoot, slowFill, proof),
|
|
260
|
+
"Invalid slow relay proof"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//SPDX-License-Identifier: Unlicense
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "./MockSpokePool.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title MockSpokePoolV2
|
|
8
|
+
* @notice Upgrades MockSpokePool for no practical reason other than to demonstrate
|
|
9
|
+
* upgradeability options
|
|
10
|
+
*/
|
|
11
|
+
contract MockSpokePoolV2 is MockSpokePool {
|
|
12
|
+
event NewEvent(bool value);
|
|
13
|
+
|
|
14
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
15
|
+
constructor(address _wrappedNativeTokenAddress) MockSpokePool(_wrappedNativeTokenAddress) {} // solhint-disable-line no-empty-blocks
|
|
16
|
+
|
|
17
|
+
// Demonstrative of how we could reset state variables in a V2 contract conveniently while initializing new
|
|
18
|
+
// modules. The `reinitializer` modifier is required to create new Initializable contracts.
|
|
19
|
+
function reinitialize(address _hubPool) public reinitializer(2) {
|
|
20
|
+
_setWithdrawalRecipient(_hubPool);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Demonstrative new function we could add in a V2 contract.
|
|
24
|
+
function emitEvent() external {
|
|
25
|
+
emit NewEvent(true);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import { BridgeHubInterface } from "../interfaces/ZkStackBridgeHub.sol";
|
|
5
|
+
|
|
6
|
+
contract MockBridgeHub is BridgeHubInterface {
|
|
7
|
+
address public immutable sharedBridge;
|
|
8
|
+
|
|
9
|
+
constructor(address _sharedBridge) {
|
|
10
|
+
sharedBridge = _sharedBridge;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
mapping(uint256 => address) baseTokens;
|
|
14
|
+
|
|
15
|
+
function setBaseToken(uint256 _chainId, address _baseToken) external {
|
|
16
|
+
baseTokens[_chainId] = _baseToken;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function requestL2TransactionDirect(
|
|
20
|
+
L2TransactionRequestDirect calldata _request
|
|
21
|
+
) external payable returns (bytes32 canonicalTxHash) {
|
|
22
|
+
canonicalTxHash = keccak256(abi.encode(_request));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function requestL2TransactionTwoBridges(
|
|
26
|
+
L2TransactionRequestTwoBridgesOuter calldata _request
|
|
27
|
+
) external payable returns (bytes32 canonicalTxHash) {
|
|
28
|
+
canonicalTxHash = keccak256(abi.encode(_request));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function baseToken(uint256 _chainId) external view returns (address) {
|
|
32
|
+
return baseTokens[_chainId] == address(0) ? address(1) : baseTokens[_chainId];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function l2TransactionBaseCost(
|
|
36
|
+
uint256,
|
|
37
|
+
uint256 _gasPrice,
|
|
38
|
+
uint256 _l2GasLimit,
|
|
39
|
+
uint256 _l2GasPerPubdataByteLimit
|
|
40
|
+
) external view returns (uint256) {
|
|
41
|
+
return _gasPrice + _l2GasLimit * _l2GasPerPubdataByteLimit;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "../external/uma/core/contracts/common/implementation/ExpandedERC20.sol";
|
|
5
|
+
import "../spoke-pools/PolygonTokenBridger.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @notice Simulated Polygon ERC20 for use in testing PolygonTokenBridger.
|
|
9
|
+
*/
|
|
10
|
+
contract PolygonERC20Test is ExpandedERC20 {
|
|
11
|
+
constructor() ExpandedERC20("Polygon Test", "POLY_TEST", 18) {} // solhint-disable-line no-empty-blocks
|
|
12
|
+
|
|
13
|
+
function withdraw(uint256 amount) public {
|
|
14
|
+
_burn(msg.sender, amount);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts-v4/token/ERC20/ERC20.sol";
|
|
5
|
+
|
|
6
|
+
contract RootChainManagerMock {
|
|
7
|
+
// Call tracking for depositEtherFor
|
|
8
|
+
uint256 public depositEtherForCallCount;
|
|
9
|
+
struct DepositEtherForCall {
|
|
10
|
+
address user;
|
|
11
|
+
uint256 value;
|
|
12
|
+
}
|
|
13
|
+
DepositEtherForCall public lastDepositEtherForCall;
|
|
14
|
+
|
|
15
|
+
// Call tracking for depositFor
|
|
16
|
+
uint256 public depositForCallCount;
|
|
17
|
+
struct DepositForCall {
|
|
18
|
+
address user;
|
|
19
|
+
address rootToken;
|
|
20
|
+
bytes depositData;
|
|
21
|
+
}
|
|
22
|
+
DepositForCall public lastDepositForCall;
|
|
23
|
+
|
|
24
|
+
function depositEtherFor(address user) external payable {
|
|
25
|
+
depositEtherForCallCount++;
|
|
26
|
+
lastDepositEtherForCall = DepositEtherForCall(user, msg.value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function depositFor(address user, address rootToken, bytes calldata depositData) external {
|
|
30
|
+
depositForCallCount++;
|
|
31
|
+
lastDepositForCall = DepositForCall(user, rootToken, depositData);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
contract FxStateSenderMock {
|
|
36
|
+
// Call tracking for sendMessageToChild
|
|
37
|
+
uint256 public sendMessageToChildCallCount;
|
|
38
|
+
struct SendMessageToChildCall {
|
|
39
|
+
address receiver;
|
|
40
|
+
bytes data;
|
|
41
|
+
}
|
|
42
|
+
SendMessageToChildCall public lastSendMessageToChildCall;
|
|
43
|
+
|
|
44
|
+
function sendMessageToChild(address _receiver, bytes calldata _data) external {
|
|
45
|
+
sendMessageToChildCallCount++;
|
|
46
|
+
lastSendMessageToChildCall = SendMessageToChildCall(_receiver, _data);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
contract DepositManagerMock {
|
|
51
|
+
// Call tracking for depositERC20ForUser
|
|
52
|
+
uint256 public depositERC20ForUserCallCount;
|
|
53
|
+
struct DepositERC20ForUserCall {
|
|
54
|
+
address token;
|
|
55
|
+
address user;
|
|
56
|
+
uint256 amount;
|
|
57
|
+
}
|
|
58
|
+
DepositERC20ForUserCall public lastDepositERC20ForUserCall;
|
|
59
|
+
|
|
60
|
+
function depositERC20ForUser(address token, address user, uint256 amount) external {
|
|
61
|
+
depositERC20ForUserCallCount++;
|
|
62
|
+
lastDepositERC20ForUserCall = DepositERC20ForUserCall(token, user, amount);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
contract PolygonRegistryMock {
|
|
67
|
+
// solhint-disable-next-line no-empty-blocks
|
|
68
|
+
function erc20Predicate() external returns (address predicate) {}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
contract PolygonERC20PredicateMock {
|
|
72
|
+
// solhint-disable-next-line no-empty-blocks
|
|
73
|
+
function startExitWithBurntTokens(bytes calldata data) external {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
contract PolygonERC20Mock is ERC20 {
|
|
77
|
+
// solhint-disable-next-line no-empty-blocks
|
|
78
|
+
constructor() ERC20("Test ERC20", "TEST") {}
|
|
79
|
+
|
|
80
|
+
// solhint-disable-next-line no-empty-blocks
|
|
81
|
+
function withdraw(uint256 amount) external {}
|
|
82
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import { IPolygonZkEVMBridge } from "../external/interfaces/IPolygonZkEVMBridge.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @notice Mock implementation of Polygon zkEVM's Bridge.
|
|
8
|
+
* @dev Used for testing PolygonZkEVM_SpokePool functionality.
|
|
9
|
+
*/
|
|
10
|
+
contract MockPolygonZkEVMBridge is IPolygonZkEVMBridge {
|
|
11
|
+
uint256 public bridgeAssetCallCount;
|
|
12
|
+
|
|
13
|
+
event BridgeAssetCalled(
|
|
14
|
+
uint32 indexed destinationNetwork,
|
|
15
|
+
address indexed destinationAddress,
|
|
16
|
+
uint256 amount,
|
|
17
|
+
address token,
|
|
18
|
+
bool forceUpdateGlobalExitRoot,
|
|
19
|
+
bytes permitData
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
struct BridgeAssetCall {
|
|
23
|
+
uint32 destinationNetwork;
|
|
24
|
+
address destinationAddress;
|
|
25
|
+
uint256 amount;
|
|
26
|
+
address token;
|
|
27
|
+
bool forceUpdateGlobalExitRoot;
|
|
28
|
+
bytes permitData;
|
|
29
|
+
uint256 value;
|
|
30
|
+
}
|
|
31
|
+
BridgeAssetCall public lastBridgeAssetCall;
|
|
32
|
+
|
|
33
|
+
function bridgeAsset(
|
|
34
|
+
uint32 destinationNetwork,
|
|
35
|
+
address destinationAddress,
|
|
36
|
+
uint256 amount,
|
|
37
|
+
address token,
|
|
38
|
+
bool forceUpdateGlobalExitRoot,
|
|
39
|
+
bytes calldata permitData
|
|
40
|
+
) external payable override {
|
|
41
|
+
bridgeAssetCallCount++;
|
|
42
|
+
lastBridgeAssetCall = BridgeAssetCall({
|
|
43
|
+
destinationNetwork: destinationNetwork,
|
|
44
|
+
destinationAddress: destinationAddress,
|
|
45
|
+
amount: amount,
|
|
46
|
+
token: token,
|
|
47
|
+
forceUpdateGlobalExitRoot: forceUpdateGlobalExitRoot,
|
|
48
|
+
permitData: permitData,
|
|
49
|
+
value: msg.value
|
|
50
|
+
});
|
|
51
|
+
emit BridgeAssetCalled(
|
|
52
|
+
destinationNetwork,
|
|
53
|
+
destinationAddress,
|
|
54
|
+
amount,
|
|
55
|
+
token,
|
|
56
|
+
forceUpdateGlobalExitRoot,
|
|
57
|
+
permitData
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function bridgeMessage(
|
|
62
|
+
uint32 destinationNetwork,
|
|
63
|
+
address destinationAddress,
|
|
64
|
+
bool forceUpdateGlobalExitRoot,
|
|
65
|
+
bytes calldata metadata
|
|
66
|
+
) external payable override {
|
|
67
|
+
// Not needed for SpokePool tests
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Allow receiving ETH
|
|
71
|
+
receive() external payable {}
|
|
72
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@scroll-tech/contracts/libraries/IScrollMessenger.sol";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title MockScrollMessenger
|
|
8
|
+
* @notice Mock implementation of IScrollMessenger for testing Scroll_SpokePool
|
|
9
|
+
*/
|
|
10
|
+
contract MockScrollMessenger is IScrollMessenger {
|
|
11
|
+
address private _xDomainMessageSender;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @notice Sets the xDomainMessageSender that will be returned by subsequent calls
|
|
15
|
+
* @param sender The address to set as the cross-domain sender
|
|
16
|
+
*/
|
|
17
|
+
function setXDomainMessageSender(address sender) external {
|
|
18
|
+
_xDomainMessageSender = sender;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @notice Returns the sender of a cross domain message
|
|
23
|
+
*/
|
|
24
|
+
function xDomainMessageSender() external view override returns (address) {
|
|
25
|
+
return _xDomainMessageSender;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @notice Impersonates a cross-domain call by setting the xDomainMessageSender
|
|
30
|
+
* and calling the target with the provided data
|
|
31
|
+
* @param target The target contract to call
|
|
32
|
+
* @param data The calldata to pass to the target
|
|
33
|
+
*/
|
|
34
|
+
function impersonateCall(address target, bytes memory data) external {
|
|
35
|
+
_xDomainMessageSender = msg.sender;
|
|
36
|
+
(bool success, bytes memory returnData) = target.call(data);
|
|
37
|
+
if (!success) {
|
|
38
|
+
// Forward the revert reason
|
|
39
|
+
assembly {
|
|
40
|
+
revert(add(returnData, 32), mload(returnData))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
_xDomainMessageSender = address(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @notice Mock implementation - does nothing
|
|
48
|
+
*/
|
|
49
|
+
function sendMessage(address, uint256, bytes calldata, uint256) external payable override {}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @notice Mock implementation - does nothing
|
|
53
|
+
*/
|
|
54
|
+
function sendMessage(address, uint256, bytes calldata, uint256, address) external payable override {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @title MockScrollL2GatewayRouter
|
|
59
|
+
* @notice Mock implementation of IL2GatewayRouterExtended for testing Scroll_SpokePool
|
|
60
|
+
*/
|
|
61
|
+
contract MockScrollL2GatewayRouter {
|
|
62
|
+
address public defaultERC20Gateway;
|
|
63
|
+
mapping(address => address) public erc20Gateways;
|
|
64
|
+
|
|
65
|
+
// Track last withdrawERC20 call for verification
|
|
66
|
+
address public lastWithdrawToken;
|
|
67
|
+
address public lastWithdrawTo;
|
|
68
|
+
uint256 public lastWithdrawAmount;
|
|
69
|
+
uint256 public lastWithdrawGasLimit;
|
|
70
|
+
|
|
71
|
+
event WithdrawERC20Called(address indexed token, address indexed to, uint256 amount, uint256 gasLimit);
|
|
72
|
+
|
|
73
|
+
constructor() {
|
|
74
|
+
defaultERC20Gateway = address(this);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @notice Sets the default ERC20 gateway address
|
|
79
|
+
*/
|
|
80
|
+
function setDefaultERC20Gateway(address gateway) external {
|
|
81
|
+
defaultERC20Gateway = gateway;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @notice Sets a custom gateway for a specific token
|
|
86
|
+
*/
|
|
87
|
+
function setERC20Gateway(address token, address gateway) external {
|
|
88
|
+
erc20Gateways[token] = gateway;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @notice Returns the gateway for a specific token, or the default gateway if not set
|
|
93
|
+
*/
|
|
94
|
+
function getERC20Gateway(address token) external view returns (address) {
|
|
95
|
+
address gateway = erc20Gateways[token];
|
|
96
|
+
return gateway != address(0) ? gateway : defaultERC20Gateway;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @notice Mock withdrawERC20 - records the call parameters
|
|
101
|
+
*/
|
|
102
|
+
function withdrawERC20(address token, address to, uint256 amount, uint256 gasLimit) external payable {
|
|
103
|
+
lastWithdrawToken = token;
|
|
104
|
+
lastWithdrawTo = to;
|
|
105
|
+
lastWithdrawAmount = amount;
|
|
106
|
+
lastWithdrawGasLimit = gasLimit;
|
|
107
|
+
emit WithdrawERC20Called(token, to, amount, gasLimit);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.0;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts-v4/utils/cryptography/MerkleProof.sol";
|
|
5
|
+
import "./interfaces/MockV2SpokePoolInterface.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @notice Library to help with merkle roots, proofs, and claims.
|
|
9
|
+
*/
|
|
10
|
+
library V2MerkleLib {
|
|
11
|
+
function verifySlowRelayFulfillment(
|
|
12
|
+
bytes32 root,
|
|
13
|
+
MockV2SpokePoolInterface.SlowFill memory slowRelayFulfillment,
|
|
14
|
+
bytes32[] memory proof
|
|
15
|
+
) internal pure returns (bool) {
|
|
16
|
+
return MerkleProof.verify(proof, root, keccak256(abi.encode(slowRelayFulfillment)));
|
|
17
|
+
}
|
|
18
|
+
}
|