@evvm/testnet-contracts 2.3.0 → 3.0.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/README.md +44 -24
- package/contracts/core/Core.sol +1392 -0
- package/contracts/core/lib/CoreStorage.sol +171 -0
- package/contracts/nameService/NameService.sol +613 -543
- package/contracts/nameService/lib/IdentityValidation.sol +15 -21
- package/contracts/p2pSwap/P2PSwap.sol +258 -145
- package/contracts/staking/Estimator.sol +25 -44
- package/contracts/staking/Staking.sol +284 -262
- package/contracts/treasury/Treasury.sol +40 -47
- package/contracts/treasuryTwoChains/TreasuryExternalChainStation.sol +585 -198
- package/contracts/treasuryTwoChains/TreasuryHostChainStation.sol +425 -174
- package/contracts/treasuryTwoChains/lib/PayloadUtils.sol +2 -4
- package/interfaces/{IEvvm.sol → ICore.sol} +58 -25
- package/interfaces/IEstimator.sol +1 -1
- package/interfaces/INameService.sol +46 -49
- package/interfaces/IP2PSwap.sol +16 -17
- package/interfaces/IStaking.sol +21 -17
- package/interfaces/ITreasury.sol +2 -1
- package/interfaces/ITreasuryExternalChainStation.sol +15 -9
- package/interfaces/ITreasuryHostChainStation.sol +14 -11
- package/interfaces/IUserValidator.sol +6 -0
- package/library/Erc191TestBuilder.sol +336 -471
- package/library/EvvmService.sol +27 -71
- package/library/errors/CoreError.sol +116 -0
- package/library/errors/CrossChainTreasuryError.sol +36 -0
- package/library/errors/NameServiceError.sol +79 -0
- package/library/errors/StakingError.sol +79 -0
- package/{contracts/treasury/lib/ErrorsLib.sol → library/errors/TreasuryError.sol} +9 -17
- package/library/structs/CoreStructs.sol +146 -0
- package/library/structs/ExternalChainStationStructs.sol +92 -0
- package/library/structs/HostChainStationStructs.sol +77 -0
- package/library/structs/NameServiceStructs.sol +47 -0
- package/library/structs/P2PSwapStructs.sol +127 -0
- package/library/structs/StakingStructs.sol +67 -0
- package/library/utils/AdvancedStrings.sol +62 -44
- package/library/utils/CAUtils.sol +29 -0
- package/library/utils/governance/Admin.sol +66 -0
- package/library/utils/governance/ProposalStructs.sol +49 -0
- package/library/utils/service/CoreExecution.sol +158 -0
- package/library/utils/service/StakingServiceUtils.sol +20 -37
- package/library/utils/signature/CoreHashUtils.sol +73 -0
- package/library/utils/signature/NameServiceHashUtils.sol +156 -0
- package/library/utils/signature/P2PSwapHashUtils.sol +65 -0
- package/library/utils/signature/StakingHashUtils.sol +41 -0
- package/library/utils/signature/TreasuryCrossChainHashUtils.sol +40 -0
- package/package.json +1 -1
- package/contracts/evvm/Evvm.sol +0 -1300
- package/contracts/evvm/lib/ErrorsLib.sol +0 -131
- package/contracts/evvm/lib/EvvmStorage.sol +0 -217
- package/contracts/evvm/lib/EvvmStructs.sol +0 -208
- package/contracts/evvm/lib/SignatureUtils.sol +0 -162
- package/contracts/nameService/lib/ErrorsLib.sol +0 -155
- package/contracts/nameService/lib/NameServiceStructs.sol +0 -125
- package/contracts/nameService/lib/SignatureUtils.sol +0 -420
- package/contracts/p2pSwap/lib/P2PSwapStructs.sol +0 -59
- package/contracts/p2pSwap/lib/SignatureUtils.sol +0 -98
- package/contracts/staking/lib/ErrorsLib.sol +0 -98
- package/contracts/staking/lib/SignatureUtils.sol +0 -105
- package/contracts/staking/lib/StakingStructs.sol +0 -106
- package/contracts/treasuryTwoChains/lib/ErrorsLib.sol +0 -48
- package/contracts/treasuryTwoChains/lib/ExternalChainStationStructs.sol +0 -80
- package/contracts/treasuryTwoChains/lib/HostChainStationStructs.sol +0 -87
- package/contracts/treasuryTwoChains/lib/SignatureUtils.sol +0 -79
- package/library/utils/GovernanceUtils.sol +0 -81
- package/library/utils/nonces/AsyncNonce.sol +0 -74
- package/library/utils/nonces/SyncNonce.sol +0 -71
- package/library/utils/service/EvvmPayments.sol +0 -144
|
@@ -3,53 +3,35 @@
|
|
|
3
3
|
|
|
4
4
|
pragma solidity ^0.8.0;
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
_____
|
|
8
|
-
/__ \_ __ ___ __ _ ___ _ _ _ __ _ _
|
|
9
|
-
/ /\| '__/ _ \/ _` / __| | | | '__| | | |
|
|
10
|
-
/ / | | | __| (_| \__ | |_| | | | |_| |
|
|
11
|
-
\/ |_| \___|\__,_|___/\__,_|_| \__, |
|
|
12
|
-
|___/
|
|
13
|
-
___ _ _ __ _ _ _
|
|
14
|
-
/ __| |__ __ _(_)_ __ / _| |_ __ _| |_(_) ___ _ __
|
|
15
|
-
/ / | '_ \ / _` | | '_ \\ \| __/ _` | __| |/ _ \| '_ \
|
|
16
|
-
/ /___| | | | (_| | | | | _\ | || (_| | |_| | (_) | | | |
|
|
17
|
-
\____/|_| |_|\__,_|_|_| |_\__/\__\__,_|\__|_|\___/|_| |_|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
_____ _____ _____ _____ _____ _____ _____ _____ _____ _____
|
|
22
|
-
|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
|
|
23
|
-
|
|
24
|
-
______ __ __ __ _
|
|
25
|
-
/ _____ __/ /____ _________ ____ _/ / _____/ /_ ____ _(_____
|
|
26
|
-
/ __/ | |/_/ __/ _ \/ ___/ __ \/ __ `/ / / ___/ __ \/ __ `/ / __ \
|
|
27
|
-
/ /____> </ /_/ __/ / / / / / /_/ / / / /__/ / / / /_/ / / / / /
|
|
28
|
-
/_____/_/|_|\__/\___/_/ /_/ /_/\__,_/_/ \___/_/ /_/\__,_/_/_/ /_/
|
|
29
|
-
|
|
30
|
-
* @title Treasury Cross-Chain Host Station Contract
|
|
31
|
-
* @author Mate labs
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import {IERC20} from "@evvm/testnet-contracts/library/primitives/IERC20.sol";
|
|
35
6
|
import {
|
|
36
|
-
|
|
37
|
-
} from "@evvm/testnet-contracts/
|
|
7
|
+
CrossChainTreasuryError as Error
|
|
8
|
+
} from "@evvm/testnet-contracts/library/errors/CrossChainTreasuryError.sol";
|
|
38
9
|
import {
|
|
39
|
-
|
|
40
|
-
} from "@evvm/testnet-contracts/
|
|
41
|
-
|
|
42
|
-
import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol";
|
|
43
|
-
|
|
10
|
+
TreasuryCrossChainHashUtils as Hash
|
|
11
|
+
} from "@evvm/testnet-contracts/library/utils/signature/TreasuryCrossChainHashUtils.sol";
|
|
44
12
|
import {
|
|
45
|
-
|
|
46
|
-
} from "@evvm/testnet-contracts/
|
|
13
|
+
ExternalChainStationStructs as Structs
|
|
14
|
+
} from "@evvm/testnet-contracts/library/structs/ExternalChainStationStructs.sol";
|
|
47
15
|
import {
|
|
48
16
|
PayloadUtils
|
|
49
17
|
} from "@evvm/testnet-contracts/contracts/treasuryTwoChains/lib/PayloadUtils.sol";
|
|
50
18
|
|
|
51
|
-
import {
|
|
19
|
+
import {CoreError} from "@evvm/testnet-contracts/library/errors/CoreError.sol";
|
|
52
20
|
|
|
21
|
+
import {
|
|
22
|
+
SignatureRecover
|
|
23
|
+
} from "@evvm/testnet-contracts/library/primitives/SignatureRecover.sol";
|
|
24
|
+
import {
|
|
25
|
+
ProposalStructs
|
|
26
|
+
} from "@evvm/testnet-contracts/library/utils/governance/ProposalStructs.sol";
|
|
27
|
+
import {
|
|
28
|
+
AdvancedStrings
|
|
29
|
+
} from "@evvm/testnet-contracts/library/utils/AdvancedStrings.sol";
|
|
30
|
+
|
|
31
|
+
import {IERC20} from "@evvm/testnet-contracts/library/primitives/IERC20.sol";
|
|
32
|
+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
33
|
+
import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol";
|
|
34
|
+
import {IMailbox} from "@hyperlane-xyz/core/contracts/interfaces/IMailbox.sol";
|
|
53
35
|
import {
|
|
54
36
|
MessagingParams,
|
|
55
37
|
MessagingReceipt
|
|
@@ -65,8 +47,6 @@ import {
|
|
|
65
47
|
import {
|
|
66
48
|
OptionsBuilder
|
|
67
49
|
} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";
|
|
68
|
-
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
69
|
-
|
|
70
50
|
import {
|
|
71
51
|
AxelarExecutable
|
|
72
52
|
} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
|
|
@@ -76,39 +56,67 @@ import {
|
|
|
76
56
|
import {
|
|
77
57
|
IInterchainGasEstimation
|
|
78
58
|
} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IInterchainGasEstimation.sol";
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
_____
|
|
63
|
+
/__ \_ __ ___ __ _ ___ _ _ _ __ _ _
|
|
64
|
+
/ /\| '__/ _ \/ _` / __| | | | '__| | | |
|
|
65
|
+
/ / | | | __| (_| \__ | |_| | | | |_| |
|
|
66
|
+
\/ |_| \___|\__,_|___/\__,_|_| \__, |
|
|
67
|
+
|___/
|
|
68
|
+
___ _ _ __ _ _ _
|
|
69
|
+
/ __| |__ __ _(_)_ __ / _| |_ __ _| |_(_) ___ _ __
|
|
70
|
+
/ / | '_ \ / _` | | '_ \\ \| __/ _` | __| |/ _ \| '_ \
|
|
71
|
+
/ /___| | | | (_| | | | | _\ | || (_| | |_| | (_) | | | |
|
|
72
|
+
\____/|_| |_|\__,_|_|_| |_\__/\__\__,_|\__|_|\___/|_| |_|
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
_____ _____ _____ _____ _____ _____ _____ _____ _____ _____
|
|
77
|
+
|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
|
|
78
|
+
|
|
79
|
+
______ __ __ __ _
|
|
80
|
+
/ _____ __/ /____ _________ ____ _/ / _____/ /_ ____ _(_____
|
|
81
|
+
/ __/ | |/_/ __/ _ \/ ___/ __ \/ __ `/ / / ___/ __ \/ __ `/ / __ \
|
|
82
|
+
/ /____> </ /_/ __/ / / / / / /_/ / / / /__/ / / / /_/ / / / / /
|
|
83
|
+
/_____/_/|_|\__/\___/_/ /_/ /_/\__,_/_/ \___/_/ /_/\__,_/_/_/ /_/
|
|
84
|
+
|
|
85
|
+
* @title EVVM External Chain Station
|
|
86
|
+
* @author Mate labs
|
|
87
|
+
* @notice Manages cross-chain deposits from an external chain to the EVVM host chain.
|
|
88
|
+
* @dev Multi-protocol bridge supporting Hyperlane, LayerZero V2, and Axelar.
|
|
89
|
+
* Facilitates token transfers using a sequential nonce system and ECDSA signatures.
|
|
90
|
+
*/
|
|
82
91
|
|
|
83
92
|
contract TreasuryExternalChainStation is
|
|
84
|
-
ExternalChainStationStructs,
|
|
85
93
|
OApp,
|
|
86
94
|
OAppOptionsType3,
|
|
87
95
|
AxelarExecutable
|
|
88
96
|
{
|
|
89
97
|
/// @notice Admin address management with time-delayed proposals
|
|
90
98
|
/// @dev Stores current admin, proposed admin, and acceptance timestamp
|
|
91
|
-
AddressTypeProposal admin;
|
|
99
|
+
ProposalStructs.AddressTypeProposal admin;
|
|
92
100
|
|
|
93
101
|
/// @notice Fisher executor address management with time-delayed proposals
|
|
94
102
|
/// @dev Fisher executor can process cross-chain bridge transactions
|
|
95
|
-
AddressTypeProposal fisherExecutor;
|
|
103
|
+
ProposalStructs.AddressTypeProposal fisherExecutor;
|
|
96
104
|
|
|
97
105
|
/// @notice Hyperlane protocol configuration for cross-chain messaging
|
|
98
106
|
/// @dev Contains domain ID, host chain address, and mailbox contract address
|
|
99
|
-
HyperlaneConfig hyperlane;
|
|
107
|
+
Structs.HyperlaneConfig hyperlane;
|
|
100
108
|
|
|
101
109
|
/// @notice LayerZero protocol configuration for omnichain messaging
|
|
102
110
|
/// @dev Contains endpoint ID, host chain address, and endpoint contract address
|
|
103
|
-
LayerZeroConfig layerZero;
|
|
111
|
+
Structs.LayerZeroConfig layerZero;
|
|
104
112
|
|
|
105
113
|
/// @notice Axelar protocol configuration for cross-chain communication
|
|
106
114
|
/// @dev Contains chain name, host chain address, gas service, and gateway addresses
|
|
107
|
-
AxelarConfig axelar;
|
|
115
|
+
Structs.AxelarConfig axelar;
|
|
108
116
|
|
|
109
117
|
/// @notice Pending proposal for changing host chain addresses across all protocols
|
|
110
118
|
/// @dev Used for coordinated updates to host chain addresses with time delay
|
|
111
|
-
ChangeHostChainAddressParams
|
|
119
|
+
Structs.ChangeHostChainAddressParams hostChainAddress;
|
|
112
120
|
|
|
113
121
|
/// @notice Unique identifier for the EVVM instance this station belongs to
|
|
114
122
|
/// @dev Immutable value set at deployment for signature verification
|
|
@@ -116,9 +124,7 @@ contract TreasuryExternalChainStation is
|
|
|
116
124
|
|
|
117
125
|
uint256 windowTimeToChangeEvvmID;
|
|
118
126
|
|
|
119
|
-
|
|
120
|
-
/// @dev Prevents replay attacks in Fisher bridge transactions
|
|
121
|
-
mapping(address => uint256) nextFisherExecutionNonce;
|
|
127
|
+
mapping(address user => mapping(uint256 nonce => bool isUsed)) asyncNonce;
|
|
122
128
|
|
|
123
129
|
/// @notice LayerZero execution options with gas limit configuration
|
|
124
130
|
/// @dev Pre-built options for LayerZero message execution (200k gas limit)
|
|
@@ -173,32 +179,32 @@ contract TreasuryExternalChainStation is
|
|
|
173
179
|
/// @param _crosschainConfig Configuration struct containing all cross-chain protocol settings
|
|
174
180
|
constructor(
|
|
175
181
|
address _admin,
|
|
176
|
-
CrosschainConfig memory _crosschainConfig
|
|
182
|
+
Structs.CrosschainConfig memory _crosschainConfig
|
|
177
183
|
)
|
|
178
184
|
OApp(_crosschainConfig.layerZero.endpointAddress, _admin)
|
|
179
185
|
Ownable(_admin)
|
|
180
186
|
AxelarExecutable(_crosschainConfig.axelar.gatewayAddress)
|
|
181
187
|
{
|
|
182
|
-
admin = AddressTypeProposal({
|
|
188
|
+
admin = ProposalStructs.AddressTypeProposal({
|
|
183
189
|
current: _admin,
|
|
184
190
|
proposal: address(0),
|
|
185
191
|
timeToAccept: 0
|
|
186
192
|
});
|
|
187
|
-
hyperlane = HyperlaneConfig({
|
|
193
|
+
hyperlane = Structs.HyperlaneConfig({
|
|
188
194
|
hostChainStationDomainId: _crosschainConfig
|
|
189
195
|
.hyperlane
|
|
190
196
|
.hostChainStationDomainId,
|
|
191
197
|
hostChainStationAddress: "",
|
|
192
198
|
mailboxAddress: _crosschainConfig.hyperlane.mailboxAddress
|
|
193
199
|
});
|
|
194
|
-
layerZero = LayerZeroConfig({
|
|
200
|
+
layerZero = Structs.LayerZeroConfig({
|
|
195
201
|
hostChainStationEid: _crosschainConfig
|
|
196
202
|
.layerZero
|
|
197
203
|
.hostChainStationEid,
|
|
198
204
|
hostChainStationAddress: "",
|
|
199
205
|
endpointAddress: _crosschainConfig.layerZero.endpointAddress
|
|
200
206
|
});
|
|
201
|
-
axelar = AxelarConfig({
|
|
207
|
+
axelar = Structs.AxelarConfig({
|
|
202
208
|
hostChainStationChainName: _crosschainConfig
|
|
203
209
|
.axelar
|
|
204
210
|
.hostChainStationChainName,
|
|
@@ -230,6 +236,8 @@ contract TreasuryExternalChainStation is
|
|
|
230
236
|
layerZero.hostChainStationAddress
|
|
231
237
|
);
|
|
232
238
|
|
|
239
|
+
hostChainAddress.currentAddress = hostChainStationAddress;
|
|
240
|
+
|
|
233
241
|
fuseSetHostChainAddress = 0x00;
|
|
234
242
|
}
|
|
235
243
|
|
|
@@ -240,7 +248,7 @@ contract TreasuryExternalChainStation is
|
|
|
240
248
|
function setEvvmID(uint256 newEvvmID) external onlyAdmin {
|
|
241
249
|
if (evvmID != 0) {
|
|
242
250
|
if (block.timestamp > windowTimeToChangeEvvmID)
|
|
243
|
-
revert
|
|
251
|
+
revert Error.WindowToChangeEvvmIDExpired();
|
|
244
252
|
}
|
|
245
253
|
|
|
246
254
|
evvmID = newEvvmID;
|
|
@@ -248,12 +256,42 @@ contract TreasuryExternalChainStation is
|
|
|
248
256
|
windowTimeToChangeEvvmID = block.timestamp + 24 hours;
|
|
249
257
|
}
|
|
250
258
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
/**
|
|
260
|
+
* @notice Deposits ERC20 tokens via selected protocol
|
|
261
|
+
* @dev Transfers tokens then bridges to host chain
|
|
262
|
+
*
|
|
263
|
+
* Process:
|
|
264
|
+
* - Transfer: User → this contract (via approval)
|
|
265
|
+
* - Encode: PayloadUtils.encodePayload(token, to, amt)
|
|
266
|
+
* - Route: Protocol-specific message dispatch
|
|
267
|
+
* - Receive: Host chain credits Core.sol balance
|
|
268
|
+
*
|
|
269
|
+
* Protocol Routing:
|
|
270
|
+
* - 0x01: Hyperlane (mailbox.dispatch + quote fee)
|
|
271
|
+
* - 0x02: LayerZero (_lzSend + quote fee)
|
|
272
|
+
* - 0x03: Axelar (payNativeGas + callContract)
|
|
273
|
+
*
|
|
274
|
+
* Fee Payment:
|
|
275
|
+
* - Hyperlane: msg.value = quote
|
|
276
|
+
* - LayerZero: msg.value = quote (refund excess)
|
|
277
|
+
* - Axelar: msg.value for gas service
|
|
278
|
+
*
|
|
279
|
+
* Host Chain Integration:
|
|
280
|
+
* - Receives: handle/_lzReceive/_execute
|
|
281
|
+
* - Credits: Core.sol balance for recipient
|
|
282
|
+
* - Fisher Bridge: Independent from Core.sol nonces
|
|
283
|
+
*
|
|
284
|
+
* Security:
|
|
285
|
+
* - Approval: Must approve this contract first
|
|
286
|
+
* - Validation: verifyAndDepositERC20 checks balance
|
|
287
|
+
* - Sender checks: Host validates origin on receive
|
|
288
|
+
*
|
|
289
|
+
* @param toAddress Recipient on host chain
|
|
290
|
+
* @param token ERC20 token address
|
|
291
|
+
* @param amount Token amount to bridge
|
|
292
|
+
* @param protocolToExecute 0x01=Hyperlane, 0x02=LZ,
|
|
293
|
+
* 0x03=Axelar
|
|
294
|
+
*/
|
|
257
295
|
function depositERC20(
|
|
258
296
|
address toAddress,
|
|
259
297
|
address token,
|
|
@@ -306,17 +344,48 @@ contract TreasuryExternalChainStation is
|
|
|
306
344
|
}
|
|
307
345
|
}
|
|
308
346
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
347
|
+
/**
|
|
348
|
+
* @notice Deposits native ETH via selected protocol
|
|
349
|
+
* @dev msg.value covers amount + protocol fees
|
|
350
|
+
*
|
|
351
|
+
* Process:
|
|
352
|
+
* - Validate: msg.value >= amount + fees
|
|
353
|
+
* - Encode: PayloadUtils.encodePayload(0x0, to, amt)
|
|
354
|
+
* - Route: Protocol-specific message dispatch
|
|
355
|
+
* - Receive: Host chain credits Core.sol balance
|
|
356
|
+
*
|
|
357
|
+
* Protocol Routing:
|
|
358
|
+
* - 0x01: Hyperlane (dispatch w/ quote + amount)
|
|
359
|
+
* - 0x02: LayerZero (_lzSend w/ fee + amount)
|
|
360
|
+
* - 0x03: Axelar (payNativeGas then callContract)
|
|
361
|
+
*
|
|
362
|
+
* Fee Calculation:
|
|
363
|
+
* - Hyperlane: msg.value = quote + amount
|
|
364
|
+
* - LayerZero: msg.value = fee + amount (refund)
|
|
365
|
+
* - Axelar: msg.value = gasService + amount
|
|
366
|
+
*
|
|
367
|
+
* Host Chain Integration:
|
|
368
|
+
* - Token Representation: address(0) for native ETH
|
|
369
|
+
* - Payload: Encoded with zero address
|
|
370
|
+
* - Credits: Core.sol balance as native token
|
|
371
|
+
* - Fisher Bridge: Independent nonce system
|
|
372
|
+
*
|
|
373
|
+
* Security:
|
|
374
|
+
* - Balance Check: Reverts if insufficient value
|
|
375
|
+
* - Excess Handling: LZ refunds, others use full
|
|
376
|
+
* - Validation: Host validates origin and sender
|
|
377
|
+
*
|
|
378
|
+
* @param toAddress Recipient on host chain
|
|
379
|
+
* @param amount ETH amount to bridge
|
|
380
|
+
* @param protocolToExecute 0x01=Hyperlane, 0x02=LZ,
|
|
381
|
+
* 0x03=Axelar
|
|
382
|
+
*/
|
|
314
383
|
function depositCoin(
|
|
315
384
|
address toAddress,
|
|
316
385
|
uint256 amount,
|
|
317
386
|
bytes1 protocolToExecute
|
|
318
387
|
) external payable {
|
|
319
|
-
if (msg.value < amount) revert
|
|
388
|
+
if (msg.value < amount) revert Error.InsufficientBalance();
|
|
320
389
|
|
|
321
390
|
bytes memory payload = PayloadUtils.encodePayload(
|
|
322
391
|
address(0),
|
|
@@ -327,8 +396,7 @@ contract TreasuryExternalChainStation is
|
|
|
327
396
|
if (protocolToExecute == 0x01) {
|
|
328
397
|
// 0x01 = Hyperlane
|
|
329
398
|
uint256 quote = getQuoteHyperlane(toAddress, address(0), amount);
|
|
330
|
-
if (msg.value < quote + amount)
|
|
331
|
-
revert ErrorsLib.InsufficientBalance();
|
|
399
|
+
if (msg.value < quote + amount) revert Error.InsufficientBalance();
|
|
332
400
|
/*messageId = */ IMailbox(hyperlane.mailboxAddress).dispatch{
|
|
333
401
|
value: quote
|
|
334
402
|
}(
|
|
@@ -339,8 +407,7 @@ contract TreasuryExternalChainStation is
|
|
|
339
407
|
} else if (protocolToExecute == 0x02) {
|
|
340
408
|
// 0x02 = LayerZero
|
|
341
409
|
uint256 fee = quoteLayerZero(toAddress, address(0), amount);
|
|
342
|
-
if (msg.value < fee + amount)
|
|
343
|
-
revert ErrorsLib.InsufficientBalance();
|
|
410
|
+
if (msg.value < fee + amount) revert Error.InsufficientBalance();
|
|
344
411
|
_lzSend(
|
|
345
412
|
layerZero.hostChainStationEid,
|
|
346
413
|
payload,
|
|
@@ -368,70 +435,164 @@ contract TreasuryExternalChainStation is
|
|
|
368
435
|
}
|
|
369
436
|
}
|
|
370
437
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
438
|
+
/**
|
|
439
|
+
* @notice Validates Fisher bridge receive confirmation
|
|
440
|
+
* @dev Confirms tokens sent FROM host TO external chain
|
|
441
|
+
*
|
|
442
|
+
* Purpose:
|
|
443
|
+
* - Acknowledge: Confirm host sent tokens
|
|
444
|
+
* - Validate: Signature from original sender
|
|
445
|
+
* - Track: Mark nonce as used
|
|
446
|
+
* - Security: Prevent replay attacks
|
|
447
|
+
*
|
|
448
|
+
* Signature Validation:
|
|
449
|
+
* - Payload: AdvancedStrings.buildSignaturePayload
|
|
450
|
+
* - Components: evvmID, host address, hash, nonce
|
|
451
|
+
* - Hash: TreasuryCrossChainHashUtils.hashData...
|
|
452
|
+
* - Recovers: Must match 'from' address
|
|
453
|
+
*
|
|
454
|
+
* Nonce System:
|
|
455
|
+
* - Independent: asyncNonce[from][nonce] mapping
|
|
456
|
+
* - Sequential: User manages nonce ordering
|
|
457
|
+
* - NOT Core.sol: Fisher bridge separate system
|
|
458
|
+
* - Prevention: Revert if nonce already used
|
|
459
|
+
*
|
|
460
|
+
* Integration Context:
|
|
461
|
+
* - Core.sol: NOT used (independent nonces)
|
|
462
|
+
* - Core.sol: NOT on external chain
|
|
463
|
+
* - SignatureRecover: ECDSA signature validation
|
|
464
|
+
* - Host Chain: Sends tokens via protocol messages
|
|
465
|
+
*
|
|
466
|
+
* Security Flow:
|
|
467
|
+
* - Check: Nonce not already used
|
|
468
|
+
* - Recover: Signer from signature payload
|
|
469
|
+
* - Validate: Recovered signer == from address
|
|
470
|
+
* - Mark: asyncNonce[from][nonce] = true
|
|
471
|
+
*
|
|
472
|
+
* @param from Original sender on host chain
|
|
473
|
+
* @param addressToReceive Recipient on external chain
|
|
474
|
+
* @param tokenAddress Token (address(0) for ETH)
|
|
475
|
+
* @param priorityFee Fee for priority processing
|
|
476
|
+
* @param amount Token amount received
|
|
477
|
+
* @param nonce Sequential nonce from user
|
|
478
|
+
* @param signature ECDSA signature from 'from' address
|
|
479
|
+
*/
|
|
379
480
|
function fisherBridgeReceive(
|
|
380
481
|
address from,
|
|
381
482
|
address addressToReceive,
|
|
382
483
|
address tokenAddress,
|
|
383
484
|
uint256 priorityFee,
|
|
384
485
|
uint256 amount,
|
|
486
|
+
uint256 nonce,
|
|
385
487
|
bytes memory signature
|
|
386
488
|
) external onlyFisherExecutor {
|
|
489
|
+
if (asyncNonce[from][nonce]) revert CoreError.AsyncNonceAlreadyUsed();
|
|
490
|
+
|
|
387
491
|
if (
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
492
|
+
SignatureRecover.recoverSigner(
|
|
493
|
+
AdvancedStrings.buildSignaturePayload(
|
|
494
|
+
evvmID,
|
|
495
|
+
hostChainAddress.currentAddress,
|
|
496
|
+
Hash.hashDataForFisherBridge(
|
|
497
|
+
addressToReceive,
|
|
498
|
+
tokenAddress,
|
|
499
|
+
priorityFee,
|
|
500
|
+
amount
|
|
501
|
+
),
|
|
502
|
+
fisherExecutor.current,
|
|
503
|
+
nonce,
|
|
504
|
+
true
|
|
505
|
+
),
|
|
396
506
|
signature
|
|
397
|
-
)
|
|
398
|
-
) revert
|
|
507
|
+
) != from
|
|
508
|
+
) revert CoreError.InvalidSignature();
|
|
399
509
|
|
|
400
|
-
|
|
510
|
+
asyncNonce[from][nonce] = true;
|
|
401
511
|
}
|
|
402
512
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
513
|
+
/**
|
|
514
|
+
* @notice Executes Fisher bridge ERC20 deposit to host
|
|
515
|
+
* @dev Validates signature, deposits, emits event
|
|
516
|
+
*
|
|
517
|
+
* Purpose:
|
|
518
|
+
* - Deposit: Transfer ERC20 from user to contract
|
|
519
|
+
* - Validate: ECDSA signature from sender
|
|
520
|
+
* - Track: Mark nonce as used
|
|
521
|
+
* - Emit: Log for Fisher executor on host chain
|
|
522
|
+
*
|
|
523
|
+
* Fisher Bridge Flow:
|
|
524
|
+
* - External: User signs intent + executor calls this
|
|
525
|
+
* - Deposit: Tokens held in this contract
|
|
526
|
+
* - Event: FisherBridgeSend logged
|
|
527
|
+
* - Host: Fisher monitors events + credits balance
|
|
528
|
+
* - Core.sol: Host chain credits recipient balance
|
|
529
|
+
*
|
|
530
|
+
* Signature Validation:
|
|
531
|
+
* - Payload: evvmID + host address + hash + nonce
|
|
532
|
+
* - Hash: hashDataForFisherBridge(to, token, fee,
|
|
533
|
+
* amt)
|
|
534
|
+
* - Recover: SignatureRecover.recoverSigner
|
|
535
|
+
* - Match: Recovered signer must equal 'from'
|
|
536
|
+
*
|
|
537
|
+
* Nonce System:
|
|
538
|
+
* - Independent: asyncNonce[from][nonce]
|
|
539
|
+
* - NOT Core.sol: Separate from EVVM nonces
|
|
540
|
+
* - Sequential: User manages own nonces
|
|
541
|
+
* - Replay Prevention: Mark used after validation
|
|
542
|
+
*
|
|
543
|
+
* Integration Context:
|
|
544
|
+
* - Core.sol: NOT used (Fisher independent)
|
|
545
|
+
* - Core.sol: Credits balance on host chain
|
|
546
|
+
* - Fisher Executor: Monitors events + processes
|
|
547
|
+
* - Host Station: Receives event + credits user
|
|
548
|
+
*
|
|
549
|
+
* Security:
|
|
550
|
+
* - Approval: Requires token approval first
|
|
551
|
+
* - Signature: ECDSA validation prevents forgery
|
|
552
|
+
* - Nonce: Sequential tracking prevents replays
|
|
553
|
+
* - Executor Only: onlyFisherExecutor modifier
|
|
554
|
+
*
|
|
555
|
+
* @param from Original sender (signer)
|
|
556
|
+
* @param addressToReceive Recipient on host chain
|
|
557
|
+
* @param tokenAddress ERC20 token address
|
|
558
|
+
* @param priorityFee Fee for priority processing
|
|
559
|
+
* @param amount Token amount to bridge
|
|
560
|
+
* @param nonce Sequential nonce from user
|
|
561
|
+
* @param signature ECDSA signature from 'from'
|
|
562
|
+
*/
|
|
411
563
|
function fisherBridgeSendERC20(
|
|
412
564
|
address from,
|
|
413
565
|
address addressToReceive,
|
|
414
566
|
address tokenAddress,
|
|
415
567
|
uint256 priorityFee,
|
|
416
568
|
uint256 amount,
|
|
569
|
+
uint256 nonce,
|
|
417
570
|
bytes memory signature
|
|
418
571
|
) external onlyFisherExecutor {
|
|
572
|
+
if (asyncNonce[from][nonce]) revert CoreError.AsyncNonceAlreadyUsed();
|
|
573
|
+
|
|
419
574
|
if (
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
575
|
+
SignatureRecover.recoverSigner(
|
|
576
|
+
AdvancedStrings.buildSignaturePayload(
|
|
577
|
+
evvmID,
|
|
578
|
+
hostChainAddress.currentAddress,
|
|
579
|
+
Hash.hashDataForFisherBridge(
|
|
580
|
+
addressToReceive,
|
|
581
|
+
tokenAddress,
|
|
582
|
+
priorityFee,
|
|
583
|
+
amount
|
|
584
|
+
),
|
|
585
|
+
fisherExecutor.current,
|
|
586
|
+
nonce,
|
|
587
|
+
true
|
|
588
|
+
),
|
|
428
589
|
signature
|
|
429
|
-
)
|
|
430
|
-
) revert
|
|
590
|
+
) != from
|
|
591
|
+
) revert CoreError.InvalidSignature();
|
|
431
592
|
|
|
432
593
|
verifyAndDepositERC20(tokenAddress, amount);
|
|
433
594
|
|
|
434
|
-
|
|
595
|
+
asyncNonce[from][nonce] = true;
|
|
435
596
|
|
|
436
597
|
emit FisherBridgeSend(
|
|
437
598
|
from,
|
|
@@ -439,41 +600,98 @@ contract TreasuryExternalChainStation is
|
|
|
439
600
|
tokenAddress,
|
|
440
601
|
priorityFee,
|
|
441
602
|
amount,
|
|
442
|
-
|
|
603
|
+
nonce
|
|
443
604
|
);
|
|
444
605
|
}
|
|
445
606
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
607
|
+
/**
|
|
608
|
+
* @notice Executes Fisher bridge ETH deposit to host
|
|
609
|
+
* @dev Validates signature and exact payment
|
|
610
|
+
*
|
|
611
|
+
* Purpose:
|
|
612
|
+
* - Deposit: Receive ETH from executor caller
|
|
613
|
+
* - Validate: ECDSA signature from original sender
|
|
614
|
+
* - Track: Mark nonce as used
|
|
615
|
+
* - Emit: Log for Fisher executor on host chain
|
|
616
|
+
*
|
|
617
|
+
* Fisher Bridge Flow:
|
|
618
|
+
* - External: User signs intent + pays executor
|
|
619
|
+
* - Deposit: msg.value = amount + priorityFee
|
|
620
|
+
* - Event: FisherBridgeSend with address(0)
|
|
621
|
+
* - Host: Fisher monitors + credits Evvm balance
|
|
622
|
+
* - Core.sol: Host chain credits recipient
|
|
623
|
+
*
|
|
624
|
+
* Payment Validation:
|
|
625
|
+
* - Exact Match: msg.value == amount + priorityFee
|
|
626
|
+
* - No Excess: Prevents overpayment mistakes
|
|
627
|
+
* - Native Token: Represented as address(0)
|
|
628
|
+
* - Host Credits: Full amount to recipient
|
|
629
|
+
*
|
|
630
|
+
* Signature Validation:
|
|
631
|
+
* - Payload: evvmID + host + hash + nonce
|
|
632
|
+
* - Hash: hashDataForFisherBridge(to, 0x0, fee,
|
|
633
|
+
* amt)
|
|
634
|
+
* - Token: address(0) represents native ETH
|
|
635
|
+
* - Recover: Must match 'from' address
|
|
636
|
+
*
|
|
637
|
+
* Nonce System:
|
|
638
|
+
* - Independent: asyncNonce[from][nonce]
|
|
639
|
+
* - NOT Core.sol: Fisher bridge separate
|
|
640
|
+
* - Sequential: User-managed ordering
|
|
641
|
+
* - Anti-Replay: Mark used after validation
|
|
642
|
+
*
|
|
643
|
+
* Integration Context:
|
|
644
|
+
* - Core.sol: NOT used (independent system)
|
|
645
|
+
* - Core.sol: Credits native balance on host
|
|
646
|
+
* - Fisher Executor: Pays ETH + processes
|
|
647
|
+
* - Host Station: Credits recipient balance
|
|
648
|
+
*
|
|
649
|
+
* Security:
|
|
650
|
+
* - Exact Payment: Prevents partial payments
|
|
651
|
+
* - Signature: ECDSA prevents unauthorized sends
|
|
652
|
+
* - Nonce: Sequential tracking prevents replays
|
|
653
|
+
* - Executor Only: onlyFisherExecutor modifier
|
|
654
|
+
*
|
|
655
|
+
* @param from Original sender (signer)
|
|
656
|
+
* @param addressToReceive Recipient on host chain
|
|
657
|
+
* @param priorityFee Fee for priority processing
|
|
658
|
+
* @param amount ETH amount to bridge
|
|
659
|
+
* @param nonce Sequential nonce from user
|
|
660
|
+
* @param signature ECDSA signature from 'from'
|
|
661
|
+
*/
|
|
453
662
|
function fisherBridgeSendCoin(
|
|
454
663
|
address from,
|
|
455
664
|
address addressToReceive,
|
|
456
665
|
uint256 priorityFee,
|
|
457
666
|
uint256 amount,
|
|
667
|
+
uint256 nonce,
|
|
458
668
|
bytes memory signature
|
|
459
669
|
) external payable onlyFisherExecutor {
|
|
670
|
+
if (asyncNonce[from][nonce]) revert CoreError.AsyncNonceAlreadyUsed();
|
|
671
|
+
|
|
460
672
|
if (
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
673
|
+
SignatureRecover.recoverSigner(
|
|
674
|
+
AdvancedStrings.buildSignaturePayload(
|
|
675
|
+
evvmID,
|
|
676
|
+
hostChainAddress.currentAddress,
|
|
677
|
+
Hash.hashDataForFisherBridge(
|
|
678
|
+
addressToReceive,
|
|
679
|
+
address(0),
|
|
680
|
+
priorityFee,
|
|
681
|
+
amount
|
|
682
|
+
),
|
|
683
|
+
fisherExecutor.current,
|
|
684
|
+
nonce,
|
|
685
|
+
true
|
|
686
|
+
),
|
|
469
687
|
signature
|
|
470
|
-
)
|
|
471
|
-
) revert
|
|
688
|
+
) != from
|
|
689
|
+
) revert CoreError.InvalidSignature();
|
|
472
690
|
|
|
473
691
|
if (msg.value != amount + priorityFee)
|
|
474
|
-
revert
|
|
692
|
+
revert Error.InsufficientBalance();
|
|
475
693
|
|
|
476
|
-
|
|
694
|
+
asyncNonce[from][nonce] = true;
|
|
477
695
|
|
|
478
696
|
emit FisherBridgeSend(
|
|
479
697
|
from,
|
|
@@ -481,18 +699,44 @@ contract TreasuryExternalChainStation is
|
|
|
481
699
|
address(0),
|
|
482
700
|
priorityFee,
|
|
483
701
|
amount,
|
|
484
|
-
|
|
702
|
+
nonce
|
|
485
703
|
);
|
|
486
704
|
}
|
|
487
705
|
|
|
488
706
|
// Hyperlane Specific Functions //
|
|
489
707
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
708
|
+
/**
|
|
709
|
+
* @notice Quotes Hyperlane cross-chain message fee
|
|
710
|
+
* @dev Queries mailbox for accurate fee estimation
|
|
711
|
+
*
|
|
712
|
+
* Purpose:
|
|
713
|
+
* - Estimate: Calculate native token fee for message
|
|
714
|
+
* - Quote: Query Hyperlane mailbox contract
|
|
715
|
+
* - Planning: Users know cost before depositERC20
|
|
716
|
+
* - Payment: Fee paid in msg.value on deposit
|
|
717
|
+
*
|
|
718
|
+
* Hyperlane Fee Model:
|
|
719
|
+
* - Calculation: mailbox.quoteDispatch
|
|
720
|
+
* - Components: Destination domain + payload size
|
|
721
|
+
* - Payment: Native token to mailbox
|
|
722
|
+
* - Delivery: Relayers submit to destination
|
|
723
|
+
*
|
|
724
|
+
* Quote Components:
|
|
725
|
+
* - Domain ID: hyperlane.hostChainStationDomainId
|
|
726
|
+
* - Recipient: hyperlane.hostChainStationAddress
|
|
727
|
+
* - Payload: PayloadUtils.encodePayload(token, to,
|
|
728
|
+
* amt)
|
|
729
|
+
*
|
|
730
|
+
* Usage:
|
|
731
|
+
* - Pre-Deposit: Call to estimate required msg.value
|
|
732
|
+
* - Display: Show users total cost
|
|
733
|
+
* - Payment: depositERC20 uses quote for dispatch
|
|
734
|
+
*
|
|
735
|
+
* @param toAddress Recipient on host chain
|
|
736
|
+
* @param token Token address (or 0x0 for ETH)
|
|
737
|
+
* @param amount Token amount to bridge
|
|
738
|
+
* @return Native token fee for Hyperlane message
|
|
739
|
+
*/
|
|
496
740
|
function getQuoteHyperlane(
|
|
497
741
|
address toAddress,
|
|
498
742
|
address token,
|
|
@@ -506,36 +750,97 @@ contract TreasuryExternalChainStation is
|
|
|
506
750
|
);
|
|
507
751
|
}
|
|
508
752
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
753
|
+
/**
|
|
754
|
+
* @notice Handles incoming Hyperlane messages
|
|
755
|
+
* @dev Validates origin, sender, processes payload
|
|
756
|
+
*
|
|
757
|
+
* Purpose:
|
|
758
|
+
* - Receive: Messages from host chain via Hyperlane
|
|
759
|
+
* - Validate: Origin domain + sender address
|
|
760
|
+
* - Process: Decode payload + transfer tokens
|
|
761
|
+
* - Security: Multi-layer validation checks
|
|
762
|
+
*
|
|
763
|
+
* Hyperlane Message Flow:
|
|
764
|
+
* - Host: TreasuryHostChainStation dispatches
|
|
765
|
+
* - Relayer: Submits message to this chain
|
|
766
|
+
* - Mailbox: Calls this handle() function
|
|
767
|
+
* - Process: decodeAndGive transfers tokens
|
|
768
|
+
*
|
|
769
|
+
* Validation Layers:
|
|
770
|
+
* - Caller: Must be hyperlane.mailboxAddress
|
|
771
|
+
* - Sender: Must be hyperlane.hostChainStation...
|
|
772
|
+
* - Origin: Must be hyperlane.hostChainStation
|
|
773
|
+
* DomainId
|
|
774
|
+
* - All checks prevent unauthorized messages
|
|
775
|
+
*
|
|
776
|
+
* Payload Processing:
|
|
777
|
+
* - Decode: PayloadUtils.decodePayload(_data)
|
|
778
|
+
* - Extract: token address, recipient, amount
|
|
779
|
+
* - Transfer: ETH (address 0) or ERC20 tokens
|
|
780
|
+
* - Recipient: Receives tokens on external chain
|
|
781
|
+
*
|
|
782
|
+
* Security:
|
|
783
|
+
* - Mailbox Only: Reverts if caller not mailbox
|
|
784
|
+
* - Sender Check: Reverts if not host station
|
|
785
|
+
* - Domain Check: Reverts if wrong origin
|
|
786
|
+
* - Three-layer validation prevents attacks
|
|
787
|
+
*
|
|
788
|
+
* @param _origin Source chain domain ID
|
|
789
|
+
* @param _sender Sender address (host station)
|
|
790
|
+
* @param _data Encoded payload (token, to, amount)
|
|
791
|
+
*/
|
|
514
792
|
function handle(
|
|
515
793
|
uint32 _origin,
|
|
516
794
|
bytes32 _sender,
|
|
517
795
|
bytes calldata _data
|
|
518
796
|
) external payable virtual {
|
|
519
797
|
if (msg.sender != hyperlane.mailboxAddress)
|
|
520
|
-
revert
|
|
798
|
+
revert Error.MailboxNotAuthorized();
|
|
521
799
|
|
|
522
800
|
if (_sender != hyperlane.hostChainStationAddress)
|
|
523
|
-
revert
|
|
801
|
+
revert Error.SenderNotAuthorized();
|
|
524
802
|
|
|
525
803
|
if (_origin != hyperlane.hostChainStationDomainId)
|
|
526
|
-
revert
|
|
804
|
+
revert Error.ChainIdNotAuthorized();
|
|
527
805
|
|
|
528
806
|
decodeAndGive(_data);
|
|
529
807
|
}
|
|
530
808
|
|
|
531
809
|
// LayerZero Specific Functions //
|
|
532
810
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
811
|
+
/**
|
|
812
|
+
* @notice Quotes LayerZero cross-chain message fee
|
|
813
|
+
* @dev Queries endpoint for native fee estimation
|
|
814
|
+
*
|
|
815
|
+
* Purpose:
|
|
816
|
+
* - Estimate: Calculate native token fee for LZ send
|
|
817
|
+
* - Quote: Query LayerZero V2 endpoint
|
|
818
|
+
* - Planning: Users know cost before deposit
|
|
819
|
+
* - Payment: Fee paid in msg.value on deposit
|
|
820
|
+
*
|
|
821
|
+
* LayerZero Fee Model:
|
|
822
|
+
* - Calculation: _quote (internal OApp function)
|
|
823
|
+
* - Components: Destination eid + payload + options
|
|
824
|
+
* - Options: 200k gas limit for execution
|
|
825
|
+
* - Refund: Excess fees returned to sender
|
|
826
|
+
*
|
|
827
|
+
* Quote Components:
|
|
828
|
+
* - Endpoint ID: layerZero.hostChainStationEid
|
|
829
|
+
* - Payload: PayloadUtils.encodePayload
|
|
830
|
+
* - Options: Pre-built with 200k gas limit
|
|
831
|
+
* - Pay ZRO: false (only native token)
|
|
832
|
+
*
|
|
833
|
+
* Usage:
|
|
834
|
+
* - Pre-Deposit: Call to estimate required msg.value
|
|
835
|
+
* - Display: Show users total cost
|
|
836
|
+
* - Payment: depositERC20/Coin uses quote
|
|
837
|
+
* - Refund: LZ returns excess to sender
|
|
838
|
+
*
|
|
839
|
+
* @param toAddress Recipient on host chain
|
|
840
|
+
* @param token Token address (or 0x0 for ETH)
|
|
841
|
+
* @param amount Token amount to bridge
|
|
842
|
+
* @return Native token fee for LayerZero message
|
|
843
|
+
*/
|
|
539
844
|
function quoteLayerZero(
|
|
540
845
|
address toAddress,
|
|
541
846
|
address token,
|
|
@@ -550,10 +855,46 @@ contract TreasuryExternalChainStation is
|
|
|
550
855
|
return fee.nativeFee;
|
|
551
856
|
}
|
|
552
857
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
858
|
+
/**
|
|
859
|
+
* @notice Handles incoming LayerZero messages
|
|
860
|
+
* @dev Validates origin + sender, processes payload
|
|
861
|
+
*
|
|
862
|
+
* Purpose:
|
|
863
|
+
* - Receive: Messages from host via LayerZero V2
|
|
864
|
+
* - Validate: Origin eid + sender address
|
|
865
|
+
* - Process: Decode payload + transfer tokens
|
|
866
|
+
* - Security: Multi-layer validation checks
|
|
867
|
+
*
|
|
868
|
+
* LayerZero Message Flow:
|
|
869
|
+
* - Host: TreasuryHostChainStation.lzSend
|
|
870
|
+
* - DVNs: Verify message across networks
|
|
871
|
+
* - Executor: Submits message to this chain
|
|
872
|
+
* - Endpoint: Calls this _lzReceive function
|
|
873
|
+
*
|
|
874
|
+
* Validation Layers:
|
|
875
|
+
* - Origin EID: Must be layerZero.hostChainStation
|
|
876
|
+
* Eid
|
|
877
|
+
* - Sender: Must be layerZero.hostChainStation
|
|
878
|
+
* Address
|
|
879
|
+
* - Peer: OApp validates via _getPeerOrRevert
|
|
880
|
+
* - All checks prevent unauthorized messages
|
|
881
|
+
*
|
|
882
|
+
* Payload Processing:
|
|
883
|
+
* - Decode: PayloadUtils.decodePayload(message)
|
|
884
|
+
* - Extract: token address, recipient, amount
|
|
885
|
+
* - Transfer: ETH (address 0) or ERC20 tokens
|
|
886
|
+
* - Recipient: Receives tokens on external chain
|
|
887
|
+
*
|
|
888
|
+
* Security:
|
|
889
|
+
* - EID Check: Reverts if wrong source endpoint
|
|
890
|
+
* - Sender Check: Reverts if not host station
|
|
891
|
+
* - OApp Pattern: Peer validation built-in
|
|
892
|
+
* - Two-layer validation prevents attacks
|
|
893
|
+
*
|
|
894
|
+
* @param _origin Origin info (srcEid, sender,
|
|
895
|
+
* nonce)
|
|
896
|
+
* @param message Encoded payload (token, to, amount)
|
|
897
|
+
*/
|
|
557
898
|
function _lzReceive(
|
|
558
899
|
Origin calldata _origin,
|
|
559
900
|
bytes32 /*_guid*/,
|
|
@@ -563,10 +904,10 @@ contract TreasuryExternalChainStation is
|
|
|
563
904
|
) internal override {
|
|
564
905
|
// Decode the payload to get the message
|
|
565
906
|
if (_origin.srcEid != layerZero.hostChainStationEid)
|
|
566
|
-
revert
|
|
907
|
+
revert Error.ChainIdNotAuthorized();
|
|
567
908
|
|
|
568
909
|
if (_origin.sender != layerZero.hostChainStationAddress)
|
|
569
|
-
revert
|
|
910
|
+
revert Error.SenderNotAuthorized();
|
|
570
911
|
|
|
571
912
|
decodeAndGive(message);
|
|
572
913
|
}
|
|
@@ -606,11 +947,52 @@ contract TreasuryExternalChainStation is
|
|
|
606
947
|
|
|
607
948
|
// Axelar Specific Functions //
|
|
608
949
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
950
|
+
/**
|
|
951
|
+
* @notice Handles incoming Axelar messages
|
|
952
|
+
* @dev Validates source chain/address, processes payload
|
|
953
|
+
*
|
|
954
|
+
* Purpose:
|
|
955
|
+
* - Receive: Messages from host via Axelar Network
|
|
956
|
+
* - Validate: Source chain name + sender address
|
|
957
|
+
* - Process: Decode payload + transfer tokens
|
|
958
|
+
* - Security: Multi-layer validation checks
|
|
959
|
+
*
|
|
960
|
+
* Axelar Message Flow:
|
|
961
|
+
* - Host: TreasuryHostChainStation.callContract
|
|
962
|
+
* - Axelar: Validates via validator network
|
|
963
|
+
* - Gateway: Calls this _execute function
|
|
964
|
+
* - Process: decodeAndGive transfers tokens
|
|
965
|
+
*
|
|
966
|
+
* Validation Layers:
|
|
967
|
+
* - Source Chain: Must be axelar.hostChainStation
|
|
968
|
+
* ChainName
|
|
969
|
+
* - Source Address: Must be axelar.hostChainStation
|
|
970
|
+
* Address
|
|
971
|
+
* - Gateway: AxelarExecutable validates caller
|
|
972
|
+
* - All checks prevent unauthorized messages
|
|
973
|
+
*
|
|
974
|
+
* String Comparison:
|
|
975
|
+
* - AdvancedStrings.equal: Chain name validation
|
|
976
|
+
* - Case-Sensitive: Exact match required
|
|
977
|
+
* - Address Format: String type for Axelar
|
|
978
|
+
* - Security: Double validation of source
|
|
979
|
+
*
|
|
980
|
+
* Payload Processing:
|
|
981
|
+
* - Decode: PayloadUtils.decodePayload(_payload)
|
|
982
|
+
* - Extract: token address, recipient, amount
|
|
983
|
+
* - Transfer: ETH (address 0) or ERC20 tokens
|
|
984
|
+
* - Recipient: Receives tokens on external chain
|
|
985
|
+
*
|
|
986
|
+
* Security:
|
|
987
|
+
* - Chain Check: Reverts if wrong source chain
|
|
988
|
+
* - Address Check: Reverts if not host station
|
|
989
|
+
* - Gateway Pattern: AxelarExecutable validation
|
|
990
|
+
* - Two-layer validation prevents attacks
|
|
991
|
+
*
|
|
992
|
+
* @param _sourceChain Source blockchain name
|
|
993
|
+
* @param _sourceAddress Source contract address
|
|
994
|
+
* @param _payload Encoded payload (token, to, amount)
|
|
995
|
+
*/
|
|
614
996
|
function _execute(
|
|
615
997
|
bytes32 /*commandId*/,
|
|
616
998
|
string calldata _sourceChain,
|
|
@@ -622,14 +1004,14 @@ contract TreasuryExternalChainStation is
|
|
|
622
1004
|
_sourceChain,
|
|
623
1005
|
axelar.hostChainStationChainName
|
|
624
1006
|
)
|
|
625
|
-
) revert
|
|
1007
|
+
) revert Error.ChainIdNotAuthorized();
|
|
626
1008
|
|
|
627
1009
|
if (
|
|
628
1010
|
!AdvancedStrings.equal(
|
|
629
1011
|
_sourceAddress,
|
|
630
1012
|
axelar.hostChainStationAddress
|
|
631
1013
|
)
|
|
632
|
-
) revert
|
|
1014
|
+
) revert Error.SenderNotAuthorized();
|
|
633
1015
|
|
|
634
1016
|
decodeAndGive(_payload);
|
|
635
1017
|
}
|
|
@@ -711,9 +1093,10 @@ contract TreasuryExternalChainStation is
|
|
|
711
1093
|
) external onlyAdmin {
|
|
712
1094
|
if (fuseSetHostChainAddress == 0x01) revert();
|
|
713
1095
|
|
|
714
|
-
|
|
1096
|
+
hostChainAddress = Structs.ChangeHostChainAddressParams({
|
|
715
1097
|
porposeAddress_AddressType: hostChainStationAddress,
|
|
716
1098
|
porposeAddress_StringType: hostChainStationAddressString,
|
|
1099
|
+
currentAddress: hostChainAddress.currentAddress,
|
|
717
1100
|
timeToAccept: block.timestamp + 1 minutes
|
|
718
1101
|
});
|
|
719
1102
|
}
|
|
@@ -721,9 +1104,10 @@ contract TreasuryExternalChainStation is
|
|
|
721
1104
|
/// @notice Cancels a pending host chain address change proposal
|
|
722
1105
|
/// @dev Resets the host chain address proposal to default state
|
|
723
1106
|
function rejectProposalHostChainAddress() external onlyAdmin {
|
|
724
|
-
|
|
1107
|
+
hostChainAddress = Structs.ChangeHostChainAddressParams({
|
|
725
1108
|
porposeAddress_AddressType: address(0),
|
|
726
1109
|
porposeAddress_StringType: "",
|
|
1110
|
+
currentAddress: hostChainAddress.currentAddress,
|
|
727
1111
|
timeToAccept: 0
|
|
728
1112
|
});
|
|
729
1113
|
}
|
|
@@ -731,37 +1115,39 @@ contract TreasuryExternalChainStation is
|
|
|
731
1115
|
/// @notice Accepts pending host chain address changes across all protocols
|
|
732
1116
|
/// @dev Updates Hyperlane, LayerZero, and Axelar configurations simultaneously
|
|
733
1117
|
function acceptHostChainAddress() external {
|
|
734
|
-
if (block.timestamp <
|
|
735
|
-
revert();
|
|
1118
|
+
if (block.timestamp < hostChainAddress.timeToAccept) revert();
|
|
736
1119
|
|
|
737
1120
|
hyperlane.hostChainStationAddress = bytes32(
|
|
738
|
-
uint256(
|
|
739
|
-
uint160(
|
|
740
|
-
hostChainAddressChangeProposal.porposeAddress_AddressType
|
|
741
|
-
)
|
|
742
|
-
)
|
|
1121
|
+
uint256(uint160(hostChainAddress.porposeAddress_AddressType))
|
|
743
1122
|
);
|
|
744
1123
|
layerZero.hostChainStationAddress = bytes32(
|
|
745
|
-
uint256(
|
|
746
|
-
uint160(
|
|
747
|
-
hostChainAddressChangeProposal.porposeAddress_AddressType
|
|
748
|
-
)
|
|
749
|
-
)
|
|
1124
|
+
uint256(uint160(hostChainAddress.porposeAddress_AddressType))
|
|
750
1125
|
);
|
|
751
|
-
axelar.hostChainStationAddress =
|
|
1126
|
+
axelar.hostChainStationAddress = hostChainAddress
|
|
752
1127
|
.porposeAddress_StringType;
|
|
753
1128
|
|
|
754
1129
|
_setPeer(
|
|
755
1130
|
layerZero.hostChainStationEid,
|
|
756
1131
|
layerZero.hostChainStationAddress
|
|
757
1132
|
);
|
|
1133
|
+
|
|
1134
|
+
hostChainAddress = Structs.ChangeHostChainAddressParams({
|
|
1135
|
+
porposeAddress_AddressType: address(0),
|
|
1136
|
+
porposeAddress_StringType: "",
|
|
1137
|
+
currentAddress: hostChainAddress.porposeAddress_AddressType,
|
|
1138
|
+
timeToAccept: 0
|
|
1139
|
+
});
|
|
758
1140
|
}
|
|
759
1141
|
|
|
760
1142
|
// Getter functions //
|
|
761
1143
|
|
|
762
1144
|
/// @notice Returns the complete admin configuration including proposals and timelock
|
|
763
1145
|
/// @return Current admin address, proposed admin, and acceptance timestamp
|
|
764
|
-
function getAdmin()
|
|
1146
|
+
function getAdmin()
|
|
1147
|
+
external
|
|
1148
|
+
view
|
|
1149
|
+
returns (ProposalStructs.AddressTypeProposal memory)
|
|
1150
|
+
{
|
|
765
1151
|
return admin;
|
|
766
1152
|
}
|
|
767
1153
|
|
|
@@ -770,19 +1156,16 @@ contract TreasuryExternalChainStation is
|
|
|
770
1156
|
function getFisherExecutor()
|
|
771
1157
|
external
|
|
772
1158
|
view
|
|
773
|
-
returns (AddressTypeProposal memory)
|
|
1159
|
+
returns (ProposalStructs.AddressTypeProposal memory)
|
|
774
1160
|
{
|
|
775
1161
|
return fisherExecutor;
|
|
776
1162
|
}
|
|
777
1163
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
address user
|
|
784
|
-
) external view returns (uint256) {
|
|
785
|
-
return nextFisherExecutionNonce[user];
|
|
1164
|
+
function getIfUsedAsyncNonce(
|
|
1165
|
+
address user,
|
|
1166
|
+
uint256 nonce
|
|
1167
|
+
) public view virtual returns (bool) {
|
|
1168
|
+
return asyncNonce[user][nonce];
|
|
786
1169
|
}
|
|
787
1170
|
|
|
788
1171
|
/// @notice Returns the complete Hyperlane protocol configuration
|
|
@@ -790,7 +1173,7 @@ contract TreasuryExternalChainStation is
|
|
|
790
1173
|
function getHyperlaneConfig()
|
|
791
1174
|
external
|
|
792
1175
|
view
|
|
793
|
-
returns (HyperlaneConfig memory)
|
|
1176
|
+
returns (Structs.HyperlaneConfig memory)
|
|
794
1177
|
{
|
|
795
1178
|
return hyperlane;
|
|
796
1179
|
}
|
|
@@ -800,14 +1183,18 @@ contract TreasuryExternalChainStation is
|
|
|
800
1183
|
function getLayerZeroConfig()
|
|
801
1184
|
external
|
|
802
1185
|
view
|
|
803
|
-
returns (LayerZeroConfig memory)
|
|
1186
|
+
returns (Structs.LayerZeroConfig memory)
|
|
804
1187
|
{
|
|
805
1188
|
return layerZero;
|
|
806
1189
|
}
|
|
807
1190
|
|
|
808
1191
|
/// @notice Returns the complete Axelar protocol configuration
|
|
809
1192
|
/// @return Axelar configuration including chain name, addresses, gas service, and gateway
|
|
810
|
-
function getAxelarConfig()
|
|
1193
|
+
function getAxelarConfig()
|
|
1194
|
+
external
|
|
1195
|
+
view
|
|
1196
|
+
returns (Structs.AxelarConfig memory)
|
|
1197
|
+
{
|
|
811
1198
|
return axelar;
|
|
812
1199
|
}
|
|
813
1200
|
|
|
@@ -837,7 +1224,7 @@ contract TreasuryExternalChainStation is
|
|
|
837
1224
|
function verifyAndDepositERC20(address token, uint256 amount) internal {
|
|
838
1225
|
if (token == address(0)) revert();
|
|
839
1226
|
if (IERC20(token).allowance(msg.sender, address(this)) < amount)
|
|
840
|
-
revert
|
|
1227
|
+
revert Error.InsufficientBalance();
|
|
841
1228
|
|
|
842
1229
|
IERC20(token).transferFrom(msg.sender, address(this), amount);
|
|
843
1230
|
}
|
|
@@ -851,4 +1238,4 @@ contract TreasuryExternalChainStation is
|
|
|
851
1238
|
/// @notice Disabled ownership renouncement function for security
|
|
852
1239
|
/// @dev Prevents accidental loss of administrative control over the contract
|
|
853
1240
|
function renounceOwnership() public virtual override onlyOwner {}
|
|
854
|
-
}
|
|
1241
|
+
}
|