Skip to content

Commit 3b23cfb

Browse files
chrismareeReinis-FRPmd0xmrice32nicholaspai
authored
feat: EVM changes required to support Solana (#672)
* feat(chain-adapters): add solana adapter (#641) * feat(chain-adapters): add solana adapter Signed-off-by: Reinis Martinsons <[email protected]> * fix: comments Signed-off-by: Reinis Martinsons <[email protected]> * test: solana adapter Signed-off-by: Reinis Martinsons <[email protected]> * Update contracts/chain-adapters/Solana_Adapter.sol Co-authored-by: Chris Maree <[email protected]> * fix: do not hash bytes32 svm address Signed-off-by: Reinis Martinsons <[email protected]> --------- Signed-off-by: Reinis Martinsons <[email protected]> Co-authored-by: Chris Maree <[email protected]> * feat: address to bytes32 contract changes (#650) * feat: add address to bytes32 contract changes Signed-off-by: Pablo Maldonado <[email protected]> * refactor: remove todos Signed-off-by: Pablo Maldonado <[email protected]> * refactor: imports Signed-off-by: Pablo Maldonado <[email protected]> * Update contracts/SpokePool.sol Co-authored-by: Reinis Martinsons <[email protected]> * feat: bytes 32 comparisons Signed-off-by: Pablo Maldonado <[email protected]> * refactor: format code Signed-off-by: Pablo Maldonado <[email protected]> * fix: tests Signed-off-by: Pablo Maldonado <[email protected]> * feat: bytes 32 check Signed-off-by: Pablo Maldonado <[email protected]> * fix: ts Signed-off-by: Pablo Maldonado <[email protected]> * feat: reuse lib in cctp adapter Signed-off-by: Pablo Maldonado <[email protected]> * feat: _preExecuteLeafHook Signed-off-by: Pablo Maldonado <[email protected]> * refactor: comments Signed-off-by: Pablo Maldonado <[email protected]> * feat: _verifyUpdateV3DepositMessage Signed-off-by: Pablo Maldonado <[email protected]> * feat: backward compatibility Signed-off-by: Pablo Maldonado <[email protected]> * feat: backwards compatibility tests Signed-off-by: Pablo Maldonado <[email protected]> * feat: change comparison casting address bytes32 Signed-off-by: Pablo Maldonado <[email protected]> * fix: test Signed-off-by: Pablo Maldonado <[email protected]> * feat: merkle tree leaf to bytes32 Signed-off-by: Pablo Maldonado <[email protected]> * test: leaf type update fixes Signed-off-by: Pablo Maldonado <[email protected]> * feat: remove helper Signed-off-by: Pablo Maldonado <[email protected]> --------- Signed-off-by: Pablo Maldonado <[email protected]> Co-authored-by: Reinis Martinsons <[email protected]> * feat: Add relayer repayment address (#653) * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> --------- Signed-off-by: chrismaree <[email protected]> * fix: clean up cast utilities (#676) * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> --------- Signed-off-by: chrismaree <[email protected]> * feat: update spokepool relayer refund to handle blocked transfers (#675) Co-authored-by: Matt Rice <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * fix(evm): merkle tree tests bytes32 Signed-off-by: Pablo Maldonado <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * feat(svm): svm-dev fixes from review (#727) * refactor(svm): reuse bytes32 to address lib in svm adapter Signed-off-by: Pablo Maldonado <[email protected]> * feat: custom errors Signed-off-by: Pablo Maldonado <[email protected]> * feat: fix test Signed-off-by: Pablo Maldonado <[email protected]> --------- Signed-off-by: Pablo Maldonado <[email protected]> * test: fix forge tests Signed-off-by: Pablo Maldonado <[email protected]> * proposal: ensure that EVM errors are always consistant on underflows (#720) * feat: revert bytes32 conversion for internal functions (#755) * Discard changes to contracts/Ovm_SpokePool.sol * fix: stack too deep (#766) * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * Revert "feat: update depositor to bytes32" (#764) This reverts commit 85f0001. * Discard changes to contracts/PolygonZkEVM_SpokePool.sol * Discard changes to contracts/Polygon_SpokePool.sol * fix: make event case consistant between evm & svm (#760) * feat(SpokePool): Remove depositExclusive (#642) This function was used to express exclusivity as a period but its no longer useful since depositV3 now allows caller to express exclusivityPeriod instead of exclusivityDeadline * feat: Introduce opt-in deterministic relay data hashes (again) (#639) * Revert "feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)" This reverts commit 9d21d1b. * Reapply "feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)" This reverts commit d363bf0. * add deposit nonces to 7683 Signed-off-by: Matt Rice <[email protected]> * fix Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> * feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583) * fix(SpokePool): Apply exclusivity consistently The new relative exclusivity check has not been propagated to fillV3RelayWithUpdatedDeposit(). Identified via test case failures in the relayer. Signed-off-by: Paul <[email protected]> * Also check on slow fill requests * Update contracts/SpokePool.sol * lint * Update * Add pure * Fix * Add tests * improve(SpokePool): _depositV3 interprets `exclusivityParameter` as 0, an offset, or a timestamp There should be a way for the deposit transaction to remove chain re-org risk affecting the block.timestamp by allowing the caller to set a fixed `exclusivityDeadline` value. This supports the existing behavior where the `exclusivityDeadline` is always emitted as its passed in. The new behavior is that if the `exclusivityParameter`, which replaces the `exclusivityDeadlineOffset` parameter, is 0 or greater than 1 year in seconds, then the `exclusivityDeadline` is equal to this parameter. Otherwise, its interpreted by `_depositV3()` as an offset. The offset would be useful in cases where the origin chain will not re-org, for example. * Update SpokePool.sol * Update SpokePool.Relay.ts * Update SpokePool.SlowRelay.ts * Update contracts/SpokePool.sol Co-authored-by: Paul <[email protected]> * Update SpokePool.sol * Update contracts/SpokePool.sol * rebase * Update SpokePool.sol * Revert "Merge branch 'npai/exclusivity-switch' into mrice32/deterministic-new" This reverts commit 2432944, reversing changes made to 6fe3534. * Revert "Merge branch 'npai/exclusivity-switch' into mrice32/deterministic-new" This reverts commit 2432944, reversing changes made to 6fe3534. * revert * Update SpokePool.sol * Fix * Update SpokePool.sol Co-authored-by: Chris Maree <[email protected]> * WIP * WIP * wip * Update SpokePool.Relay.ts * Fix * Update SpokePool.sol * Update SpokePool.sol --------- Signed-off-by: Matt Rice <[email protected]> Signed-off-by: Paul <[email protected]> Co-authored-by: nicholaspai <[email protected]> Co-authored-by: nicholaspai <[email protected]> Co-authored-by: Paul <[email protected]> Co-authored-by: Chris Maree <[email protected]> * docs: fix comment duplication (#775) Signed-off-by: Pablo Maldonado <[email protected]> * fix: emit hashed message in evm fill events (#772) * fix: emit hashed message in evm fill events Signed-off-by: Reinis Martinsons <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * fix: linting Signed-off-by: Reinis Martinsons <[email protected]> --------- Signed-off-by: Reinis Martinsons <[email protected]> Signed-off-by: chrismaree <[email protected]> Co-authored-by: chrismaree <[email protected]> * fix: linting Signed-off-by: Reinis Martinsons <[email protected]> * feat: improve _getV3RelayHash method (#779) * WIP Signed-off-by: chrismaree <[email protected]> * WIP Signed-off-by: chrismaree <[email protected]> * fix: Address Storage layout issue in CI (#836) * add new storage layout Signed-off-by: Chris Maree <[email protected]> * Discard changes to storage-layouts/PolygonZkEVM_SpokePool.json * Discard changes to storage-layouts/Redstone_SpokePool.json * Discard changes to storage-layouts/Scroll_SpokePool.json * Discard changes to storage-layouts/Zora_SpokePool.json * Discard changes to storage-layouts/WorldChain_SpokePool.json * add new storage layout Signed-off-by: Chris Maree <[email protected]> --------- Signed-off-by: Chris Maree <[email protected]> * fix(evm): C01 - Address incorrect use of relayerRefund over msg.sender in claimRelayerRefund function (#826) Signed-off-by: Chris Maree <[email protected]> * fix(evm): L01 - Update function from public to external (#827) Signed-off-by: Chris Maree <[email protected]> * fix(evm): L03 - Address incorrect Right Shift in AddressConverters Lib (#828) Signed-off-by: Chris Maree <[email protected]> * fix(evm): L04 - Remove repeated function (#829) Signed-off-by: Chris Maree <[email protected]> * fix(evm): N01 - Add missing docstring for repaymentAddress (#830) Signed-off-by: Chris Maree <[email protected]> * fix(evm): N02 - Address typographical Errors in spoke pool (#831) * WIP Signed-off-by: Chris Maree <[email protected]> * Update contracts/SpokePool.sol --------- Signed-off-by: Chris Maree <[email protected]> Co-authored-by: Matt Rice <[email protected]> * feat: update function and event naming for backwards compatibility (#805) * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * refined overfloaded function structure Signed-off-by: Chris Maree <[email protected]> * Discard changes to test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> * update event names Signed-off-by: Matt Rice <[email protected]> * fix tests Signed-off-by: Matt Rice <[email protected]> * update function Signed-off-by: Matt Rice <[email protected]> * update naming Signed-off-by: Matt Rice <[email protected]> * drop unintended svm changes Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * WIP Signed-off-by: Chris Maree <[email protected]> * feat: extend current add-legacy-fill-method-svm-dev (#864) * WIP Signed-off-by: Chris Maree <[email protected]> --------- Signed-off-by: Chris Maree <[email protected]> Signed-off-by: Chris Maree <[email protected]> Signed-off-by: Matt Rice <[email protected]> Co-authored-by: Chris Maree <[email protected]> Co-authored-by: Matt Rice <[email protected]> * fix: update legacy FilledV3Relay event to match old event signature (#873) * fix: update legacy event to match old event signature Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> * WIP Signed-off-by: Matt Rice <[email protected]> --------- Signed-off-by: Matt Rice <[email protected]> * fix: use entire message when calculating relay hash for evm chains (#867) * fix: hash entire message when calculating relay hash for evm chains Signed-off-by: bennett <[email protected]> * make getV3RelayHash public Signed-off-by: bennett <[email protected]> * update fixture with relay hash change Signed-off-by: bennett <[email protected]> --------- Signed-off-by: bennett <[email protected]> * feat(SpokePool): Permit historical fillDeadline on deposit (#870) * feat(SpokePool): Permit historical fillDeadline on deposit This removes a sharp edge for pre-fill deposits, where the deposit comes after the fill. Permitting a historical fillDeadline gives more flexibility to the relayer around when they submit the deposit on the origin chain. * fix test * restore test * Bump approvals * fix: add check to ensure depositor is a valid EVM address (#874) Signed-off-by: Matt Rice <[email protected]> * fix(evm): L02 _destinationSettler Can Return Zero Address (#834) * fix: L02 _destinationSettler Can Return Zero Address * updated implementation to be in internal function Signed-off-by: Chris Maree <[email protected]> --------- Signed-off-by: Chris Maree <[email protected]> Co-authored-by: Chris Maree <[email protected]> Co-authored-by: nicholaspai <[email protected]> * improve: Verify relay hashes are the same pre and post upgrade (#878) * fix: hash entire message when calculating relay hash for evm chains Signed-off-by: bennett <[email protected]> * make getV3RelayHash public Signed-off-by: bennett <[email protected]> * update fixture with relay hash change Signed-off-by: bennett <[email protected]> * improve: Verify relay hashes are the same pre and post upgrade Adds a simple unit test to check that the same data hash is constructed * fix * Update test/evm/hardhat/MerkleLib.Proofs.ts * Update test/evm/hardhat/SpokePool.Relay.ts * Update SpokePool.Relay.ts --------- Signed-off-by: bennett <[email protected]> Co-authored-by: bennett <[email protected]> * Fix merge conflict that removed exclusivity parameter * Fix SwapAndBridge merge conflict * reorder stack variables Signed-off-by: bennett <[email protected]> * export test functions Signed-off-by: bennett <[email protected]> * bump package Signed-off-by: bennett <[email protected]> * fix: simpler solution to stack too deep --------- Signed-off-by: Reinis Martinsons <[email protected]> Signed-off-by: Pablo Maldonado <[email protected]> Signed-off-by: chrismaree <[email protected]> Signed-off-by: Matt Rice <[email protected]> Signed-off-by: Paul <[email protected]> Signed-off-by: Chris Maree <[email protected]> Signed-off-by: Chris Maree <[email protected]> Signed-off-by: bennett <[email protected]> Co-authored-by: Reinis Martinsons <[email protected]> Co-authored-by: Pablo Maldonado <[email protected]> Co-authored-by: Matt Rice <[email protected]> Co-authored-by: nicholaspai <[email protected]> Co-authored-by: nicholaspai <[email protected]> Co-authored-by: Paul <[email protected]> Co-authored-by: Reinis Martinsons <[email protected]> Co-authored-by: Chris Maree <[email protected]> Co-authored-by: bmzig <[email protected]> Co-authored-by: bennett <[email protected]>
1 parent ceda866 commit 3b23cfb

File tree

61 files changed

+3092
-859
lines changed

Some content is hidden

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

61 files changed

+3092
-859
lines changed

contracts/SpokePool.sol

+602-222
Large diffs are not rendered by default.

contracts/SpokePoolVerifier.sol

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
33

44
import "@openzeppelin/contracts/utils/Address.sol";
55
import "./interfaces/V3SpokePoolInterface.sol";
6+
import { AddressToBytes32 } from "./libraries/AddressConverters.sol";
67

78
/**
89
* @notice SpokePoolVerifier is a contract that verifies that the SpokePool exists on this chain before sending ETH to it.
@@ -14,12 +15,13 @@ import "./interfaces/V3SpokePoolInterface.sol";
1415
*/
1516
contract SpokePoolVerifier {
1617
using Address for address;
18+
using AddressToBytes32 for address;
1719

1820
error InvalidMsgValue();
1921
error InvalidSpokePool();
2022

2123
/**
22-
* @notice Passthrough function to `depositV3()` on the SpokePool contract.
24+
* @notice Passthrough function to `deposit()` on the SpokePool contract.
2325
* @dev Protects the caller from losing their ETH (or other native token) by reverting if the SpokePool address
2426
* they intended to call does not exist on this chain. Because this contract can be deployed at the same address
2527
* everywhere callers should be protected even if the transaction is submitted to an unintended network.
@@ -42,12 +44,12 @@ contract SpokePoolVerifier {
4244
*/
4345
function deposit(
4446
V3SpokePoolInterface spokePool,
45-
address recipient,
46-
address inputToken,
47+
bytes32 recipient,
48+
bytes32 inputToken,
4749
uint256 inputAmount,
4850
uint256 outputAmount,
4951
uint256 destinationChainId,
50-
address exclusiveRelayer,
52+
bytes32 exclusiveRelayer,
5153
uint32 quoteTimestamp,
5254
uint32 fillDeadline,
5355
uint32 exclusivityDeadline,
@@ -56,13 +58,13 @@ contract SpokePoolVerifier {
5658
if (msg.value != inputAmount) revert InvalidMsgValue();
5759
if (!address(spokePool).isContract()) revert InvalidSpokePool();
5860
// Set msg.sender as the depositor so that msg.sender can speed up the deposit.
59-
spokePool.depositV3{ value: msg.value }(
60-
msg.sender,
61+
spokePool.deposit{ value: msg.value }(
62+
msg.sender.toBytes32(),
6163
recipient,
6264
inputToken,
6365
// @dev Setting outputToken to 0x0 to instruct fillers to use the equivalent token
6466
// as the originToken on the destination chain.
65-
address(0),
67+
bytes32(0),
6668
inputAmount,
6769
outputAmount,
6870
destinationChainId,

contracts/SwapAndBridge.sol

+24-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
66
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
77
import "./Lockable.sol";
88
import "@uma/core/contracts/common/implementation/MultiCaller.sol";
9+
import "./libraries/AddressConverters.sol";
910

1011
/**
1112
* @title SwapAndBridgeBase
@@ -14,6 +15,7 @@ import "@uma/core/contracts/common/implementation/MultiCaller.sol";
1415
*/
1516
abstract contract SwapAndBridgeBase is Lockable, MultiCaller {
1617
using SafeERC20 for IERC20;
18+
using AddressToBytes32 for address;
1719

1820
// This contract performs a low level call with arbirary data to an external contract. This is a large attack
1921
// surface and we should whitelist which function selectors are allowed to be called on the exchange.
@@ -159,16 +161,30 @@ abstract contract SwapAndBridgeBase is Lockable, MultiCaller {
159161
depositData.outputAmount
160162
);
161163
// Deposit the swapped tokens into Across and bridge them using remainder of input params.
162-
_acrossInputToken.safeIncreaseAllowance(address(SPOKE_POOL), returnAmount);
163-
SPOKE_POOL.depositV3(
164-
depositData.depositor,
165-
depositData.recipient,
166-
address(_acrossInputToken), // input token
167-
depositData.outputToken, // output token
168-
returnAmount, // input amount.
164+
_depositV3(_acrossInputToken, returnAmount, depositData);
165+
}
166+
167+
/**
168+
* @notice Approves the spoke pool and calls `depositV3` function with the specified input parameters.
169+
* @param _acrossInputToken Token to deposit into the spoke pool.
170+
* @param _acrossInputAmount Amount of the input token to deposit into the spoke pool.
171+
* @param depositData Specifies the Across deposit params to use.
172+
*/
173+
function _depositV3(
174+
IERC20 _acrossInputToken,
175+
uint256 _acrossInputAmount,
176+
DepositData calldata depositData
177+
) internal {
178+
_acrossInputToken.safeIncreaseAllowance(address(SPOKE_POOL), _acrossInputAmount);
179+
SPOKE_POOL.deposit(
180+
depositData.depositor.toBytes32(),
181+
depositData.recipient.toBytes32(),
182+
address(_acrossInputToken).toBytes32(), // input token
183+
depositData.outputToken.toBytes32(), // output token
184+
_acrossInputAmount, // input amount.
169185
depositData.outputAmount, // output amount
170186
depositData.destinationChainid,
171-
depositData.exclusiveRelayer,
187+
depositData.exclusiveRelayer.toBytes32(),
172188
depositData.quoteTimestamp,
173189
depositData.fillDeadline,
174190
depositData.exclusivityDeadline,
+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { IMessageTransmitter, ITokenMessenger } from "../external/interfaces/CCTPInterfaces.sol";
5+
import { SpokePoolInterface } from "../interfaces/SpokePoolInterface.sol";
6+
import { AdapterInterface } from "./interfaces/AdapterInterface.sol";
7+
import { CircleCCTPAdapter, CircleDomainIds } from "../libraries/CircleCCTPAdapter.sol";
8+
import { Bytes32ToAddress } from "../libraries/AddressConverters.sol";
9+
10+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
11+
12+
/**
13+
* @notice Contract containing logic to send messages from L1 to Solana via CCTP.
14+
* @dev Public functions calling external contracts do not guard against reentrancy because they are expected to be
15+
* called via delegatecall, which will execute this contract's logic within the context of the originating contract.
16+
* For example, the HubPool will delegatecall these functions, therefore it's only necessary that the HubPool's methods
17+
* that call this contract's logic guard against reentrancy.
18+
* @custom:security-contact [email protected]
19+
*/
20+
21+
// solhint-disable-next-line contract-name-camelcase
22+
contract Solana_Adapter is AdapterInterface, CircleCCTPAdapter {
23+
/**
24+
* @notice We use Bytes32ToAddress library to map a Solana address to an Ethereum address representation.
25+
* @dev The Ethereum address is derived from the Solana address by truncating it to its lowest 20 bytes. This same
26+
* conversion must be done by the HubPool owner when adding Solana spoke pool and setting the corresponding pool
27+
* rebalance and deposit routes.
28+
*/
29+
using Bytes32ToAddress for bytes32;
30+
31+
/**
32+
* @notice The official Circle CCTP MessageTransmitter contract endpoint.
33+
* @dev Posted officially here: https://developers.circle.com/stablecoins/docs/evm-smart-contracts
34+
*/
35+
// solhint-disable-next-line immutable-vars-naming
36+
IMessageTransmitter public immutable cctpMessageTransmitter;
37+
38+
// Solana spoke pool address, decoded from Base58 to bytes32.
39+
bytes32 public immutable SOLANA_SPOKE_POOL_BYTES32;
40+
41+
// Solana spoke pool address, mapped to its EVM address representation.
42+
address public immutable SOLANA_SPOKE_POOL_ADDRESS;
43+
44+
// USDC mint address on Solana, decoded from Base58 to bytes32.
45+
bytes32 public immutable SOLANA_USDC_BYTES32;
46+
47+
// USDC mint address on Solana, mapped to its EVM address representation.
48+
address public immutable SOLANA_USDC_ADDRESS;
49+
50+
// USDC token address on Solana for the spoke pool (vault ATA), decoded from Base58 to bytes32.
51+
bytes32 public immutable SOLANA_SPOKE_POOL_USDC_VAULT;
52+
53+
// Custom errors for constructor argument validation.
54+
error InvalidCctpTokenMessenger(address tokenMessenger);
55+
error InvalidCctpMessageTransmitter(address messageTransmitter);
56+
57+
// Custom errors for relayMessage validation.
58+
error InvalidRelayMessageTarget(address target);
59+
error InvalidOriginToken(address originToken);
60+
error InvalidDestinationChainId(uint256 destinationChainId);
61+
62+
// Custom errors for relayTokens validation.
63+
error InvalidL1Token(address l1Token);
64+
error InvalidL2Token(address l2Token);
65+
error InvalidAmount(uint256 amount);
66+
error InvalidTokenRecipient(address to);
67+
68+
/**
69+
* @notice Constructs new Adapter.
70+
* @param _l1Usdc USDC address on L1.
71+
* @param _cctpTokenMessenger TokenMessenger contract to bridge tokens via CCTP.
72+
* @param _cctpMessageTransmitter MessageTransmitter contract to bridge messages via CCTP.
73+
* @param solanaSpokePool Solana spoke pool address, decoded from Base58 to bytes32.
74+
* @param solanaUsdc USDC mint address on Solana, decoded from Base58 to bytes32.
75+
* @param solanaSpokePoolUsdcVault USDC token address on Solana for the spoke pool, decoded from Base58 to bytes32.
76+
*/
77+
constructor(
78+
IERC20 _l1Usdc,
79+
ITokenMessenger _cctpTokenMessenger,
80+
IMessageTransmitter _cctpMessageTransmitter,
81+
bytes32 solanaSpokePool,
82+
bytes32 solanaUsdc,
83+
bytes32 solanaSpokePoolUsdcVault
84+
) CircleCCTPAdapter(_l1Usdc, _cctpTokenMessenger, CircleDomainIds.Solana) {
85+
// Solana adapter requires CCTP TokenMessenger and MessageTransmitter contracts to be set.
86+
if (address(_cctpTokenMessenger) == address(0)) {
87+
revert InvalidCctpTokenMessenger(address(_cctpTokenMessenger));
88+
}
89+
if (address(_cctpMessageTransmitter) == address(0)) {
90+
revert InvalidCctpMessageTransmitter(address(_cctpMessageTransmitter));
91+
}
92+
93+
cctpMessageTransmitter = _cctpMessageTransmitter;
94+
95+
SOLANA_SPOKE_POOL_BYTES32 = solanaSpokePool;
96+
SOLANA_SPOKE_POOL_ADDRESS = solanaSpokePool.toAddressUnchecked();
97+
98+
SOLANA_USDC_BYTES32 = solanaUsdc;
99+
SOLANA_USDC_ADDRESS = solanaUsdc.toAddressUnchecked();
100+
101+
SOLANA_SPOKE_POOL_USDC_VAULT = solanaSpokePoolUsdcVault;
102+
}
103+
104+
/**
105+
* @notice Send cross-chain message to target on Solana.
106+
* @dev Only allows sending messages to the Solana spoke pool.
107+
* @param target Program on Solana (translated as EVM address) that will receive message.
108+
* @param message Data to send to target.
109+
*/
110+
function relayMessage(address target, bytes calldata message) external payable override {
111+
if (target != SOLANA_SPOKE_POOL_ADDRESS) {
112+
revert InvalidRelayMessageTarget(target);
113+
}
114+
115+
bytes4 selector = bytes4(message[:4]);
116+
if (selector == SpokePoolInterface.setEnableRoute.selector) {
117+
cctpMessageTransmitter.sendMessage(
118+
CircleDomainIds.Solana,
119+
SOLANA_SPOKE_POOL_BYTES32,
120+
_translateSetEnableRoute(message)
121+
);
122+
} else {
123+
cctpMessageTransmitter.sendMessage(CircleDomainIds.Solana, SOLANA_SPOKE_POOL_BYTES32, message);
124+
}
125+
126+
// TODO: consider if we need also to emit the translated message.
127+
emit MessageRelayed(target, message);
128+
}
129+
130+
/**
131+
* @notice Bridge tokens to Solana.
132+
* @dev Only allows bridging USDC to Solana spoke pool.
133+
* @param l1Token L1 token to deposit.
134+
* @param l2Token L2 token to receive.
135+
* @param amount Amount of L1 tokens to deposit and L2 tokens to receive.
136+
* @param to Bridge recipient.
137+
*/
138+
function relayTokens(
139+
address l1Token,
140+
address l2Token,
141+
uint256 amount,
142+
address to
143+
) external payable override {
144+
if (l1Token != address(usdcToken)) {
145+
revert InvalidL1Token(l1Token);
146+
}
147+
if (l2Token != SOLANA_USDC_ADDRESS) {
148+
revert InvalidL2Token(l2Token);
149+
}
150+
if (amount > type(uint64).max) {
151+
revert InvalidAmount(amount);
152+
}
153+
if (to != SOLANA_SPOKE_POOL_ADDRESS) {
154+
revert InvalidTokenRecipient(to);
155+
}
156+
157+
_transferUsdc(SOLANA_SPOKE_POOL_USDC_VAULT, amount);
158+
159+
// TODO: consider if we need also to emit the translated addresses.
160+
emit TokensRelayed(l1Token, l2Token, amount, to);
161+
}
162+
163+
/**
164+
* @notice Translates a message to enable/disable a route on Solana spoke pool.
165+
* @param message Message to translate, expecting setEnableRoute(address,uint256,bool).
166+
* @return Translated message, using setEnableRoute(bytes32,uint64,bool).
167+
*/
168+
function _translateSetEnableRoute(bytes calldata message) internal view returns (bytes memory) {
169+
(address originToken, uint256 destinationChainId, bool enable) = abi.decode(
170+
message[4:],
171+
(address, uint256, bool)
172+
);
173+
174+
if (originToken != SOLANA_USDC_ADDRESS) {
175+
revert InvalidOriginToken(originToken);
176+
}
177+
178+
if (destinationChainId > type(uint64).max) {
179+
revert InvalidDestinationChainId(destinationChainId);
180+
}
181+
182+
return
183+
abi.encodeWithSignature(
184+
"setEnableRoute(bytes32,uint64,bool)",
185+
SOLANA_USDC_BYTES32,
186+
uint64(destinationChainId),
187+
enable
188+
);
189+
}
190+
}

0 commit comments

Comments
 (0)