## CrossChain/ ### /CrossChainTellerBase.sol Defines a `BridgeData` struct to hold information needed for cross-chain bridging operations. ```solidity struct BridgeData { uint32 chainSelector; address destinationChainReceiver; ERC20 bridgeFeeToken; uint64 messageGas; bytes data; } ``` Declares the `CrossChainTellerBase` abstract contract, inheriting from `TellerWithMultiAssetSupport`. It defines events for sending and receiving messages, and a constructor that initializes the parent contract. Abstract contracts serve as base contracts for other contracts to inherit from and utilize their functionalities14. They provide a way to define a common interface or structure that derived contracts must implement. ```solidity abstract contract CrossChainTellerBase is TellerWithMultiAssetSupport { event MessageSent(bytes32 messageId, uint256 shareAmount, address to); event MessageReceived(bytes32 messageId, uint256 shareAmount, address to); constructor( address _owner, address _vault, address _accountant ) TellerWithMultiAssetSupport(_owner, _vault, _accountant) { } ``` This function allows users to deposit assets and bridge them cross-chain in a single transaction. It checks if the asset is supported, performs the deposit, and then calls the bridge function. ```solidity function depositAndBridge( ERC20 depositAsset, uint256 depositAmount, uint256 minimumMint, BridgeData calldata data ) external payable requiresAuth nonReentrant { // ... implementation } ``` Allows users to preview the fee required for bridging a specific amount of shares. ```solidity function previewFee(uint256 shareAmount, BridgeData calldata data) external view returns (uint256 fee) { return _quote(shareAmount, data); } ``` This function handles the bridging process for users who already have vault tokens. It burns the shares from the sender and calls the internal `_bridge` function. ```solidity function bridge( uint256 shareAmount, BridgeData calldata data ) public payable requiresAuth returns (bytes32 messageId) { if (isPaused) revert TellerWithMultiAssetSupport__Paused(); _beforeBridge(data); // Since shares are directly burned, call `beforeTransfer` to enforce before transfer hooks. beforeTransfer(msg.sender); // Burn shares from sender vault.exit(address(0), ERC20(address(0)), 0, msg.sender, shareAmount); messageId = _bridge(shareAmount, data); _afterBridge(shareAmount, data, messageId); } ``` These are internal virtual functions that can be overridden by inheriting contracts to implement specific bridging logic, fee calculation, and hooks for before and after bridging and receiving operations. ```solidity function _bridge(uint256 shareAmount, BridgeData calldata data) internal virtual returns (bytes32); function _quote(uint256 shareAmount, BridgeData calldata data) internal view virtual returns (uint256); function _beforeBridge(BridgeData calldata data) internal virtual; function _afterBridge(uint256 shareAmount, BridgeData calldata data, bytes32 messageId) internal virtual { emit MessageSent(messageId, shareAmount, data.destinationChainReceiver); } function _beforeReceive() internal virtual { if (isPaused) revert TellerWithMultiAssetSupport__Paused(); } function _afterReceive(uint256 shareAmount, address destinationChainReceiver, bytes32 messageId) internal virtual { emit MessageReceived(messageId, shareAmount, destinationChainReceiver); } ``` ### /CrossChainOPTellerWithMultiAssetSupport.sol This file defines a smart contract for cross-chain communication and asset bridging, specifically designed for Optimism (OP) layer 2 solution. It extends the functionality of a base contract called `CrossChainTellerBase`. The contract imports a base contract and defines an interface for cross-domain messaging. ```solidity import { CrossChainTellerBase, BridgeData, ERC20 } from "./CrossChainTellerBase.sol"; interface ICrossDomainMessenger { function xDomainMessageSender() external view returns (address); function sendMessage(address _target, bytes calldata _message, uint32 _gasLimit) external; } ``` This contract inherits from `CrossChainTellerBase`. ```solidity contract CrossChainOPTellerWithMultiAssetSupport is CrossChainTellerBase { // ... contract body } ``` - `messenger`: An immutable reference to the cross-domain messenger contract. - `peer`: The address of the peer contract on the other chain. - `maxMessageGas` and `minMessageGas`: Gas limits for cross-chain messages. - `nonce`: A counter for generating unique message IDs. Initializes the contract, setting up the messenger and initial peer address. ```solidity constructor( address _owner, address _vault, address _accountant, address _messenger ) CrossChainTellerBase(_owner, _vault, _accountant) { messenger = ICrossDomainMessenger(_messenger); peer = address(this); } ``` **Key Functions** a. `setPeer`: Allows the owner to set a new peer address. b. `setGasBounds`: Allows the owner to set new gas bounds for messages. c. `receiveBridgeMessage`: Receives cross-chain messages and mints shares. d. `_bridge`: Internal function to send cross-chain messages. e. `_beforeBridge`: Checks gas bounds and ensures no fee is paid. f. `_quote`: Returns the bridge fee (always 0 for OP). Why use custom errors? Is this designed to only be used for OP cross messaging or is it inspired by that? If it is specifly built for chains in the OP stack will there be additional CrossChain messangers for other chains? ### /MultiChainTellerBase.sol Imports the CrossChainTellerBase contract and BridgeData struct from CrossChainTellerBase.sol. ```solidity import { CrossChainTellerBase, BridgeData } from "./CrossChainTellerBase.sol"; ``` Defines a struct to store chain-specific information. ```solidity struct Chain { bool allowMessagesFrom; bool allowMessagesTo; address targetTeller; uint64 messageGasLimit; uint64 minimumMessageGas; } ``` Defines custom errors ```solidity error MultiChainTellerBase_MessagesNotAllowedFrom(uint32 chainSelector); error MultiChainTellerBase_MessagesNotAllowedFromSender(uint256 chainSelector, address sender); error MultiChainTellerBase_MessagesNotAllowedTo(uint256 chainSelector); error MultiChainTellerBase_ZeroMessageGasLimit(); error MultiChainTellerBase_GasLimitExceeded(); error MultiChainTellerBase_GasTooLow(); ``` Declares an abstract contract that inherits from `CrossChainTellerBase`. Abstract contracts serve as base contracts for other contracts to inherit from and utilize their functionalities14. They provide a way to define a common interface or structure that derived contracts must implement. **Events** ```solidity event ChainAdded( uint256 chainSelector, bool allowMessagesFrom, bool allowMessagesTo, address targetTeller, uint64 messageGasLimit, uint64 messageGasMin ); event ChainRemoved(uint256 chainSelector); event ChainAllowMessagesFrom(uint256 chainSelector, address targetTeller); event ChainAllowMessagesTo(uint256 chainSelector, address targetTeller); event ChainStopMessagesFrom(uint256 chainSelector); event ChainStopMessagesTo(uint256 chainSelector); event ChainSetGasLimit(uint256 chainSelector, uint64 messageGasLimit); ``` Stores chain information mapped to chain selectors. ```solidity mapping(uint32 => Chain) public selectorToChains; ``` Initializes the contract and calls the parent constructor. ```solidity constructor( address _owner, address _vault, address _accountant ) CrossChainTellerBase(_owner, _vault, _accountant) { } ``` **Chain Management functions** - `addChain`: Adds a new chain configuration. - `stopMessagesFromChain`: Blocks messages from a specific chain. - `allowMessagesFromChain`: Allows messages from a specific chain. - `removeChain`: Removes a chain configuration. - `allowMessagesToChain`: Allows messages to a specific chain. - `stopMessagesToChain`: Stops messages to a specific chain. - `setChainGasLimit`: Sets the gas limit for messages to a chain. Overrides the `_beforeBridge` function from the parent contract to perform checks before bridging, such as verifying if messages are allowed and gas limits are within acceptable ranges. ```solidity function _beforeBridge(BridgeData calldata data) internal override { if (!selectorToChains[data.chainSelector].allowMessagesTo) { revert MultiChainTellerBase_MessagesNotAllowedTo(data.chainSelector); } if (data.messageGas > selectorToChains[data.chainSelector].messageGasLimit) { revert MultiChainTellerBase_GasLimitExceeded(); } if (data.messageGas < selectorToChains[data.chainSelector].minimumMessageGas) { revert MultiChainTellerBase_GasTooLow(); } } ``` ### /MultiChainLayerZeroTellerWithMultiAssetSupport.sol Bring in necessary dependencies, including the base contracts and data structures used in this contract. ```solidity import { MultiChainTellerBase, MultiChainTellerBase_MessagesNotAllowedFrom } from "./MultiChainTellerBase.sol"; import { BridgeData, ERC20 } from "./CrossChainTellerBase.sol"; import { OAppAuth, MessagingFee, Origin, MessagingReceipt } from "./OAppAuth/OAppAuth.sol"; import { OptionsBuilder } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OptionsBuilder.sol"; ``` This defines the main contract, which inherits from `MultiChainTellerBase` and `OAppAuth`. It also uses the `OptionsBuilder` library for `bytes` and defines a custom error. ```solidity contract MultiChainLayerZeroTellerWithMultiAssetSupport is MultiChainTellerBase, OAppAuth { using OptionsBuilder for bytes; error MultiChainLayerZeroTellerWithMultiAssetSupport_InvalidToken(); ``` The constructor initializes the contract, calling the constructors of both parent contracts. ```solidity constructor( address _owner, address _vault, address _accountant, address _endpoint ) MultiChainTellerBase(_owner, _vault, _accountant) OAppAuth(_endpoint, _owner) { } ``` This internal function calculates the fee quote for bridging assets. It encodes the message, sets up options, and checks if the bridge fee token is native. It then returns the native fee required for the transaction. ```solidity function _quote(uint256 shareAmount, BridgeData calldata data) internal view override returns (uint256) { bytes memory _message = abi.encode(shareAmount, data.destinationChainReceiver); bytes memory _options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(data.messageGas, 0); if (address(data.bridgeFeeToken) != NATIVE) { revert MultiChainLayerZeroTellerWithMultiAssetSupport_InvalidToken(); } MessagingFee memory fee = _quote(data.chainSelector, _message, _options, false); return fee.nativeFee; } ``` This function is called when data is received from LayerZero. It checks if messages are allowed from the source chain, decodes the payload, and calls the vault to process the received shares. ```solidity function _lzReceive( Origin calldata _origin, bytes32 _guid, bytes calldata payload, address, // Executor address as specified by the OApp. bytes calldata // Any extra data or options to trigger on receipt. ) internal override { _beforeReceive(); if (!selectorToChains[_origin.srcEid].allowMessagesFrom) { revert MultiChainTellerBase_MessagesNotAllowedFrom(_origin.srcEid); } // Decode the payload to get the message (uint256 shareAmount, address receiver) = abi.decode(payload, (uint256, address)); vault.enter(address(0), ERC20(address(0)), 0, receiver, shareAmount); _afterReceive(shareAmount, receiver, _guid); } ``` This internal function handles the actual bridging logic. It checks if the bridge fee token is native, encodes the payload, sets up options, and sends the message through LayerZero. It returns a unique identifier for the transaction. ```solidity function _bridge(uint256 shareAmount, BridgeData calldata data) internal override returns (bytes32) { if (address(data.bridgeFeeToken) != NATIVE) { revert MultiChainLayerZeroTellerWithMultiAssetSupport_InvalidToken(); } bytes memory _payload = abi.encode(shareAmount, data.destinationChainReceiver); bytes memory _options = OptionsBuilder.newOptions().addExecutorLzReceiveOption(data.messageGas, 0); MessagingReceipt memory receipt = _lzSend( data.chainSelector, _payload, _options, // Fee in native gas and ZRO token. MessagingFee(msg.value, 0), // Refund address in case of failed source message. payable(msg.sender) ); return receipt.guid; } ```