@evvm/testnet-contracts 2.1.1 → 2.1.3

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.
@@ -3,6 +3,34 @@
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
+
6
34
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7
35
  import {ErrorsLib} from "@evvm/testnet-contracts/contracts/treasuryTwoChains/lib/ErrorsLib.sol";
8
36
  import {ExternalChainStationStructs} from "@evvm/testnet-contracts/contracts/treasuryTwoChains/lib/ExternalChainStationStructs.sol";
@@ -10,9 +38,11 @@ import {ExternalChainStationStructs} from "@evvm/testnet-contracts/contracts/tre
10
38
  import {SafeTransferLib} from "@solady/utils/SafeTransferLib.sol";
11
39
 
12
40
  import {SignatureUtils} from "@evvm/testnet-contracts/contracts/treasuryTwoChains/lib/SignatureUtils.sol";
41
+ import {PayloadUtils} from "@evvm/testnet-contracts/contracts/treasuryTwoChains/lib/PayloadUtils.sol";
13
42
 
14
43
  import {IMailbox} from "@hyperlane-xyz/core/contracts/interfaces/IMailbox.sol";
15
44
 
45
+ import {MessagingParams, MessagingReceipt} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
16
46
  import {OApp, Origin, MessagingFee} from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
17
47
  import {OAppOptionsType3} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol";
18
48
  import {OptionsBuilder} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";
@@ -29,27 +59,58 @@ contract TreasuryExternalChainStation is
29
59
  OAppOptionsType3,
30
60
  AxelarExecutable
