Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cd311fd
feat: Hypercorelib (#1137)
tbwebb22 Oct 15, 2025
54607f3
feat: Hypercorelib - clean up decimal conversion functions (#1139)
tbwebb22 Oct 17, 2025
b4450f6
feat: OP Adapter update (#1132)
fusmanii Oct 17, 2025
7ae992f
feat: eraVM Spoke Pool 7702 Handling (#1122)
fusmanii Oct 17, 2025
21f22b6
feat: add safe bridge check (#1140)
tbwebb22 Oct 17, 2025
4e954db
feat: Sponsored Bridging - CCTP (#1135)
fusmanii Oct 20, 2025
4b2f04e
feat: sponsored bridging -- OFT track (#1134)
grasphoper Oct 20, 2025
cb310e2
fix: add fixes for issues that codex found (#1142)
mrice32 Oct 20, 2025
a608d74
fix account activation flow
grasphoper Oct 20, 2025
1361b2d
use CREATE2 for deterministic SwapHandler addresses. Check for HyperC…
grasphoper Oct 20, 2025
1a063e5
improve events
grasphoper Oct 20, 2025
fc51389
misc
grasphoper Oct 20, 2025
cb1e75a
feat: add arbitrary actions execution to sponsored bridging (#1143)
mrice32 Oct 20, 2025
9aa5bbc
Update contracts/periphery/mintburn/HyperCoreFlowExecutor.sol
mrice32 Oct 20, 2025
7ff0982
Update contracts/periphery/mintburn/HyperCoreFlowExecutor.sol
mrice32 Oct 20, 2025
335b7bd
fix typo
mrice32 Oct 20, 2025
5de7a4b
moved nonce setting before external calls
fusmanii Oct 21, 2025
356c852
Fix: Remvoed token info setting on deploy
fusmanii Oct 21, 2025
08503a1
Undo commented out require
fusmanii Oct 21, 2025
c402e1f
Remove unused constructor args
fusmanii Oct 22, 2025
7b05bb1
move LayerZero libs to `external/` (#1151)
grasphoper Oct 22, 2025
4e8e1a2
feat: add swapsProcessed return values to finalizePendingsSwaps (#1145)
tbwebb22 Oct 22, 2025
e5da749
fix: arbitrary actions flow (#1149)
grasphoper Oct 22, 2025
00eda37
chore: Added NatSpec to CCTP contracts (#1144)
fusmanii Oct 22, 2025
da90282
add reentrancy guards and follow the CEI pattern where possible (#1148)
grasphoper Oct 22, 2025
bd3c77a
improve: Move BytesLib to external folder (#1153)
fusmanii Oct 23, 2025
081eb36
fix: HyperCoreFlowExecutor stack too deep (#1154)
tbwebb22 Oct 23, 2025
6b7a8a4
feat: remove swap calcs form contracts (#1158)
grasphoper Oct 23, 2025
ec9bd79
add Bytes from OZ (#1160)
grasphoper Oct 23, 2025
52d0162
fix: Set version to cancun (#1162)
fusmanii Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ artifacts-zk
# Foundry files
out
zkout
cache-foundry

# Upgradeability files
.openzeppelin
Expand Down
2 changes: 1 addition & 1 deletion contracts/SpokePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1637,7 +1637,7 @@ abstract contract SpokePool is
* @param account The address to check.
* @return True if the address is a 7702 delegated wallet, false otherwise.
*/
function _is7702DelegatedWallet(address account) internal view returns (bool) {
function _is7702DelegatedWallet(address account) internal view virtual returns (bool) {
return bytes3(account.code) == EIP7702_PREFIX;
}

Expand Down
14 changes: 9 additions & 5 deletions contracts/ZkSync_SpokePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import "./SpokePool.sol";

// https://github.com/matter-labs/era-contracts/blob/6391c0d7bf6184d7f6718060e3991ba6f0efe4a7/zksync/contracts/bridge/L2ERC20Bridge.sol#L104
interface ZkBridgeLike {
function withdraw(
address _l1Receiver,
address _l2Token,
uint256 _amount
) external;
function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external;
}

interface IL2ETH {
Expand Down Expand Up @@ -131,6 +127,14 @@ contract ZkSync_SpokePool is SpokePool, CircleCCTPAdapter {
* INTERNAL FUNCTIONS *
**************************************/

/**
* @notice Checks if an address is a 7702 delegated wallet (EOA with delegated code).
* @return False Since eraVM does not support 7702 delegated wallets, this function always returns false.
*/
function _is7702DelegatedWallet(address) internal pure override returns (bool) {
return false;
}

/**
* @notice Wraps any ETH into WETH before executing base function. This is necessary because SpokePool receives
* ETH over the canonical token bridge instead of WETH.
Expand Down
35 changes: 21 additions & 14 deletions contracts/chain-adapters/OP_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,43 @@ contract OP_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter {
IL1StandardBridge public immutable L1_STANDARD_BRIDGE;
IOpUSDCBridgeAdapter public immutable L1_OP_USDC_BRIDGE;

error InvalidBridgeConfig();

/**
* @notice Constructs new Adapter.
* @param _l1Weth WETH address on L1.
* @param _l1Usdc USDC address on L1.
* @param _crossDomainMessenger XDomainMessenger Destination chain system contract.
* @param _l1StandardBridge Standard bridge contract.
* @param _l1Usdc USDC address on L1.
* @param _l1USDCBridge OP USDC bridge contract.
* @param _cctpTokenMessenger CCTP token messenger contract.
* @param _recipientCircleDomainId Circle domain ID of the destination chain.
*/
constructor(
WETH9Interface _l1Weth,
IERC20 _l1Usdc,
address _crossDomainMessenger,
IL1StandardBridge _l1StandardBridge,
IOpUSDCBridgeAdapter _l1USDCBridge
IOpUSDCBridgeAdapter _l1USDCBridge,
ITokenMessenger _cctpTokenMessenger,
uint32 _recipientCircleDomainId
)
CrossDomainEnabled(_crossDomainMessenger)
CircleCCTPAdapter(
_l1Usdc,
// Hardcode cctp messenger to 0x0 to disable CCTP bridging.
ITokenMessenger(address(0)),
CircleDomainIds.UNINITIALIZED
)
CircleCCTPAdapter(_l1Usdc, _cctpTokenMessenger, _recipientCircleDomainId)
{
L1_WETH = _l1Weth;
L1_STANDARD_BRIDGE = _l1StandardBridge;
L1_OP_USDC_BRIDGE = _l1USDCBridge;

address zero = address(0);
if (address(_l1Usdc) != zero) {
bool opUSDCBridgeDisabled = address(_l1USDCBridge) == zero;
bool cctpUSDCBridgeDisabled = address(_cctpTokenMessenger) == zero;
// Bridged and Native USDC are mutually exclusive.
if (opUSDCBridgeDisabled == cctpUSDCBridgeDisabled) {
revert InvalidBridgeConfig();
}
}
}

/**
Expand All @@ -79,12 +91,7 @@ contract OP_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter {
* @param amount Amount of L1 tokens to deposit and L2 tokens to receive.
* @param to Bridge recipient.
*/
function relayTokens(
address l1Token,
address l2Token,
uint256 amount,
address to
) external payable override {
function relayTokens(address l1Token, address l2Token, uint256 amount, address to) external payable override {
// If the l1Token is weth then unwrap it to ETH then send the ETH to the standard bridge.
if (l1Token == address(L1_WETH)) {
L1_WETH.withdraw(amount);
Expand Down
70 changes: 69 additions & 1 deletion contracts/external/interfaces/CCTPInterfaces.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ interface ITokenMessenger {
function localMinter() external view returns (ITokenMinter minter);
}

// Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L138C1-L166C15
interface ITokenMessengerV2 {
// Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L138C1-L166C15
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
Expand Down Expand Up @@ -88,6 +88,39 @@ interface ITokenMessengerV2 {
uint256 maxFee,
uint32 minFinalityThreshold
) external;

// Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/TokenMessengerV2.sol#L180C1-L210C15
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - `hookData` is zero-length
* - `burnToken` is not supported
* - `destinationDomain` has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - maxFee is greater than or equal to `amount`.
* - MessageTransmitterV2#sendMessage reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain to receive message on
* @param mintRecipient address of mint recipient on destination domain, as bytes32
* @param burnToken token to burn `amount` of, on local domain
* @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0),
* any address can broadcast the message.
* @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken
* @param hookData hook data to append to burn message for interpretation on destination domain
*/
function depositForBurnWithHook(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller,
uint256 maxFee,
uint32 minFinalityThreshold,
bytes calldata hookData
) external;
}

/**
Expand Down Expand Up @@ -128,3 +161,38 @@ interface IMessageTransmitter {
bytes calldata messageBody
) external returns (uint64);
}

interface IMessageTransmitterV2 {
// Source: https://github.com/circlefin/evm-cctp-contracts/blob/63ab1f0ac06ce0793c0bbfbb8d09816bc211386d/src/v2/MessageTransmitterV2.sol#L176C1-L209C61
/**
* @notice Receive a message. Messages can only be broadcast once for a given nonce.
* The message body of a valid message is passed to the specified recipient for further processing.
*
* @dev Attestation format:
* A valid attestation is the concatenated 65-byte signature(s) of exactly
* `thresholdSignature` signatures, in increasing order of attester address.
* ***If the attester addresses recovered from signatures are not in
* increasing order, signature verification will fail.***
* If incorrect number of signatures or duplicate signatures are supplied,
* signature verification will fail.
*
* Message Format:
*
* Field Bytes Type Index
* version 4 uint32 0
* sourceDomain 4 uint32 4
* destinationDomain 4 uint32 8
* nonce 32 bytes32 12
* sender 32 bytes32 44
* recipient 32 bytes32 76
* destinationCaller 32 bytes32 108
* minFinalityThreshold 4 uint32 140
* finalityThresholdExecuted 4 uint32 144
* messageBody dynamic bytes 148
* @param message Message bytes
* @param attestation Concatenated 65-byte signature(s) of `message`, in increasing order
* of the attester address recovered from signatures.
* @return success True, if successful; false, if not
*/
function receiveMessage(bytes calldata message, bytes calldata attestation) external returns (bool success);
}
25 changes: 25 additions & 0 deletions contracts/external/interfaces/ILayerZeroComposer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0;

/**
* @title ILayerZeroComposer
* @dev Copied over from https://github.com/LayerZero-Labs/LayerZero-v2/blob/2ff4988f85b5c94032eb71bbc4073e69c078179d/packages/layerzero-v2/evm/protocol/contracts/interfaces/ILayerZeroComposer.sol#L8
*/
interface ILayerZeroComposer {
/**
* @notice Composes a LayerZero message from an OApp.
* @param _from The address initiating the composition, typically the OApp where the lzReceive was called.
* @param _guid The unique identifier for the corresponding LayerZero src/dst tx.
* @param _message The composed message payload in bytes. NOT necessarily the same payload passed via lzReceive.
* @param _executor The address of the executor for the composed message.
* @param _extraData Additional arbitrary data in bytes passed by the entity who executes the lzCompose.
*/
function lzCompose(
address _from,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) external payable;
}
99 changes: 99 additions & 0 deletions contracts/external/libraries/BytesLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Bytes } from "@openzeppelin/contracts-v5/utils/Bytes.sol";

library BytesLib {
/**************************************
* ERRORS *
**************************************/
error OutOfBounds();

/**************************************
* FUNCTIONS *
**************************************/

// The following 4 functions are copied from solidity-bytes-utils library
// https://github.com/GNSPS/solidity-bytes-utils/blob/fc502455bb2a7e26a743378df042612dd50d1eb9/contracts/BytesLib.sol#L323C5-L398C6
// Code was copied, and slightly modified to use revert instead of require

/**
* @notice Reads a uint16 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the uint16
* @return result The uint16 result
*/
function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16 result) {
if (_bytes.length < _start + 2) {
revert OutOfBounds();
}

// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x2), _start))
}
}

/**
* @notice Reads a uint32 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the uint32
* @return result The uint32 result
*/
function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result) {
if (_bytes.length < _start + 4) {
revert OutOfBounds();
}

// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x4), _start))
}
}

/**
* @notice Reads a uint256 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the uint256
* @return result The uint256 result
*/
function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result) {
if (_bytes.length < _start + 32) {
revert OutOfBounds();
}

// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x20), _start))
}
}

/**
* @notice Reads a bytes32 from a bytes array at a given start index
* @param _bytes The bytes array to convert
* @param _start The start index of the bytes32
* @return result The bytes32 result
*/
function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result) {
if (_bytes.length < _start + 32) {
revert OutOfBounds();
}

// solhint-disable-next-line no-inline-assembly
assembly {
result := mload(add(add(_bytes, 0x20), _start))
}
}

/**
* @notice Reads a bytes array from a bytes array at a given start index and length
* Source: OpenZeppelin Contracts v5 (utils/Bytes.sol)
* @param _bytes The bytes array to convert
* @param _start The start index of the bytes array
* @param _end The end index of the bytes array
* @return result The bytes array result
*/
function slice(bytes memory _bytes, uint256 _start, uint256 _end) internal pure returns (bytes memory result) {
return Bytes.slice(_bytes, _start, _end);
}
}
Loading
Loading