Skip to content

Commit 62c4d96

Browse files
authored
feat: add xERC20 standard support via Hyperlane (#914)
* first draft of OFTTransportAdapter Signed-off-by: Ihor Farion <ihor@umaproject.org> * update yarn.lock file Signed-off-by: Ihor Farion <ihor@umaproject.org> * Revert "update yarn.lock file" This reverts commit 4c216ea. Signed-off-by: Ihor Farion <ihor@umaproject.org> * add yarn.lock compatible with master Signed-off-by: Ihor Farion <ihor@umaproject.org> * polish OFTTransportAdapter, add OFT support to Arbitrum_Adapter on L1, and Arbitrum_SpokePool on L2 Signed-off-by: Ihor Farion <ihor@umaproject.org> * polish + fix missing approval Signed-off-by: Ihor Farion <ihor@umaproject.org> * add context for dstEid Signed-off-by: Ihor Farion <ihor@umaproject.org> * address most of the PR comments about contracts Signed-off-by: Ihor Farion <ihor@umaproject.org> * update deploy scripts, add tests for OFT messaging, polish contracts Signed-off-by: Ihor Farion <ihor@umaproject.org> * cleanup comments and extraneous log file Signed-off-by: Ihor Farion <ihor@umaproject.org> * revert package.json prepublish change Signed-off-by: Ihor Farion <ihor@umaproject.org> * generalize oft adapter to support multiple tokens. Introduce OFTAddressBook to support that. Update deploy / test scripts to reflect new functionality Signed-off-by: Ihor Farion <ihor@umaproject.org> * add __gap to ArbitrumSpokePool, update stale comments on OFTTransportAdapter, update layouts of ArbitrumSpokePool and AlephZeroSpokePool Signed-off-by: Ihor Farion <ihor@umaproject.org> * update some comments; adjust fee cap naming Signed-off-by: Ihor Farion <ihor@umaproject.org> * address PR comments Signed-off-by: Ihor Farion <ihor@umaproject.org> * address PR comments Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix deploy script, remove incorrect values from consts Signed-off-by: Ihor Farion <ihor@umaproject.org> * improve comment Signed-off-by: Ihor Farion <ihor@umaproject.org> * init commit that adds xerc20 hyperlane adapter and embeds it into arbitrum adapter and spokepool Signed-off-by: Ihor Farion <ihor@umaproject.org> * complete adapter / spoke modifications to support xerc20 trasnfers through hyperlane Signed-off-by: Ihor Farion <ihor@umaproject.org> * updated chain adapters with XERC20 support: mode, base, unichain, blast, linea, optimism; updated spokepools: same chains; added spoke + adapter tests: arbitrum, optimism, linea Signed-off-by: Ihor Farion <ihor@umaproject.org> * update arbitrum spokepool gap Signed-off-by: Ihor Farion <ihor@umaproject.org> * polish code to fit blast spokepool into a bytecode size limit Signed-off-by: Ihor Farion <ihor@umaproject.org> * added testing xerc20 functionality for chain adapters: base, blast, mode, unichain Signed-off-by: Ihor Farion <ihor@umaproject.org> * add spoke pool tests for xerc20 transfers for: base, mode, unichain Signed-off-by: Ihor Farion <ihor@umaproject.org> * add blast spoke pool tests in a separate commit for easy revert Signed-off-by: Ihor Farion <ihor@umaproject.org> * add oftFeeCap as a param to arbitrum adapter construction Signed-off-by: Ihor Farion <ihor@umaproject.org> * move OFT functionality to SpokePool for easy further integration and removing boilerplate code Signed-off-by: Ihor Farion <ihor@umaproject.org> * remove layerzero from foundry remappings Signed-off-by: Ihor Farion <ihor@umaproject.org> * remove MockBlast_SpokePool and related test, as they're of limited use Signed-off-by: Ihor Farion <ihor@umaproject.org> * address licensing comment; add permalink to LZ OFT code on github Signed-off-by: Ihor Farion <ihor@umaproject.org> * make AddressBook consistent between OFT and XERC20 Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix a couple of comment typos Signed-off-by: Ihor Farion <ihor@umaproject.org> * checkpoint: deploy scripts and test modifications for adapter / spokepool constructor changes are still needed Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix a couple of comment typos Signed-off-by: Ihor Farion <ihor@umaproject.org> * adjust xerc20 code to fit the new adapter store pattern Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix adapter tests with new pattern Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix all spokepool tests Signed-off-by: Ihor Farion <ihor@umaproject.org> * update storage layouts Signed-off-by: Ihor Farion <ihor@umaproject.org> * update adapter deployment scripts Signed-off-by: Ihor Farion <ihor@umaproject.org> * update all deploy scripts; update compiler params so that Blast_Spoke fits in the bytecode limit Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix foundry tests Signed-off-by: Ihor Farion <ihor@umaproject.org> * add QOL improvements to package.json , fix CI Signed-off-by: Ihor Farion <ihor@umaproject.org> * small pre-review polish Signed-off-by: Ihor Farion <ihor@umaproject.org> * deploy AdapterStore on sepolia, final polish Signed-off-by: Ihor Farion <ihor@umaproject.org> * polish comments Signed-off-by: Ihor Farion <ihor@umaproject.org> * make AdapterStore upgradeable; move AdapterStore.sol to a different folder; deploy upgradeable version Signed-off-by: Ihor Farion <ihor@umaproject.org> * Revert "make AdapterStore upgradeable; move AdapterStore.sol to a different folder; deploy upgradeable version" This reverts commit 7ef6170. Signed-off-by: Ihor Farion <ihor@umaproject.org> * make AdapterStore future-proof to additional storage requirements Signed-off-by: Ihor Farion <ihor@umaproject.org> * remove unused imports Signed-off-by: Ihor Farion <ihor@umaproject.org> * add new AdapterStore deployments, add verification to AdapterStore deploy script Signed-off-by: Ihor Farion <ihor@umaproject.org> * polish: remove oft / xerc20 chain id libs, change to constants. Rename _setHypXERC20Router and add human error protection Signed-off-by: Ihor Farion <ihor@umaproject.org> * OFT-specific polish: renaming Signed-off-by: Ihor Farion <ihor@umaproject.org> * remove named fee cap constants from chain-specific spokepools due to name collision; substitute for a const literal with comment. Fix tests Signed-off-by: Ihor Farion <ihor@umaproject.org> * add oftDstEid and hypXERC20DstDomain as constructor params wherever possible. Refactor AdapterStore functionality for chain Adapters Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix all tests Signed-off-by: Ihor Farion <ihor@umaproject.org> * fix all deployment scripts Signed-off-by: Ihor Farion <ihor@umaproject.org> * modify evm-contract-sizes.sh to use --optimizer-runs flag insted of modifying foundry.toml on the fly Signed-off-by: Ihor Farion <ihor@umaproject.org> * adjust var naming and comments in AdapterStore Signed-off-by: Ihor Farion <ihor@umaproject.org> * clean up deploy scripts; bump constants repo dep Signed-off-by: Ihor Farion <ihor@umaproject.org> * remove deploy from include Signed-off-by: Ihor Farion <ihor@umaproject.org> * cleanup hardcoded vars from tests Signed-off-by: Ihor Farion <ihor@umaproject.org> * address new PR comments Signed-off-by: Ihor Farion <ihor@umaproject.org> --------- Signed-off-by: Ihor Farion <ihor@umaproject.org>
1 parent 5bedb25 commit 62c4d96

File tree

108 files changed

+4410
-331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+4410
-331
lines changed

.github/workflows/pr.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ jobs:
8080
run: yarn build
8181
- name: Test evm-hardhat
8282
shell: bash
83-
run: yarn test-evm
83+
run: yarn test-evm-hardhat
8484
- name: Test svm-anchor
8585
shell: bash
8686
run: yarn test-svm
@@ -114,4 +114,4 @@ jobs:
114114
- name: Inspect storage layouts
115115
run: ./scripts/checkStorageLayout.sh
116116
- name: Test evm-foundry
117-
run: forge test --match-path test/evm/foundry/local/**/*.t.sol
117+
run: yarn test-evm-foundry

contracts/AdapterStore.sol

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
5+
6+
library MessengerTypes {
7+
bytes32 public constant OFT_MESSENGER = bytes32("OFT_MESSENGER");
8+
bytes32 public constant HYP_XERC20_ROUTER = bytes32("HYP_XERC20_ROUTER");
9+
}
10+
11+
/**
12+
* @dev A helper contract for chain adapters on the hub chain that support OFT or xERC20(via Hyperlane) messaging.
13+
* @dev Handles token => messenger/router mapping storage. Adapters can't store this themselves as they're called
14+
* @dev via `delegateCall` and their storage is not part of available context.
15+
*/
16+
contract AdapterStore is Ownable {
17+
// (messengerType, dstDomainId, srcChainToken) => messenger address
18+
mapping(bytes32 => mapping(uint256 => mapping(address => address))) public crossChainMessengers;
19+
20+
event MessengerSet(
21+
bytes32 indexed messengerType,
22+
uint256 indexed dstDomainId,
23+
address indexed srcChainToken,
24+
address srcChainMessenger
25+
);
26+
27+
error ArrayLengthMismatch();
28+
29+
function setMessenger(
30+
bytes32 messengerType,
31+
uint256 dstDomainId,
32+
address srcChainToken,
33+
address srcChainMessenger
34+
) external onlyOwner {
35+
_setMessenger(messengerType, dstDomainId, srcChainToken, srcChainMessenger);
36+
}
37+
38+
function batchSetMessengers(
39+
bytes32[] calldata messengerTypes,
40+
uint256[] calldata dstDomainIds,
41+
address[] calldata srcChainTokens,
42+
address[] calldata srcChainMessengers
43+
) external onlyOwner {
44+
if (
45+
messengerTypes.length != dstDomainIds.length ||
46+
messengerTypes.length != srcChainTokens.length ||
47+
messengerTypes.length != srcChainMessengers.length
48+
) {
49+
revert ArrayLengthMismatch();
50+
}
51+
52+
for (uint256 i = 0; i < dstDomainIds.length; i++) {
53+
_setMessenger(messengerTypes[i], dstDomainIds[i], srcChainTokens[i], srcChainMessengers[i]);
54+
}
55+
}
56+
57+
function _setMessenger(
58+
bytes32 _messengerType,
59+
uint256 _dstDomainId,
60+
address _srcChainToken,
61+
address _srcChainMessenger
62+
) internal {
63+
crossChainMessengers[_messengerType][_dstDomainId][_srcChainToken] = _srcChainMessenger;
64+
emit MessengerSet(_messengerType, _dstDomainId, _srcChainToken, _srcChainMessenger);
65+
}
66+
}

contracts/AlephZero_SpokePool.sol

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,21 @@ contract AlephZero_SpokePool is Arbitrum_SpokePool {
1818
uint32 _fillDeadlineBuffer,
1919
IERC20 _l2Usdc,
2020
ITokenMessenger _cctpTokenMessenger,
21-
// _oftFeeCap can be set to 0 for AlephZero_SpokePool as AlephZero does not support OFT transfers.
22-
uint256 _oftFeeCap
21+
uint32 _oftDstEid,
22+
uint256 _oftFeeCap,
23+
uint32 _hypXERC20DstDomain,
24+
uint256 _hypXERC20FeeCap
2325
)
2426
Arbitrum_SpokePool(
2527
_wrappedNativeTokenAddress,
2628
_depositQuoteTimeBuffer,
2729
_fillDeadlineBuffer,
2830
_l2Usdc,
2931
_cctpTokenMessenger,
30-
_oftFeeCap
32+
_oftDstEid,
33+
_oftFeeCap,
34+
_hypXERC20DstDomain,
35+
_hypXERC20FeeCap
3136
)
3237
{} // solhint-disable-line no-empty-blocks
3338
}