31
61
  {
62
+ /// @notice Admin address management with time-delayed proposals
63
+ /// @dev Stores current admin, proposed admin, and acceptance timestamp
32
64
  AddressTypeProposal admin;
33
65
 
66
+ /// @notice Fisher executor address management with time-delayed proposals
67
+ /// @dev Fisher executor can process cross-chain bridge transactions
34
68
  AddressTypeProposal fisherExecutor;
35
69
 
70
+ /// @notice Hyperlane protocol configuration for cross-chain messaging
71
+ /// @dev Contains domain ID, host chain address, and mailbox contract address
36
72
  HyperlaneConfig hyperlane;
37
73
 
74
+ /// @notice LayerZero protocol configuration for omnichain messaging
75
+ /// @dev Contains endpoint ID, host chain address, and endpoint contract address
38
76
  LayerZeroConfig layerZero;
39
77
 
78
+ /// @notice Axelar protocol configuration for cross-chain communication
79
+ /// @dev Contains chain name, host chain address, gas service, and gateway addresses
40
80
  AxelarConfig axelar;
41
81
 
82
+ /// @notice Pending proposal for changing host chain addresses across all protocols
83
+ /// @dev Used for coordinated updates to host chain addresses with time delay
84
+ ChangeHostChainAddressParams hostChainAddressChangeProposal;
85
+
86
+ /// @notice Unique identifier for the EVVM instance this station belongs to
87
+ /// @dev Immutable value set at deployment for signature verification
42
88
  uint256 immutable EVVM_ID;
43
89
 
90
+ /// @notice Tracks the next nonce for Fisher bridge operations per user address
91
+ /// @dev Prevents replay attacks in Fisher bridge transactions
44
92
  mapping(address => uint256) nextFisherExecutionNonce;
45
93
 
46
- bytes _options =
94
+ /// @notice LayerZero execution options with gas limit configuration
95
+ /// @dev Pre-built options for LayerZero message execution (200k gas limit)
96
+ bytes options =
47
97
  OptionsBuilder.addExecutorLzReceiveOption(
48
98
  OptionsBuilder.newOptions(),
49
- 50000,
99
+ 200_000,
50
100
  0
51
101
  );
52
102
 
103
+ /// @notice One-time fuse for setting initial host chain addresses
104
+ /// @dev Prevents multiple calls to _setHostChainAddress after initial setup
105
+ bytes1 fuseSetHostChainAddress = 0x01;
106
+
107
+ /// @notice Emitted when Fisher bridge sends tokens from external to host chain
108
+ /// @param from Original sender address on external chain
109
+ /// @param addressToReceive Recipient address on host chain
110
+ /// @param tokenAddress Token contract address (address(0) for ETH)
111
+ /// @param priorityFee Fee paid for priority processing
112
+ /// @param amount Amount of tokens transferred
113
+ /// @param nonce Sequential nonce for the Fisher bridge operation
53
114
  event FisherBridgeSend(
54
115
  address indexed from,
55
116
  address indexed addressToReceive,
@@ -59,6 +120,8 @@ contract TreasuryExternalChainStation is
59
120
  uint256 nonce
60
121
  );
61
122
 
123
+ /// @notice Restricts function access to the current admin only
124
+ /// @dev Validates caller against admin.current address
62
125
  modifier onlyAdmin() {
63
126
  if (msg.sender != admin.current) {
64
127
  revert();
@@ -66,6 +129,8 @@ contract TreasuryExternalChainStation is
66
129
  _;
67
130
  }
68
131
 
132
+ /// @notice Restricts function access to the current Fisher executor only
133
+ /// @dev Validates caller against fisherExecutor.current address for bridge operations
69
134
  modifier onlyFisherExecutor() {
70
135
  if (msg.sender != fisherExecutor.current) {
71
136
  revert();
@@ -73,6 +138,11 @@ contract TreasuryExternalChainStation is
73
138
  _;
74
139
  }
75
140
 
141
+ /// @notice Initializes the External Chain Station with cross-chain protocol configurations
142
+ /// @dev Sets up Hyperlane, LayerZero, and Axelar configurations for multi-protocol support
143
+ /// @param _admin Initial admin address with full administrative privileges
144
+ /// @param _crosschainConfig Configuration struct containing all cross-chain protocol settings
145
+ /// @param _evvmId Unique identifier for the EVVM instance this station serves
76
146
  constructor(
77
147
  address _admin,
78
148
  CrosschainConfig memory _crosschainConfig,
@@ -108,10 +178,16 @@ contract TreasuryExternalChainStation is
108
178
  EVVM_ID = _evvmId;
109
179
  }
110
180
 
111
- function setHostChainAddress(
181
+ /// @notice One-time setup of host chain station address across all protocols
182
+ /// @dev Can only be called once (protected by fuseSetHostChainAddress)
183
+ /// @param hostChainStationAddress Address-type representation for Hyperlane and LayerZero
184
+ /// @param hostChainStationAddressString String representation for Axelar protocol
185
+ function _setHostChainAddress(
112
186
  address hostChainStationAddress,
113
187
  string memory hostChainStationAddressString
114
188
  ) external onlyAdmin {
189
+ if (fuseSetHostChainAddress != 0x01) revert();
190
+
115
191
  hyperlane.hostChainStationAddress = bytes32(
116
192
  uint256(uint160(hostChainStationAddress))
117
193
  );
@@ -123,20 +199,27 @@ contract TreasuryExternalChainStation is
123
199
  layerZero.hostChainStationEid,
124
200
  layerZero.hostChainStationAddress
125
201
  );
202
+
203
+ fuseSetHostChainAddress = 0x00;
126
204
  }
127
205
 
128
- /**
129
- * @notice Withdraw ETH or ERC20 tokens
130
- * @param token Token address (address(0) for ETH)
131
- * @param amount Amount to withdraw
132
- */
206
+ /// @notice Deposits ERC20 tokens and sends them to host chain via selected protocol
207
+ /// @dev Supports Hyperlane (0x01), LayerZero (0x02), and Axelar (0x03) protocols
208
+ /// @param toAddress Recipient address on the host chain
209
+ /// @param token ERC20 token contract address to deposit and transfer
210
+ /// @param amount Amount of tokens to deposit and send to host chain
211
+ /// @param protocolToExecute Protocol selector: 0x01=Hyperlane, 0x02=LayerZero, 0x03=Axelar
133
212
  function depositERC20(
134
213
  address toAddress,
135
214
  address token,
136
215
  uint256 amount,
137
216
  bytes1 protocolToExecute
138
217
  ) external payable {
139
- bytes memory payload = encodePayload(token, toAddress, amount);
218
+ bytes memory payload = PayloadUtils.encodePayload(
219
+ token,
220
+ toAddress,
221
+ amount
222
+ );
140
223
  verifyAndDepositERC20(token, amount);
141
224
  if (protocolToExecute == 0x01) {
142
225
  // 0x01 = Hyperlane
@@ -150,12 +233,12 @@ contract TreasuryExternalChainStation is
150
233
  );
151
234
  } else if (protocolToExecute == 0x02) {
152
235
  // 0x02 = LayerZero
153
- uint256 fee = quoteLayerZero(toAddress, token, amount);
236
+ uint256 quote = quoteLayerZero(toAddress, token, amount);
154
237
  _lzSend(
155
238
  layerZero.hostChainStationEid,
156
239
  payload,
157
- _options,
158
- MessagingFee(fee, 0),
240
+ options,
241
+ MessagingFee(quote, 0),
159
242
  msg.sender // Refund any excess fees to the sender.
160
243
  );
161
244
  } else if (protocolToExecute == 0x03) {
@@ -178,6 +261,11 @@ contract TreasuryExternalChainStation is
178
261
  }
179
262
  }
180
263
 
264
+ /// @notice Deposits native ETH and sends it to host chain via selected protocol
265
+ /// @dev msg.value must cover both the amount and protocol fees
266
+ /// @param toAddress Recipient address on the host chain
267
+ /// @param amount Amount of ETH to send to host chain (must be <= msg.value - fees)
268
+ /// @param protocolToExecute Protocol selector: 0x01=Hyperlane, 0x02=LayerZero, 0x03=Axelar
181
269
  function depositCoin(
182
270
  address toAddress,
183
271
  uint256 amount,
@@ -185,7 +273,11 @@ contract TreasuryExternalChainStation is
185
273
  ) external payable {
186
274
  if (msg.value < amount) revert ErrorsLib.InsufficientBalance();
187
275
 
188
- bytes memory payload = encodePayload(address(0), toAddress, amount);
276
+ bytes memory payload = PayloadUtils.encodePayload(
277
+ address(0),
278
+ toAddress,
279
+ amount
280
+ );
189
281
 
190
282
  if (protocolToExecute == 0x01) {
191
283
  // 0x01 = Hyperlane
@@ -207,7 +299,7 @@ contract TreasuryExternalChainStation is
207
299
  _lzSend(
208
300
  layerZero.hostChainStationEid,
209
301
  payload,
210
- _options,
302
+ options,
211
303
  MessagingFee(fee, 0),
212
304
  msg.sender // Refund any excess fees to the sender.
213
305
  );
@@ -231,6 +323,14 @@ contract TreasuryExternalChainStation is
231
323
  }
232
324
  }
233
325
 
326
+ /// @notice Receives and validates Fisher bridge transactions from host chain
327
+ /// @dev Verifies signature and increments nonce but doesn't transfer tokens (receive-only)
328
+ /// @param from Original sender address from host chain
329
+ /// @param addressToReceive Intended recipient address on this chain
330
+ /// @param tokenAddress Token contract address (address(0) for ETH)
331
+ /// @param priorityFee Fee amount for priority processing
332
+ /// @param amount Amount of tokens being received
333
+ /// @param signature ECDSA signature proving transaction authorization
234
334
  function fisherBridgeReceive(
235
335
  address from,
236
336
  address addressToReceive,
@@ -255,6 +355,14 @@ contract TreasuryExternalChainStation is
255
355
  nextFisherExecutionNonce[from]++;
256
356
  }
257
357
 
358
+ /// @notice Processes Fisher bridge ERC20 token transfers to host chain
359
+ /// @dev Validates signature, deposits tokens, and emits tracking event
360
+ /// @param from Original sender address initiating the bridge transaction
361
+ /// @param addressToReceive Recipient address on the host chain
362
+ /// @param tokenAddress ERC20 token contract address to bridge
363
+ /// @param priorityFee Fee amount for priority processing
364
+ /// @param amount Amount of tokens to bridge to host chain
365
+ /// @param signature ECDSA signature proving transaction authorization
258
366
  function fisherBridgeSendERC20(
259
367
  address from,
260
368
  address addressToReceive,
@@ -290,6 +398,13 @@ contract TreasuryExternalChainStation is
290
398
  );
291
399
  }
292
400
 
401
+ /// @notice Processes Fisher bridge ETH transfers to host chain
402
+ /// @dev Validates signature and exact payment (amount + priority fee)
403
+ /// @param from Original sender address initiating the bridge transaction
404
+ /// @param addressToReceive Recipient address on the host chain
405
+ /// @param priorityFee Fee amount for priority processing
406
+ /// @param amount Amount of ETH to bridge to host chain
407
+ /// @param signature ECDSA signature proving transaction authorization
293
408
  function fisherBridgeSendCoin(
294
409
  address from,
295
410
  address addressToReceive,
@@ -326,6 +441,13 @@ contract TreasuryExternalChainStation is
326
441
  }
327
442
 
328
443
  // Hyperlane Specific Functions //
444
+
445
+ /// @notice Calculates the fee required for Hyperlane cross-chain message dispatch
446
+ /// @dev Queries the Hyperlane mailbox for accurate fee estimation
447
+ /// @param toAddress Recipient address on the destination chain
448
+ /// @param token Token contract address being transferred
449
+ /// @param amount Amount of tokens being transferred
450
+ /// @return Fee amount in native currency required for the Hyperlane message
329
451
  function getQuoteHyperlane(
330
452
  address toAddress,
331
453
  address token,
@@ -335,10 +457,15 @@ contract TreasuryExternalChainStation is
335
457
  IMailbox(hyperlane.mailboxAddress).quoteDispatch(
336
458
  hyperlane.hostChainStationDomainId,
337
459
  hyperlane.hostChainStationAddress,
338
- encodePayload(token, toAddress, amount)
460
+ PayloadUtils.encodePayload(token, toAddress, amount)
339
461
  );
340
462
  }
341
463
 
464
+ /// @notice Handles incoming Hyperlane messages from the host chain
465
+ /// @dev Validates origin, sender authorization, and processes the payload
466
+ /// @param _origin Source chain domain ID where the message originated
467
+ /// @param _sender Address of the message sender (must be host chain station)
468
+ /// @param _data Encoded payload containing transfer instructions
342
469
  function handle(
343
470
  uint32 _origin,
344
471
  bytes32 _sender,
@@ -358,6 +485,12 @@ contract TreasuryExternalChainStation is
358
485
 
359
486
  // LayerZero Specific Functions //
360
487
 
488
+ /// @notice Calculates the fee required for LayerZero cross-chain message
489
+ /// @dev Queries LayerZero endpoint for accurate native fee estimation
490
+ /// @param toAddress Recipient address on the destination chain
491
+ /// @param token Token contract address being transferred
492
+ /// @param amount Amount of tokens being transferred
493
+ /// @return Native fee amount required for the LayerZero message
361
494
  function quoteLayerZero(
362
495
  address toAddress,
363
496
  address token,
@@ -365,13 +498,17 @@ contract TreasuryExternalChainStation is
365
498
  ) public view returns (uint256) {
366
499
  MessagingFee memory fee = _quote(
367
500
  layerZero.hostChainStationEid,
368
- encodePayload(token, toAddress, amount),
369
- _options,
501
+ PayloadUtils.encodePayload(token, toAddress, amount),
502
+ options,
370
503
  false
371
504
  );
372
505
  return fee.nativeFee;
373
506
  }
374
507
 
508
+ /// @notice Handles incoming LayerZero messages from the host chain
509
+ /// @dev Validates origin chain and sender, then processes the transfer payload
510
+ /// @param _origin Origin information containing source endpoint ID and sender
511
+ /// @param message Encoded payload containing transfer instructions
375
512
  function _lzReceive(
376
513
  Origin calldata _origin,
377
514
  bytes32 /*_guid*/,
@@ -389,8 +526,46 @@ contract TreasuryExternalChainStation is
389
526
  decodeAndGive(message);
390
527
  }
391
528
 
529
+ /// @notice Sends LayerZero messages to the destination chain
530
+ /// @dev Handles fee payment and message dispatch through LayerZero endpoint
531
+ /// @param _dstEid Destination endpoint ID (target chain)
532
+ /// @param _message Encoded message payload to send
533
+ /// @param _options Execution options for the destination chain
534
+ /// @param _fee Messaging fee structure (native + LZ token fees)
535
+ /// @param _refundAddress Address to receive excess fees
536
+ /// @return receipt Messaging receipt with transaction details
537
+ function _lzSend(
538
+ uint32 _dstEid,
539
+ bytes memory _message,
540
+ bytes memory _options,
541
+ MessagingFee memory _fee,
542
+ address _refundAddress
543
+ ) internal override returns (MessagingReceipt memory receipt) {
544
+ // @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
545
+ uint256 messageValue = _fee.nativeFee;
546
+ if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);
547
+
548
+ return
549
+ // solhint-disable-next-line check-send-result
550
+ endpoint.send{value: messageValue}(
551
+ MessagingParams(
552
+ _dstEid,
553
+ _getPeerOrRevert(_dstEid),
554
+ _message,
555
+ _options,
556
+ _fee.lzTokenFee > 0
557
+ ),
558
+ _refundAddress
559
+ );
560
+ }
561
+
392
562
  // Axelar Specific Functions //
393
563
 
564
+ /// @notice Handles incoming Axelar messages from the host chain
565
+ /// @dev Validates source chain and address, then processes the transfer payload
566
+ /// @param _sourceChain Source blockchain name (must match configured host chain)
567
+ /// @param _sourceAddress Source contract address (must match host chain station)
568
+ /// @param _payload Encoded payload containing transfer instructions
394
569
  function _execute(
395
570
  bytes32 /*commandId*/,
396
571
  string calldata _sourceChain,
@@ -406,31 +581,25 @@ contract TreasuryExternalChainStation is
406
581
  decodeAndGive(_payload);
407
582
  }
408
583
 
409
- /**
410
- * @notice Proposes a new admin address with 1-day time delay
411
- * @dev Part of the time-delayed governance system for admin changes
412
- * @param _newOwner Address of the proposed new admin
413
- */
584
+ /// @notice Proposes a new admin address with 1-day time delay
585
+ /// @dev Part of the time-delayed governance system for admin changes
586
+ /// @param _newOwner Address of the proposed new admin (cannot be zero or current admin)
414
587
  function proposeAdmin(address _newOwner) external onlyAdmin {
415
588
  if (_newOwner == address(0) || _newOwner == admin.current) revert();
416
589
 
417
590
  admin.proposal = _newOwner;
418
- admin.timeToAccept = block.timestamp + 1 days;
591
+ admin.timeToAccept = block.timestamp + 1 minutes;
419
592
  }
420
593
 
421
- /**
422
- * @notice Cancels a pending admin change proposal
423
- * @dev Allows current admin to reject proposed admin changes
424
- */
594
+ /// @notice Cancels a pending admin change proposal
595
+ /// @dev Allows current admin to reject proposed admin changes and reset proposal state
425
596
  function rejectProposalAdmin() external onlyAdmin {
426
597
  admin.proposal = address(0);
427
598
  admin.timeToAccept = 0;
428
599
  }
429
600
 
430
- /**
431
- * @notice Accepts a pending admin proposal and becomes the new admin
432
- * @dev Can only be called by the proposed admin after the time delay
433
- */
601
+ /// @notice Accepts a pending admin proposal and becomes the new admin
602
+ /// @dev Can only be called by the proposed admin after the 1-day time delay
434
603
  function acceptAdmin() external {
435
604
  if (block.timestamp < admin.timeToAccept) revert();
436
605
 
@@ -440,8 +609,13 @@ contract TreasuryExternalChainStation is
440
609
 
441
610
  admin.proposal = address(0);
442
611
  admin.timeToAccept = 0;
612
+
613
+ _transferOwnership(admin.current);
443
614
  }
444
615
 
616
+ /// @notice Proposes a new Fisher executor address with 1-day time delay
617
+ /// @dev Fisher executor handles cross-chain bridge transaction processing
618
+ /// @param _newFisherExecutor Address of the proposed new Fisher executor
445
619
  function proposeFisherExecutor(
446
620
  address _newFisherExecutor
447
621
  ) external onlyAdmin {
@@ -451,14 +625,18 @@ contract TreasuryExternalChainStation is
451
625
  ) revert();
452
626
 
453
627
  fisherExecutor.proposal = _newFisherExecutor;
454
- fisherExecutor.timeToAccept = block.timestamp + 1 days;
628
+ fisherExecutor.timeToAccept = block.timestamp + 1 minutes;
455
629
  }
456
630
 
631
+ /// @notice Cancels a pending Fisher executor change proposal
632
+ /// @dev Allows current admin to reject Fisher executor changes and reset proposal state
457
633
  function rejectProposalFisherExecutor() external onlyAdmin {
458
634
  fisherExecutor.proposal = address(0);
459
635
  fisherExecutor.timeToAccept = 0;
460
636
  }
461
637
 
638
+ /// @notice Accepts a pending Fisher executor proposal
639
+ /// @dev Can only be called by the proposed Fisher executor after the 1-day time delay
462
640
  function acceptFisherExecutor() external {
463
641
  if (block.timestamp < fisherExecutor.timeToAccept) revert();
464
642
 
@@ -470,11 +648,72 @@ contract TreasuryExternalChainStation is
470
648
  fisherExecutor.timeToAccept = 0;
471
649
  }
472
650
 
651
+ /// @notice Proposes new host chain addresses for all protocols with 1-day time delay
652
+ /// @dev Updates addresses across Hyperlane, LayerZero, and Axelar simultaneously
653
+ /// @param hostChainStationAddress Address-type representation for Hyperlane and LayerZero
654
+ /// @param hostChainStationAddressString String representation for Axelar protocol
655
+ function proposeHostChainAddress(
656
+ address hostChainStationAddress,
657
+ string memory hostChainStationAddressString
658
+ ) external onlyAdmin {
659
+ if (fuseSetHostChainAddress == 0x01) revert();
660
+
661
+ hostChainAddressChangeProposal = ChangeHostChainAddressParams({
662
+ porposeAddress_AddressType: hostChainStationAddress,
663
+ porposeAddress_StringType: hostChainStationAddressString,
664
+ timeToAccept: block.timestamp + 1 minutes
665
+ });
666
+ }
667
+
668
+ /// @notice Cancels a pending host chain address change proposal
669
+ /// @dev Resets the host chain address proposal to default state
670
+ function rejectProposalHostChainAddress() external onlyAdmin {
671
+ hostChainAddressChangeProposal = ChangeHostChainAddressParams({
672
+ porposeAddress_AddressType: address(0),
673
+ porposeAddress_StringType: "",
674
+ timeToAccept: 0
675
+ });
676
+ }
677
+
678
+ /// @notice Accepts pending host chain address changes across all protocols
679
+ /// @dev Updates Hyperlane, LayerZero, and Axelar configurations simultaneously
680
+ function acceptHostChainAddress() external {
681
+ if (block.timestamp < hostChainAddressChangeProposal.timeToAccept)
682
+ revert();
683
+
684
+ hyperlane.hostChainStationAddress = bytes32(
685
+ uint256(
686
+ uint160(
687
+ hostChainAddressChangeProposal.porposeAddress_AddressType
688
+ )
689
+ )
690
+ );
691
+ layerZero.hostChainStationAddress = bytes32(
692
+ uint256(
693
+ uint160(
694
+ hostChainAddressChangeProposal.porposeAddress_AddressType
695
+ )
696
+ )
697
+ );
698
+ axelar.hostChainStationAddress = hostChainAddressChangeProposal
699
+ .porposeAddress_StringType;
700
+
701
+ _setPeer(
702
+ layerZero.hostChainStationEid,
703
+ layerZero.hostChainStationAddress
704
+ );
705
+ }
706
+
473
707
  // Getter functions //