contracts/Arbitrum_SpokePool.sol

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
33

44
import "./SpokePool.sol";
55
import "./libraries/CircleCCTPAdapter.sol";
6+
import "./libraries/HypXERC20Adapter.sol";
67
import { CrossDomainAddressUtils } from "./libraries/CrossDomainAddressUtils.sol";
78
import { ArbitrumL2ERC20GatewayLike } from "./interfaces/ArbitrumBridge.sol";
89

@@ -28,10 +29,20 @@ contract Arbitrum_SpokePool is SpokePool, CircleCCTPAdapter {
2829
uint32 _fillDeadlineBuffer,
2930
IERC20 _l2Usdc,
3031
ITokenMessenger _cctpTokenMessenger,
31-
// _oftFeeCap can be set to 1 ether for Arbitrum, but has to be custom-set for other chains that might inherit this adapter, like AlephZero
32-
uint256 _oftFeeCap
32+
uint32 _oftDstEid,
33+
uint256 _oftFeeCap,
34+
uint32 _hypXERC20DstDomain,
35+
uint256 _hypXERC20FeeCap
3336
)
34-
SpokePool(_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer, _oftFeeCap)
37+
SpokePool(
38+
_wrappedNativeTokenAddress,
39+
_depositQuoteTimeBuffer,
40+
_fillDeadlineBuffer,
41+
_oftDstEid,
42+
_oftFeeCap,
43+
_hypXERC20DstDomain,
44+
_hypXERC20FeeCap
45+
)
3546
CircleCCTPAdapter(_l2Usdc, _cctpTokenMessenger, CircleDomainIds.Ethereum)
3647
{} // solhint-disable-line no-empty-blocks
3748