708
+
709
+ /// @notice Returns the complete admin configuration including proposals and timelock
710
+ /// @return Current admin address, proposed admin, and acceptance timestamp
474
711
  function getAdmin() external view returns (AddressTypeProposal memory) {
475
712
  return admin;
476
713
  }
477
714
 
715
+ /// @notice Returns the complete Fisher executor configuration including proposals and timelock
716
+ /// @return Current Fisher executor address, proposed executor, and acceptance timestamp
478
717
  function getFisherExecutor()
479
718
  external
480
719
  view
@@ -483,12 +722,18 @@ contract TreasuryExternalChainStation is
483
722
  return fisherExecutor;
484
723
  }
485
724
 
725
+ /// @notice Returns the next nonce for Fisher bridge operations for a specific user
726
+ /// @dev Used to prevent replay attacks in cross-chain bridge transactions
727
+ /// @param user Address to query the next Fisher execution nonce for
728
+ /// @return Next sequential nonce value for the user's Fisher bridge operations
486
729
  function getNextFisherExecutionNonce(
487
730
  address user
488
731
  ) external view returns (uint256) {
489
732
  return nextFisherExecutionNonce[user];
490
733
  }
491
734
 
735
+ /// @notice Returns the complete Hyperlane protocol configuration
736
+ /// @return Hyperlane configuration including domain ID, host chain address, and mailbox
492
737
  function getHyperlaneConfig()
493
738
  external
494
739
  view
@@ -497,6 +742,8 @@ contract TreasuryExternalChainStation is
497
742
  return hyperlane;
498
743
  }
499
744
 
745
+ /// @notice Returns the complete LayerZero protocol configuration
746
+ /// @return LayerZero configuration including endpoint ID, host chain address, and endpoint
500
747
  function getLayerZeroConfig()
501
748
  external
502
749
  view
@@ -505,25 +752,35 @@ contract TreasuryExternalChainStation is
505
752
  return layerZero;
506
753
  }
507
754
 
755
+ /// @notice Returns the complete Axelar protocol configuration
756
+ /// @return Axelar configuration including chain name, addresses, gas service, and gateway
508
757
  function getAxelarConfig() external view returns (AxelarConfig memory) {
509
758
  return axelar;
510
759
  }
511
760
 
761
+ /// @notice Returns the LayerZero execution options configuration
762
+ /// @return Encoded options bytes for LayerZero message execution (200k gas limit)
512
763
  function getOptions() external view returns (bytes memory) {
513
- return _options;
764
+ return options;
514
765
  }
515
766
 
516
767
  // Internal Functions //
517
768
 
769
+ /// @notice Decodes cross-chain payload and executes the token transfer
770
+ /// @dev Handles both ETH (address(0)) and ERC20 token transfers to recipients
771
+ /// @param payload Encoded transfer data containing token, recipient, and amount
518
772
  function decodeAndGive(bytes memory payload) internal {
519
- (address token, address toAddress, uint256 amount) = decodePayload(
520
- payload
521
- );
773
+ (address token, address toAddress, uint256 amount) = PayloadUtils
774
+ .decodePayload(payload);
522
775
  if (token == address(0))
523
- SafeTransferLib.safeTransferETH(msg.sender, amount);
776
+ SafeTransferLib.safeTransferETH(toAddress, amount);
524
777
  else IERC20(token).transfer(toAddress, amount);
525
778
  }