@@ -86,12 +97,20 @@ contract Arbitrum_SpokePool is SpokePool, CircleCCTPAdapter {
8697

8798
function _bridgeTokensToHubPool(uint256 amountToReturn, address l2TokenAddress) internal override {
8899
address oftMessenger = _getOftMessenger(l2TokenAddress);
100+
address hypRouter = _getXERC20HypRouter(l2TokenAddress);
89101

90102
// If the l2TokenAddress is UDSC, we need to use the CCTP bridge.
91103
if (_isCCTPEnabled() && l2TokenAddress == address(usdcToken)) {
92104
_transferUsdc(withdrawalRecipient, amountToReturn);
93105
} else if (oftMessenger != address(0)) {
94106
_transferViaOFT(IERC20(l2TokenAddress), IOFT(oftMessenger), withdrawalRecipient, amountToReturn);
107+
} else if (hypRouter != address(0)) {
108+
_transferXERC20ViaHyperlane(
109+
IERC20(l2TokenAddress),
110+
IHypXERC20Router(hypRouter),
111+
withdrawalRecipient,
112+
amountToReturn
113+
);
95114
} else {
96115
// Check that the Ethereum counterpart of the L2 token is stored on this contract.
97116
address ethereumTokenToBridge = whitelistedTokens[l2TokenAddress];

contracts/Base_SpokePool.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ contract Base_SpokePool is Ovm_SpokePool {
1616
uint32 _depositQuoteTimeBuffer,
1717
uint32 _fillDeadlineBuffer,
1818
IERC20 _l2Usdc,
19-
ITokenMessenger _cctpTokenMessenger
19+
ITokenMessenger _cctpTokenMessenger,
20+
uint32 _hypXERC20DstDomain,
21+
uint256 _hypXERC20FeeCap
2022
)
2123
Ovm_SpokePool(
2224
_wrappedNativeTokenAddress,
2325
_depositQuoteTimeBuffer,
2426
_fillDeadlineBuffer,
2527
_l2Usdc,
26-
_cctpTokenMessenger
28+
_cctpTokenMessenger,
29+
_hypXERC20DstDomain,
30+
_hypXERC20FeeCap
2731
)
2832
{} // solhint-disable-line no-empty-blocks
2933

contracts/Blast_SpokePool.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,18 @@ contract Blast_SpokePool is Ovm_SpokePool {
7575
address usdb,
7676
address l1Usdb,
7777
address yieldRecipient,
78-
address blastRetriever
78+
address blastRetriever,
79+
uint32 _hypXERC20DstDomain,
80+
uint256 _hypXERC20FeeCap
7981
)
8082
Ovm_SpokePool(
8183
_wrappedNativeTokenAddress,
8284
_depositQuoteTimeBuffer,
8385
_fillDeadlineBuffer,
8486
_l2Usdc,
85-
_cctpTokenMessenger
87+
_cctpTokenMessenger,
88+
_hypXERC20DstDomain,
89+
_hypXERC20FeeCap
8690
)
8791
{
8892
USDB = usdb;

contracts/Boba_SpokePool.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ contract Boba_SpokePool is Ovm_SpokePool {
1111
constructor(
1212
address _wrappedNativeTokenAddress,
1313
uint32 _depositQuoteTimeBuffer,
14-
uint32 _fillDeadlineBuffer
14+
uint32 _fillDeadlineBuffer,
15+
uint32 _hypXERC20DstDomain,
16+
uint256 _hypXERC20FeeCap
1517
)
1618
Ovm_SpokePool(
1719
_wrappedNativeTokenAddress,
1820
_depositQuoteTimeBuffer,
1921
_fillDeadlineBuffer,
2022
IERC20(address(0)),
21-
ITokenMessenger(address(0))
23+
ITokenMessenger(address(0)),
24+
_hypXERC20DstDomain,
25+
_hypXERC20FeeCap
2226
)
2327
{} // solhint-disable-line no-empty-blocks
2428

contracts/Cher_SpokePool.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ contract Cher_SpokePool is Ovm_SpokePool {
2222
uint32 _depositQuoteTimeBuffer,
2323
uint32 _fillDeadlineBuffer,
2424
IERC20 _l2Usdc,
25-
ITokenMessenger _cctpTokenMessenger
25+
ITokenMessenger _cctpTokenMessenger,
26+
uint32 _hypXERC20DstDomain,
27+
uint256 _hypXERC20FeeCap
2628
)
2729
Ovm_SpokePool(
2830
_wrappedNativeTokenAddress,
2931
_depositQuoteTimeBuffer,
3032
_fillDeadlineBuffer,
3133
_l2Usdc,
32-
_cctpTokenMessenger
34+
_cctpTokenMessenger,
35+
_hypXERC20DstDomain,
36+
_hypXERC20FeeCap
3337
)
3438
{} // solhint-disable-line no-empty-blocks
3539

contracts/DoctorWho_SpokePool.sol

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ contract DoctorWho_SpokePool is Ovm_SpokePool {
1616
uint32 _depositQuoteTimeBuffer,
1717
uint32 _fillDeadlineBuffer,
1818
IERC20 _l2Usdc,
19-
ITokenMessenger _cctpTokenMessenger
19+
ITokenMessenger _cctpTokenMessenger,
20+
uint32 _hypXERC20DstDomain,
21+
uint256 _hypXERC20FeeCap
2022
)
2123
Ovm_SpokePool(
2224
_wrappedNativeTokenAddress,
2325
_depositQuoteTimeBuffer,
2426
_fillDeadlineBuffer,
2527
_l2Usdc,
26-
_cctpTokenMessenger
28+
_cctpTokenMessenger,
29+
_hypXERC20DstDomain,
30+
_hypXERC20FeeCap
2731
)
2832
{} // solhint-disable-line no-empty-blocks
2933

contracts/Ethereum_SpokePool.sol

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,24 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
1111
contract Ethereum_SpokePool is SpokePool, OwnableUpgradeable {
1212
using SafeERC20Upgradeable for IERC20Upgradeable;
1313

14-
// Ethereum_SpokePool does not use OFT messaging, setting the cap to 0
15-
uint256 private constant OFT_FEE_CAP = 0;
16-
1714
/// @custom:oz-upgrades-unsafe-allow constructor
1815
constructor(
1916
address _wrappedNativeTokenAddress,
2017
uint32 _depositQuoteTimeBuffer,
2118
uint32 _fillDeadlineBuffer
22-
) SpokePool(_wrappedNativeTokenAddress, _depositQuoteTimeBuffer, _fillDeadlineBuffer, OFT_FEE_CAP) {} // solhint-disable-line no-empty-blocks
19+
)
20+
SpokePool(
21+
_wrappedNativeTokenAddress,
22+
_depositQuoteTimeBuffer,
23+
_fillDeadlineBuffer,
24+
// Ethereum_SpokePool does not use OFT messaging; setting destination eid and fee cap to 0
25+
0,
26+
0,
27+
// Ethereum_SpokePool does not use Hyperlane xERC20 messaging; setting destination eid and fee cap to 0
28+
0,
29+
0
30+
)
31+
{} // solhint-disable-line no-empty-blocks
2332

2433
/**
2534
* @notice Construct the Ethereum SpokePool.

0 commit comments

Comments
 (0)