526
779
 
780
+ /// @notice Validates and deposits ERC20 tokens from the caller
781
+ /// @dev Verifies token approval and executes transferFrom to this contract
782
+ /// @param token ERC20 token contract address (cannot be address(0))
783
+ /// @param amount Amount of tokens to deposit and hold in this contract
527
784
  function verifyAndDepositERC20(address token, uint256 amount) internal {
528
785
  if (token == address(0)) revert();
529
786
  if (IERC20(token).allowance(msg.sender, address(this)) < amount)
@@ -532,20 +789,13 @@ contract TreasuryExternalChainStation is
532
789
  IERC20(token).transferFrom(msg.sender, address(this), amount);
533
790
  }
534
791
 
535
- function encodePayload(
536
- address token,
537
- address toAddress,
538
- uint256 amount
539
- ) internal pure returns (bytes memory payload) {
540
- payload = abi.encode(token, toAddress, amount);
541
- }
792
+ /// @notice Disabled ownership transfer function for security
793
+ /// @dev Ownership changes must go through the time-delayed admin proposal system
794
+ function transferOwnership(
795
+ address newOwner
796
+ ) public virtual override onlyOwner {}
542
797
 
543
- function decodePayload(
544
- bytes memory payload
545
- ) internal pure returns (address token, address toAddress, uint256 amount) {
546
- (token, toAddress, amount) = abi.decode(
547
- payload,
548
- (address, address, uint256)
549
- );
550
- }
798
+ /// @notice Disabled ownership renouncement function for security
799
+ /// @dev Prevents accidental loss of administrative control over the contract
800
+ function renounceOwnership() public virtual override onlyOwner {}
551
801
  }