> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# OVault EVM Implementation

> Step-by-step guide to ovault evm implementation using LayerZero V2. Build and deploy omnichain applications with crosschain messaging. Follow step-by-step d...

export const ContractInterface = ({contractName, interfaceType, interfaceName, abi: abiProp, description = null}) => {
  const {useState, useMemo, useEffect} = React;
  const [isDark, setIsDark] = useState(true);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const checkTheme = () => setIsDark(document.documentElement.classList.contains('dark'));
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const EMBEDDED_ABIS = {
    EndpointV2: [{
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "from",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "guid",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "uint16",
        "name": "index",
        "type": "uint16"
      }],
      "name": "ComposeDelivered",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "from",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "guid",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "uint16",
        "name": "index",
        "type": "uint16"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "message",
        "type": "bytes"
      }],
      "name": "ComposeSent",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "newLib",
        "type": "address"
      }],
      "name": "DefaultReceiveLibrarySet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "oldLib",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "expiry",
        "type": "uint256"
      }],
      "name": "DefaultReceiveLibraryTimeoutSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "newLib",
        "type": "address"
      }],
      "name": "DefaultSendLibrarySet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "delegate",
        "type": "address"
      }],
      "name": "DelegateSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "srcEid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "sender",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }],
      "name": "InboundNonceSkipped",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "newLib",
        "type": "address"
      }],
      "name": "LibraryRegistered",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "address",
        "name": "executor",
        "type": "address"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "guid",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "uint16",
        "name": "index",
        "type": "uint16"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "gas",
        "type": "uint256"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "message",
        "type": "bytes"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "extraData",
        "type": "bytes"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "reason",
        "type": "bytes"
      }],
      "name": "LzComposeAlert",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "executor",
        "type": "address"
      }, {
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "indexed": false,
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "guid",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "gas",
        "type": "uint256"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "message",
        "type": "bytes"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "extraData",
        "type": "bytes"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "reason",
        "type": "bytes"
      }],
      "name": "LzReceiveAlert",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "token",
        "type": "address"
      }],
      "name": "LzTokenSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "srcEid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "sender",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "payloadHash",
        "type": "bytes32"
      }],
      "name": "PacketBurnt",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "indexed": false,
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }],
      "name": "PacketDelivered",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "srcEid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "sender",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "payloadHash",
        "type": "bytes32"
      }],
      "name": "PacketNilified",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "bytes",
        "name": "encodedPayload",
        "type": "bytes"
      }, {
        "indexed": false,
        "internalType": "bytes",
        "name": "options",
        "type": "bytes"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "sendLibrary",
        "type": "address"
      }],
      "name": "PacketSent",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "indexed": false,
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "payloadHash",
        "type": "bytes32"
      }],
      "name": "PacketVerified",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "newLib",
        "type": "address"
      }],
      "name": "ReceiveLibrarySet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "oldLib",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "expiry",
        "type": "uint256"
      }],
      "name": "ReceiveLibraryTimeoutSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "address",
        "name": "newLib",
        "type": "address"
      }],
      "name": "SendLibrarySet",
      "type": "event"
    }, {
      "inputs": [],
      "name": "LZ_AlreadyRegistered",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ComposeExists",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ComposeNotFound",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "bytes32",
        "name": "expected",
        "type": "bytes32"
      }, {
        "internalType": "bytes32",
        "name": "actual",
        "type": "bytes32"
      }],
      "name": "LZ_DefaultReceiveLibraryUnavailable",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_DefaultSendLibraryUnavailable",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_IncompletePath",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "required",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "supplied",
        "type": "uint256"
      }],
      "name": "LZ_InsufficientFee",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_InvalidExpiry",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_InvalidNonce",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }],
      "name": "LZ_InvalidPayloadHash",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_InvalidReceiveLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_LzTokenUnavailable",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_NotRegisteredLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_OnlyAltToken",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_OnlyNonDefaultLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_OnlyReceiveLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_OnlyRegisteredLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_OnlyRegisteredOrDefaultLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_OnlySendLibrary",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_PathNotInitializable",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_PathNotVerifiable",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }],
      "name": "LZ_ReceiveReentrantCall",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_SameValue",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_SendReentrancy",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_Unauthorized",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_UnsupportedEid",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_UnsupportedInterface",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ZeroLzTokenFee",
      "type": "error"
    }],
    OApp: [{
      "inputs": [],
      "name": "InvalidDelegate",
      "type": "error"
    }, {
      "inputs": [],
      "name": "InvalidEndpointCall",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LzTokenUnavailable",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "NoPeer",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "msgValue",
        "type": "uint256"
      }],
      "name": "NotEnoughNative",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "addr",
        "type": "address"
      }],
      "name": "OnlyEndpoint",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "sender",
        "type": "bytes32"
      }],
      "name": "OnlyPeer",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }],
      "name": "OwnableInvalidOwner",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "account",
        "type": "address"
      }],
      "name": "OwnableUnauthorizedAccount",
      "type": "error"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }],
      "name": "OwnershipTransferred",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "peer",
        "type": "bytes32"
      }],
      "name": "PeerSet",
      "type": "event"
    }],
    OFT: [{
      "inputs": [{
        "internalType": "uint256",
        "name": "amountSD",
        "type": "uint256"
      }],
      "name": "AmountSDOverflowed",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "allowance",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "needed",
        "type": "uint256"
      }],
      "name": "ERC20InsufficientAllowance",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "balance",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "needed",
        "type": "uint256"
      }],
      "name": "ERC20InsufficientBalance",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "approver",
        "type": "address"
      }],
      "name": "ERC20InvalidApprover",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }],
      "name": "ERC20InvalidReceiver",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }],
      "name": "ERC20InvalidSender",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }],
      "name": "ERC20InvalidSpender",
      "type": "error"
    }, {
      "inputs": [],
      "name": "InvalidDelegate",
      "type": "error"
    }, {
      "inputs": [],
      "name": "InvalidEndpointCall",
      "type": "error"
    }, {
      "inputs": [],
      "name": "InvalidLocalDecimals",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "bytes",
        "name": "options",
        "type": "bytes"
      }],
      "name": "InvalidOptions",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LzTokenUnavailable",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "NoPeer",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "msgValue",
        "type": "uint256"
      }],
      "name": "NotEnoughNative",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "addr",
        "type": "address"
      }],
      "name": "OnlyEndpoint",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "sender",
        "type": "bytes32"
      }],
      "name": "OnlyPeer",
      "type": "error"
    }, {
      "inputs": [],
      "name": "OnlySelf",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }],
      "name": "OwnableInvalidOwner",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "account",
        "type": "address"
      }],
      "name": "OwnableUnauthorizedAccount",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "amountLD",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "minAmountLD",
        "type": "uint256"
      }],
      "name": "SlippageExceeded",
      "type": "error"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "Approval",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "struct EnforcedOptionParam[]",
        "name": "_enforcedOptions",
        "type": "tuple[]"
      }],
      "name": "EnforcedOptionSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "inspector",
        "type": "address"
      }],
      "name": "MsgInspectorSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "bytes32",
        "name": "guid",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "uint32",
        "name": "srcEid",
        "type": "uint32"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "toAddress",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountReceivedLD",
        "type": "uint256"
      }],
      "name": "OFTReceived",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "bytes32",
        "name": "guid",
        "type": "bytes32"
      }, {
        "indexed": false,
        "internalType": "uint32",
        "name": "dstEid",
        "type": "uint32"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "fromAddress",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountSentLD",
        "type": "uint256"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "amountReceivedLD",
        "type": "uint256"
      }],
      "name": "OFTSent",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "address",
        "name": "previousOwner",
        "type": "address"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }],
      "name": "OwnershipTransferred",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "indexed": false,
        "internalType": "bytes32",
        "name": "peer",
        "type": "bytes32"
      }],
      "name": "PeerSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address",
        "name": "preCrimeAddress",
        "type": "address"
      }],
      "name": "PreCrimeSet",
      "type": "event"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": true,
        "internalType": "address",
        "name": "from",
        "type": "address"
      }, {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "indexed": false,
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "Transfer",
      "type": "event"
    }],
    SendUln302: [{
      "inputs": [{
        "internalType": "uint32",
        "name": "configType",
        "type": "uint32"
      }],
      "name": "LZ_ULN_InvalidConfigType",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidRequiredDVNCount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidOptionalDVNCount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidOptionalDVNThreshold",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_AtLeastOneDVN",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidConfirmations",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_Unsorted",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint8",
        "name": "workerId",
        "type": "uint8"
      }],
      "name": "LZ_ULN_InvalidWorkerId",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "cursor",
        "type": "uint256"
      }],
      "name": "LZ_ULN_InvalidWorkerOptions",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint8",
        "name": "optionType",
        "type": "uint8"
      }],
      "name": "LZ_ULN_UnsupportedOptionType",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidLegacyType1Option",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidLegacyType2Option",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "LZ_ULN_UnsupportedEid",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "requested",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "available",
        "type": "uint256"
      }],
      "name": "LZ_MessageLib_InvalidAmount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_TransferFailed",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_OnlyEndpoint",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_InvalidExecutor",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_NotTreasury",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_CannotWithdrawAltToken",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "actual",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "max",
        "type": "uint256"
      }],
      "name": "LZ_MessageLib_InvalidMessageSize",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_ZeroMessageSize",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "cursor",
        "type": "uint256"
      }],
      "name": "DVN_InvalidDVNOptions",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "dvnIdx",
        "type": "uint256"
      }],
      "name": "DVN_InvalidDVNIdx",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "Transfer_NativeFailed",
      "type": "error"
    }, {
      "inputs": [],
      "name": "Transfer_ToAddressIsZero",
      "type": "error"
    }, {
      "anonymous": false,
      "inputs": [{
        "indexed": false,
        "internalType": "address[]",
        "name": "requiredDVNs",
        "type": "address[]"
      }, {
        "indexed": false,
        "internalType": "address[]",
        "name": "optionalDVNs",
        "type": "address[]"
      }, {
        "indexed": false,
        "internalType": "uint256[]",
        "name": "fees",
        "type": "uint256[]"
      }],
      "name": "DVNFeePaid",
      "type": "event"
    }],
    ReceiveUln302: [{
      "inputs": [],
      "name": "LZ_ULN_InvalidPacketHeader",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidPacketVersion",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidEid",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "configType",
        "type": "uint32"
      }],
      "name": "LZ_ULN_InvalidConfigType",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidRequiredDVNCount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidOptionalDVNCount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidOptionalDVNThreshold",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_AtLeastOneDVN",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidConfirmations",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_Unsorted",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "LZ_ULN_UnsupportedEid",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_Verifying",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_OnlyEndpoint",
      "type": "error"
    }],
    ReadLib1002: [{
      "inputs": [{
        "internalType": "uint32",
        "name": "configType",
        "type": "uint32"
      }],
      "name": "LZ_RL_InvalidConfigType",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidRequiredDVNCount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidOptionalDVNCount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidOptionalDVNThreshold",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidPacketHeader",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidPacketVersion",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidCmdHash",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidReceiver",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "requested",
        "type": "uint256"
      }, {
        "internalType": "uint256",
        "name": "available",
        "type": "uint256"
      }],
      "name": "LZ_RL_InvalidAmount",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_Verifying",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidEid",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_AtLeastOneDVN",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_Unsorted",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "LZ_RL_UnsupportedEid",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_MessageLib_OnlyEndpoint",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_InvalidExecutor",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_NotTreasury",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_RL_CannotWithdrawAltToken",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint8",
        "name": "workerId",
        "type": "uint8"
      }],
      "name": "LZ_ULN_InvalidWorkerId",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "cursor",
        "type": "uint256"
      }],
      "name": "LZ_ULN_InvalidWorkerOptions",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint8",
        "name": "optionType",
        "type": "uint8"
      }],
      "name": "LZ_ULN_UnsupportedOptionType",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidLegacyType1Option",
      "type": "error"
    }, {
      "inputs": [],
      "name": "LZ_ULN_InvalidLegacyType2Option",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "cursor",
        "type": "uint256"
      }],
      "name": "DVN_InvalidDVNOptions",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "uint256",
        "name": "dvnIdx",
        "type": "uint256"
      }],
      "name": "DVN_InvalidDVNIdx",
      "type": "error"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "Transfer_NativeFailed",
      "type": "error"
    }, {
      "inputs": [],
      "name": "Transfer_ToAddressIsZero",
      "type": "error"
    }]
  };
  const [isExpanded, setIsExpanded] = useState(false);
  const abi = abiProp || EMBEDDED_ABIS[contractName] || [];
  const interfaceAbi = useMemo(() => {
    return abi.find(item => item.type === interfaceType && item.name === interfaceName);
  }, [abi, interfaceType, interfaceName]);
  const KNOWN_HASHES = {
    'Transfer(address,address,uint256)': '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
    'Approval(address,address,uint256)': '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925',
    'OwnershipTransferred(address,address)': '0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0',
    'PeerSet(uint32,bytes32)': '0x238399d427b947898edb290f5ff0f9109849b1c3ba196a42e35f00c50a54b98b',
    'OFTSent(bytes32,uint32,address,uint256,uint256)': '0x85496b760a4b7f8d66384b9df21b381f5d1b1e79f229a47aaf4c232edc2fe59a',
    'OFTReceived(bytes32,uint32,address,uint256)': '0xefed6d3500546b29533b128a29e3a94d70788727f0507505ac12eaf2e578fd9c',
    'EnforcedOptionSet(tuple[])': '0xabfcc23fe004d3dcb13c3ca8a2f9d46fe40d605bffb52d6f15c835e4e63077df',
    'MsgInspectorSet(address)': '0xf0be4f1e87349231d80c36b33f9e8639658eeaf474014dee15a3e6a4d4414197',
    'PreCrimeSet(address)': '0xd48d879cef83a1c0bdda516f27b13ddb1b3f8bbac1c9e1511bb2a659c2427760',
    'ComposeDelivered(address,address,bytes32,uint16)': '0x0036c98efcf9e6641dfbc9051f66f405253e8e0c2ab4a24dccda15595b7378c8',
    'ComposeSent(address,address,bytes32,uint16,bytes)': '0x3d52ff888d033fd3dd1d8057da59e850c91d91a72c41dfa445b247dfedeb6dc1',
    'DefaultReceiveLibrarySet(uint32,address)': '0xc16891855cffb4a5ac51ac11864a3f3c96ba816cc45fe686c987ae36277de5ec',
    'DefaultReceiveLibraryTimeoutSet(uint32,address,uint256)': '0x55b28633cdb29709386f555dfc54418592ad475ce7a65a78ac5928af60ffb8f8',
    'DefaultSendLibrarySet(uint32,address)': '0x16aa0f528038ab41019e95bae5b418a50ba8532c5800e3b7ea2f517d3fa625f5',
    'DelegateSet(address,address)': '0x6ee10e9ed4d6ce9742703a498707862f4b00f1396a87195eb93267b3d7983981',
    'InboundNonceSkipped(uint32,bytes32,address,uint64)': '0x28f40053783033ef755556a0c3315379141f51a33aed8334174ffbadd90bde48',
    'LibraryRegistered(address)': '0x6b374d56679ca9463f27c85c6311e2bb7fde69bf201d3da39d53f10bd9d78af5',
    'LzComposeAlert(address,address,address,bytes32,uint16,uint256,uint256,bytes,bytes,bytes)': '0x8a0b1dce321c5c5fb42349bce46d18087c04140de520917661fb923e44a904b9',
    'LzReceiveAlert(address,address,tuple,bytes32,uint256,uint256,bytes,bytes,bytes)': '0x21f5530e549466b2a729a41e89261ce02761174ed179437d4bdf38654775bb28',
    'LzTokenSet(address)': '0xd476ec5ec1ac11cec3714d41e7ea49419471aceb9bd0dff1becfc3e363a62396',
    'PacketBurnt(uint32,bytes32,address,uint64,bytes32)': '0x7f68a37a6e69a0de35024a234558f9efe4b33b58657753d21eaaa82d51c3510e',
    'PacketDelivered(tuple,address)': '0x4c3d7720f9df83b50eb0d3aa6b66b40170d50fafd31b6faa27fe71e33e7758f4',
    'PacketNilified(uint32,bytes32,address,uint64,bytes32)': '0xaf0450c392c4f702515a457a362328c8aa21916048ca6d0419e248b30cb55292',
    'PacketSent(bytes,bytes,address)': '0x1ab700d4ced0c005b164c0f789fd09fcbb0156d4c2041b8a3bfbcd961cd1567f',
    'PacketVerified(tuple,address,bytes32)': '0x548eaf10ee96d1fd0c1c9f8862b83d33af264f6ddb110beb29560b414abf54c6',
    'ReceiveLibrarySet(address,uint32,address)': '0xcd6f92f5ac6185a5acfa02c92090746cec64d777269cbcd0ed031e396657a1c2',
    'ReceiveLibraryTimeoutSet(address,uint32,address,uint256)': '0x4e0a5bbfa0c11a64effb1ada324b5437a17272e1aed9320398715ef71bb20928',
    'SendLibrarySet(address,uint32,address)': '0x4cff966ebee29a156dcb34cf72c1d06231fb1777f6bdf6e8089819232f002b1c',
    'DVNFeePaid(address[],address[],uint256[])': '0x07ea52d82345d6e838192107d8fd7123d9c2ec8e916cd0aad13fd2b60db24644'
  };
  const KNOWN_ERROR_SELECTORS = {
    'LZ_AlreadyRegistered()': '0x8aea2fe6',
    'LZ_ComposeExists()': '0x15082194',
    'LZ_ComposeNotFound()': '0x1b662763',
    'LZ_DefaultReceiveLibraryUnavailable(bytes32,bytes32)': '0xc9a6dd88',
    'LZ_DefaultSendLibraryUnavailable()': '0xd586961a',
    'LZ_IncompletePath()': '0xf6ad1b1e',
    'LZ_InsufficientFee(uint256,uint256)': '0xd247c105',
    'LZ_InvalidExpiry()': '0x05df9f22',
    'LZ_InvalidNonce()': '0xff76c58a',
    'LZ_InvalidPayloadHash(uint64)': '0x1e53a72a',
    'LZ_InvalidReceiveLibrary()': '0x4fa6ecac',
    'LZ_LzTokenUnavailable()': '0x5af6d2aa',
    'LZ_NotRegisteredLibrary()': '0xb79784d5',
    'LZ_OnlyAltToken()': '0x9a317a52',
    'LZ_OnlyNonDefaultLibrary()': '0x379052a0',
    'LZ_OnlyReceiveLibrary()': '0xa455bbce',
    'LZ_OnlyRegisteredLibrary()': '0x3bb77a06',
    'LZ_OnlyRegisteredOrDefaultLibrary()': '0xfe85efd0',
    'LZ_OnlySendLibrary()': '0x35e31cfd',
    'LZ_PathNotInitializable()': '0x751cb20f',
    'LZ_PathNotVerifiable()': '0x0177e1ca',
    'LZ_ReceiveReentrantCall(address)': '0x30d14180',
    'LZ_SameValue()': '0xd0ecb66b',
    'LZ_SendReentrancy()': '0xee120b09',
    'LZ_Unauthorized()': '0xc4c52593',
    'LZ_UnsupportedEid()': '0x391daaa4',
    'LZ_UnsupportedInterface()': '0xeb64f35d',
    'LZ_ZeroLzTokenFee()': '0x17751237',
    'LZ_ULN_InvalidConfigType(uint32)': '0xba97c1fa',
    'LZ_ULN_InvalidRequiredDVNCount()': '0x83aa17da',
    'LZ_ULN_InvalidOptionalDVNCount()': '0x42211366',
    'LZ_ULN_InvalidOptionalDVNThreshold()': '0x38682fa9',
    'LZ_ULN_AtLeastOneDVN()': '0xce2c3751',
    'LZ_ULN_InvalidConfirmations()': '0x503667ae',
    'LZ_ULN_Unsorted()': '0x447516e1',
    'LZ_ULN_InvalidWorkerId(uint8)': '0x6780cfaf',
    'LZ_ULN_InvalidWorkerOptions(uint256)': '0x6592671c',
    'LZ_ULN_UnsupportedOptionType(uint8)': '0x496926ad',
    'LZ_ULN_InvalidLegacyType1Option()': '0x0dc652a8',
    'LZ_ULN_InvalidLegacyType2Option()': '0xc0927c56',
    'LZ_ULN_UnsupportedEid(uint32)': '0xf0c10d04',
    'LZ_ULN_InvalidPacketHeader()': '0xc9bf37b7',
    'LZ_ULN_InvalidPacketVersion()': '0x3a9ae7b9',
    'LZ_ULN_InvalidEid()': '0x42d2c97e',
    'LZ_ULN_Verifying()': '0x4c3118d4',
    'LZ_MessageLib_InvalidAmount(uint256,uint256)': '0x34084fb8',
    'LZ_MessageLib_TransferFailed()': '0xde5999a0',
    'LZ_MessageLib_OnlyEndpoint()': '0x467409c3',
    'LZ_MessageLib_InvalidExecutor()': '0x20e9d05a',
    'LZ_MessageLib_NotTreasury()': '0x885762a7',
    'LZ_MessageLib_CannotWithdrawAltToken()': '0xe695d044',
    'LZ_MessageLib_InvalidMessageSize(uint256,uint256)': '0xc667af3e',
    'LZ_MessageLib_ZeroMessageSize()': '0x49cfac43',
    'LZ_RL_InvalidConfigType(uint32)': '0x0cb84e82',
    'LZ_RL_InvalidRequiredDVNCount()': '0x55a01c02',
    'LZ_RL_InvalidOptionalDVNCount()': '0xe35436c6',
    'LZ_RL_InvalidOptionalDVNThreshold()': '0xf92940be',
    'LZ_RL_InvalidPacketHeader()': '0x864d08b3',
    'LZ_RL_InvalidPacketVersion()': '0x7952a82f',
    'LZ_RL_InvalidCmdHash()': '0x660e96ee',
    'LZ_RL_InvalidReceiver()': '0xa4884a59',
    'LZ_RL_InvalidAmount(uint256,uint256)': '0xb79d1263',
    'LZ_RL_Verifying()': '0x28ca4b51',
    'LZ_RL_InvalidEid()': '0x3e96df16',
    'LZ_RL_AtLeastOneDVN()': '0x9b5f9f7a',
    'LZ_RL_Unsorted()': '0xc0762a5d',
    'LZ_RL_UnsupportedEid(uint32)': '0x0ff03e54',
    'LZ_RL_InvalidExecutor()': '0x76df55a6',
    'LZ_RL_NotTreasury()': '0x96830d1e',
    'LZ_RL_CannotWithdrawAltToken()': '0xb77f5044',
    'DVN_InvalidDVNOptions(uint256)': '0x04eb6e0c',
    'DVN_InvalidDVNIdx(uint256)': '0xfc4f2308',
    'Transfer_NativeFailed(address,uint256)': '0x465bc834',
    'Transfer_ToAddressIsZero()': '0x6b7a9310',
    'SlippageExceeded(uint256,uint256)': '0x71c4efed',
    'AmountSDOverflowed(uint256)': '0xe2ce9413',
    'InvalidLocalDecimals()': '0x1e9714b0',
    'InvalidOptions(bytes)': '0x9a6d49cd',
    'OnlySelf()': '0x14d4a4e8',
    'OnlyPeer(uint32,bytes32)': '0xc26bebcc',
    'NoPeer(uint32)': '0xf6ff4fb7',
    'InvalidEndpointCall()': '0x0fbdec0a',
    'InvalidDelegate()': '0xb5863604',
    'NotEnoughNative(uint256)': '0x9f704120',
    'OnlyEndpoint(address)': '0x91ac5e4f',
    'LzTokenUnavailable()': '0x5373352a',
    'ERC20InsufficientAllowance(address,uint256,uint256)': '0xfb8f41b2',
    'ERC20InsufficientBalance(address,uint256,uint256)': '0xe450d38c',
    'ERC20InvalidApprover(address)': '0xe602df05',
    'ERC20InvalidReceiver(address)': '0xec442f05',
    'ERC20InvalidSender(address)': '0x96c6fd1e',
    'ERC20InvalidSpender(address)': '0x94280d62',
    'OwnableInvalidOwner(address)': '0x1e4fbdf7',
    'OwnableUnauthorizedAccount(address)': '0x118cdaa7'
  };
  const getCanonicalSignature = () => {
    if (!interfaceAbi) return null;
    const paramTypes = interfaceAbi.inputs?.map(input => input.type).join(',') || '';
    return `${interfaceName}(${paramTypes})`;
  };
  const getFullSignature = () => {
    if (!interfaceAbi) return null;
    const params = interfaceAbi.inputs?.map(input => {
      const indexed = interfaceType === 'event' && input.indexed ? ' indexed' : '';
      const name = input.name ? ` ${input.name}` : '';
      return `${input.type}${indexed}${name}`;
    }).join(', ') || '';
    return `${interfaceName}(${params})`;
  };
  const getTopicHash = () => {
    const sig = getCanonicalSignature();
    return KNOWN_HASHES[sig] || null;
  };
  const getErrorSelector = () => {
    const sig = getCanonicalSignature();
    return KNOWN_ERROR_SELECTORS[sig] || null;
  };
  if (!interfaceAbi) {
    return <div className="rounded-xl border overflow-hidden mb-4" style={{
      borderColor: isDark ? '#3f3f46' : '#e4e4e7',
      backgroundColor: isDark ? '#18181b' : '#ffffff'
    }}>
        <div className="p-4 text-sm" style={{
      color: isDark ? '#ef4444' : '#dc2626'
    }}>
          {interfaceType} "{interfaceName}" not found in {contractName} ABI
        </div>
      </div>;
  }
  const isEvent = interfaceType === 'event';
  const isError = interfaceType === 'error';
  return <div className="rounded-xl border overflow-hidden mb-4" style={{
    borderColor: isDark ? '#3f3f46' : '#e4e4e7',
    backgroundColor: isDark ? '#18181b' : '#ffffff'
  }}>
      {}
      <div className="flex items-center justify-between p-3 cursor-pointer transition-colors" style={{
    backgroundColor: isDark ? '#27272a' : '#f4f4f5'
  }} onClick={() => setIsExpanded(!isExpanded)}>
        <div className="flex items-center gap-3 flex-1 min-w-0">
          {}
          <span className="px-2 py-1 rounded text-xs font-semibold" style={{
    backgroundColor: isEvent ? '#8b5cf6' : '#ef4444',
    color: '#ffffff'
  }}>
            {interfaceType.toUpperCase()}
          </span>

          {}
          {isError && getErrorSelector() && <code className="px-2 py-1 rounded text-xs font-mono border" style={{
    backgroundColor: isDark ? '#1d1d1d' : '#e7e0e0',
    color: isDark ? '#fca5a5' : '#dc2626',
    borderColor: isDark ? '#2d2d2d' : '#d4cdcd'
  }}>
              {getErrorSelector()}
            </code>}

          {}
          <div className="font-mono text-sm truncate" style={{
    color: isDark ? '#e4e4e7' : '#27272a'
  }}>
            <span style={{
    color: isDark ? '#60a5fa' : '#2563eb'
  }}>{interfaceName}</span>
            <span style={{
    color: isDark ? '#a1a1aa' : '#71717a'
  }}>(</span>
            {interfaceAbi.inputs && interfaceAbi.inputs.map((input, index) => <span key={index}>
                {isEvent && input.indexed && <span style={{
    color: '#f59e0b'
  }}>indexed </span>}
                <span style={{
    color: isDark ? '#4ade80' : '#16a34a'
  }}>{input.type}</span>
                {input.name && <span style={{
    color: isDark ? '#a1a1aa' : '#71717a'
  }}> {input.name}</span>}
                {index < interfaceAbi.inputs.length - 1 && <span style={{
    color: isDark ? '#a1a1aa' : '#71717a'
  }}>, </span>}
              </span>)}
            <span style={{
    color: isDark ? '#a1a1aa' : '#71717a'
  }}>)</span>
          </div>
        </div>

        {}
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" style={{
    color: isDark ? '#71717a' : '#a1a1aa',
    transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
    transition: 'transform 0.2s'
  }}>
          <path d="M6 9L12 15L18 9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </div>

      {}
      {isExpanded && <div className="p-4" style={{
    backgroundColor: isDark ? '#18181b' : '#ffffff'
  }}>
          {}
          {description && <div className="mb-4 text-sm" style={{
    color: isDark ? '#a1a1aa' : '#71717a'
  }}>
              {description}
            </div>}

          {}
          {interfaceAbi.inputs && interfaceAbi.inputs.length > 0 && <div className="mb-4">
              <div className="text-xs font-semibold uppercase tracking-wide mb-2" style={{
    color: isDark ? '#71717a' : '#a1a1aa'
  }}>
                PARAMETERS
              </div>
              <div className="rounded-lg p-3 space-y-2" style={{
    backgroundColor: isDark ? '#27272a' : '#f4f4f5'
  }}>
                {interfaceAbi.inputs.map((input, index) => <div key={index} className="flex items-start gap-2">
                    <span className="font-mono text-sm" style={{
    color: isDark ? '#60a5fa' : '#2563eb'
  }}>
                      {input.name || 'param' + index}
                    </span>
                    <span className="font-mono text-sm" style={{
    color: isDark ? '#4ade80' : '#16a34a'
  }}>
                      {input.type}
                    </span>
                    {isEvent && input.indexed && <span className="px-1.5 py-0.5 rounded text-xs" style={{
    backgroundColor: isDark ? '#78350f' : '#fef3c7',
    color: isDark ? '#fbbf24' : '#92400e'
  }}>
                        indexed
                      </span>}
                    {}
                    {input.type === 'tuple' && input.components && <div className="ml-4 mt-1 pl-3 border-l-2 space-y-1" style={{
    borderColor: isDark ? '#3f3f46' : '#e4e4e7'
  }}>
                        {input.components.map((comp, compIndex) => <div key={compIndex} className="flex items-center gap-2 text-sm font-mono">
                            <span style={{
    color: isDark ? '#a1a1aa' : '#71717a'
  }}>{comp.name}</span>
                            <span style={{
    color: isDark ? '#4ade80' : '#16a34a'
  }}>{comp.type}</span>
                          </div>)}
                      </div>}
                  </div>)}
              </div>
            </div>}

          {}
          <div>
            <div className="text-xs font-semibold uppercase tracking-wide mb-2" style={{
    color: isDark ? '#71717a' : '#a1a1aa'
  }}>
              SIGNATURE
            </div>
            <div className="rounded-lg p-3 font-mono text-sm overflow-x-auto" style={{
    backgroundColor: isDark ? '#27272a' : '#f4f4f5',
    color: isDark ? '#e4e4e7' : '#27272a'
  }}>
              <code>{getFullSignature()}</code>
            </div>
            {isError && <div className="mt-2 text-xs" style={{
    color: isDark ? '#71717a' : '#a1a1aa'
  }}>
                <span>4-byte selector: </span>
                <code className="px-1.5 py-0.5 rounded" style={{
    backgroundColor: isDark ? '#27272a' : '#f4f4f5'
  }}>
                  {getErrorSelector() || `keccak256("${getCanonicalSignature()}")[0:4]`}
                </code>
              </div>}
            {isEvent && <div className="mt-2 text-xs" style={{
    color: isDark ? '#71717a' : '#a1a1aa'
  }}>
                <span>Topic hash: </span>
                <code className="px-1.5 py-0.5 rounded break-all" style={{
    backgroundColor: isDark ? '#27272a' : '#f4f4f5'
  }}>
                  {getTopicHash() || `keccak256("${getCanonicalSignature()}")`}
                </code>
              </div>}
          </div>
        </div>}
    </div>;
};

export const InteractiveContract = ({contractName, functionName, abi: abiProp, description = null, showDeployments = true, disableChainSelection = true}) => {
  const {useState, useEffect, useRef, useMemo} = React;
  const EMBEDDED_ABIS = {
    EndpointV2: [{
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_srcEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_sender",
        "type": "bytes32"
      }, {
        "internalType": "uint64",
        "name": "_nonce",
        "type": "uint64"
      }, {
        "internalType": "bytes32",
        "name": "_payloadHash",
        "type": "bytes32"
      }],
      "name": "burn",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }],
      "name": "clear",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_from",
        "type": "address"
      }, {
        "internalType": "address",
        "name": "_to",
        "type": "address"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "uint16",
        "name": "_index",
        "type": "uint16"
      }],
      "name": "composeQueue",
      "outputs": [{
        "internalType": "bytes32",
        "name": "messageHash",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "defaultReceiveLibrary",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "defaultReceiveLibraryTimeout",
      "outputs": [{
        "internalType": "address",
        "name": "lib",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "expiry",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "defaultSendLibrary",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "oapp",
        "type": "address"
      }],
      "name": "delegates",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "eid",
      "outputs": [{
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "uint32",
        "name": "_configType",
        "type": "uint32"
      }],
      "name": "getConfig",
      "outputs": [{
        "internalType": "bytes",
        "name": "config",
        "type": "bytes"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "getReceiveLibrary",
      "outputs": [{
        "internalType": "address",
        "name": "lib",
        "type": "address"
      }, {
        "internalType": "bool",
        "name": "isDefault",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "getRegisteredLibraries",
      "outputs": [{
        "internalType": "address[]",
        "name": "",
        "type": "address[]"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "getSendContext",
      "outputs": [{
        "internalType": "uint32",
        "name": "dstEid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "getSendLibrary",
      "outputs": [{
        "internalType": "address",
        "name": "lib",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_srcEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_sender",
        "type": "bytes32"
      }],
      "name": "inboundNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_srcEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_sender",
        "type": "bytes32"
      }, {
        "internalType": "uint64",
        "name": "_nonce",
        "type": "uint64"
      }],
      "name": "inboundPayloadHash",
      "outputs": [{
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }],
      "name": "initializable",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "isDefaultSendLibrary",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }],
      "name": "isRegisteredLibrary",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "isSendingMessage",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "isSupportedEid",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }],
      "name": "isValidReceiveLibrary",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_srcEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_sender",
        "type": "bytes32"
      }],
      "name": "lazyInboundNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_from",
        "type": "address"
      }, {
        "internalType": "address",
        "name": "_to",
        "type": "address"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "uint16",
        "name": "_index",
        "type": "uint16"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzCompose",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceive",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [],
      "name": "lzToken",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "nativeToken",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_dstEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_receiver",
        "type": "bytes32"
      }],
      "name": "nextGuid",
      "outputs": [{
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_srcEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_sender",
        "type": "bytes32"
      }, {
        "internalType": "uint64",
        "name": "_nonce",
        "type": "uint64"
      }, {
        "internalType": "bytes32",
        "name": "_payloadHash",
        "type": "bytes32"
      }],
      "name": "nilify",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_dstEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_receiver",
        "type": "bytes32"
      }],
      "name": "outboundNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "receiver",
          "type": "bytes32"
        }, {
          "internalType": "bytes",
          "name": "message",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "options",
          "type": "bytes"
        }, {
          "internalType": "bool",
          "name": "payInLzToken",
          "type": "bool"
        }],
        "internalType": "struct MessagingParams",
        "name": "_params",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }],
      "name": "quote",
      "outputs": [{
        "components": [{
          "internalType": "uint256",
          "name": "nativeFee",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "lzTokenFee",
          "type": "uint256"
        }],
        "internalType": "struct MessagingFee",
        "name": "",
        "type": "tuple"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }],
      "name": "receiveLibraryTimeout",
      "outputs": [{
        "internalType": "address",
        "name": "lib",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "expiry",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }],
      "name": "registerLibrary",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "receiver",
          "type": "bytes32"
        }, {
          "internalType": "bytes",
          "name": "message",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "options",
          "type": "bytes"
        }, {
          "internalType": "bool",
          "name": "payInLzToken",
          "type": "bool"
        }],
        "internalType": "struct MessagingParams",
        "name": "_params",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_refundAddress",
        "type": "address"
      }],
      "name": "send",
      "outputs": [{
        "components": [{
          "internalType": "bytes32",
          "name": "guid",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }, {
          "components": [{
            "internalType": "uint256",
            "name": "nativeFee",
            "type": "uint256"
          }, {
            "internalType": "uint256",
            "name": "lzTokenFee",
            "type": "uint256"
          }],
          "internalType": "struct MessagingFee",
          "name": "fee",
          "type": "tuple"
        }],
        "internalType": "struct MessagingReceipt",
        "name": "",
        "type": "tuple"
      }],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_to",
        "type": "address"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "uint16",
        "name": "_index",
        "type": "uint16"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }],
      "name": "sendCompose",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }, {
        "components": [{
          "internalType": "uint32",
          "name": "eid",
          "type": "uint32"
        }, {
          "internalType": "uint32",
          "name": "configType",
          "type": "uint32"
        }, {
          "internalType": "bytes",
          "name": "config",
          "type": "bytes"
        }],
        "internalType": "struct SetConfigParam[]",
        "name": "_params",
        "type": "tuple[]"
      }],
      "name": "setConfig",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_newLib",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "_gracePeriod",
        "type": "uint256"
      }],
      "name": "setDefaultReceiveLibrary",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "_expiry",
        "type": "uint256"
      }],
      "name": "setDefaultReceiveLibraryTimeout",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_newLib",
        "type": "address"
      }],
      "name": "setDefaultSendLibrary",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_delegate",
        "type": "address"
      }],
      "name": "setDelegate",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_lzToken",
        "type": "address"
      }],
      "name": "setLzToken",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_newLib",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "_gracePeriod",
        "type": "uint256"
      }],
      "name": "setReceiveLibrary",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_lib",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "_expiry",
        "type": "uint256"
      }],
      "name": "setReceiveLibraryTimeout",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_newLib",
        "type": "address"
      }],
      "name": "setSendLibrary",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_oapp",
        "type": "address"
      }, {
        "internalType": "uint32",
        "name": "_srcEid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_sender",
        "type": "bytes32"
      }, {
        "internalType": "uint64",
        "name": "_nonce",
        "type": "uint64"
      }],
      "name": "skip",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }],
      "name": "verifiable",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_receiver",
        "type": "address"
      }, {
        "internalType": "bytes32",
        "name": "_payloadHash",
        "type": "bytes32"
      }],
      "name": "verify",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }],
    OApp: [{
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }],
      "name": "allowInitializePath",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "endpoint",
      "outputs": [{
        "internalType": "contract ILayerZeroEndpointV2",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "",
        "type": "tuple"
      }, {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }],
      "name": "isComposeMsgSender",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceive",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }],
      "name": "nextNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oAppVersion",
      "outputs": [{
        "internalType": "uint64",
        "name": "senderVersion",
        "type": "uint64"
      }, {
        "internalType": "uint64",
        "name": "receiverVersion",
        "type": "uint64"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [],
      "name": "owner",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "peers",
      "outputs": [{
        "internalType": "bytes32",
        "name": "peer",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "renounceOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_delegate",
        "type": "address"
      }],
      "name": "setDelegate",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_peer",
        "type": "bytes32"
      }],
      "name": "setPeer",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }],
      "name": "transferOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }],
    OAppRead: [{
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }],
      "name": "allowInitializePath",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "endpoint",
      "outputs": [{
        "internalType": "contract ILayerZeroEndpointV2",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "",
        "type": "tuple"
      }, {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }],
      "name": "isComposeMsgSender",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceive",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }],
      "name": "nextNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oAppVersion",
      "outputs": [{
        "internalType": "uint64",
        "name": "senderVersion",
        "type": "uint64"
      }, {
        "internalType": "uint64",
        "name": "receiverVersion",
        "type": "uint64"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [],
      "name": "owner",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "peers",
      "outputs": [{
        "internalType": "bytes32",
        "name": "peer",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "renounceOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_delegate",
        "type": "address"
      }],
      "name": "setDelegate",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_peer",
        "type": "bytes32"
      }],
      "name": "setPeer",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_channelId",
        "type": "uint32"
      }, {
        "internalType": "bool",
        "name": "_active",
        "type": "bool"
      }],
      "name": "setReadChannel",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }],
      "name": "transferOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }],
    OFT: [{
      "inputs": [],
      "name": "SEND",
      "outputs": [{
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "SEND_AND_CALL",
      "outputs": [{
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }],
      "name": "allowInitializePath",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "owner",
        "type": "address"
      }, {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }],
      "name": "allowance",
      "outputs": [{
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "approvalRequired",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "approve",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "account",
        "type": "address"
      }],
      "name": "balanceOf",
      "outputs": [{
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "uint16",
        "name": "_msgType",
        "type": "uint16"
      }, {
        "internalType": "bytes",
        "name": "_extraOptions",
        "type": "bytes"
      }],
      "name": "combineOptions",
      "outputs": [{
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "decimalConversionRate",
      "outputs": [{
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "decimals",
      "outputs": [{
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "endpoint",
      "outputs": [{
        "internalType": "contract ILayerZeroEndpointV2",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "internalType": "uint16",
        "name": "msgType",
        "type": "uint16"
      }],
      "name": "enforcedOptions",
      "outputs": [{
        "internalType": "bytes",
        "name": "enforcedOption",
        "type": "bytes"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "",
        "type": "tuple"
      }, {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }],
      "name": "isComposeMsgSender",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_peer",
        "type": "bytes32"
      }],
      "name": "isPeer",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceive",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "components": [{
            "internalType": "uint32",
            "name": "srcEid",
            "type": "uint32"
          }, {
            "internalType": "bytes32",
            "name": "sender",
            "type": "bytes32"
          }, {
            "internalType": "uint64",
            "name": "nonce",
            "type": "uint64"
          }],
          "internalType": "struct Origin",
          "name": "origin",
          "type": "tuple"
        }, {
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "address",
          "name": "receiver",
          "type": "address"
        }, {
          "internalType": "bytes32",
          "name": "guid",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "value",
          "type": "uint256"
        }, {
          "internalType": "address",
          "name": "executor",
          "type": "address"
        }, {
          "internalType": "bytes",
          "name": "message",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "extraData",
          "type": "bytes"
        }],
        "internalType": "struct InboundPacket[]",
        "name": "_packets",
        "type": "tuple[]"
      }],
      "name": "lzReceiveAndRevert",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceiveSimulate",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [],
      "name": "msgInspector",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "name",
      "outputs": [{
        "internalType": "string",
        "name": "",
        "type": "string"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }],
      "name": "nextNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oApp",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oAppVersion",
      "outputs": [{
        "internalType": "uint64",
        "name": "senderVersion",
        "type": "uint64"
      }, {
        "internalType": "uint64",
        "name": "receiverVersion",
        "type": "uint64"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oftVersion",
      "outputs": [{
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }, {
        "internalType": "uint64",
        "name": "version",
        "type": "uint64"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [],
      "name": "owner",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "peers",
      "outputs": [{
        "internalType": "bytes32",
        "name": "peer",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "preCrime",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "to",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "amountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "bytes",
          "name": "extraOptions",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "composeMsg",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "oftCmd",
          "type": "bytes"
        }],
        "internalType": "struct SendParam",
        "name": "_sendParam",
        "type": "tuple"
      }],
      "name": "quoteOFT",
      "outputs": [{
        "components": [{
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "maxAmountLD",
          "type": "uint256"
        }],
        "internalType": "struct OFTLimit",
        "name": "oftLimit",
        "type": "tuple"
      }, {
        "components": [{
          "internalType": "int256",
          "name": "feeAmountLD",
          "type": "int256"
        }, {
          "internalType": "string",
          "name": "description",
          "type": "string"
        }],
        "internalType": "struct OFTFeeDetail[]",
        "name": "oftFeeDetails",
        "type": "tuple[]"
      }, {
        "components": [{
          "internalType": "uint256",
          "name": "amountSentLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "amountReceivedLD",
          "type": "uint256"
        }],
        "internalType": "struct OFTReceipt",
        "name": "oftReceipt",
        "type": "tuple"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "to",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "amountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "bytes",
          "name": "extraOptions",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "composeMsg",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "oftCmd",
          "type": "bytes"
        }],
        "internalType": "struct SendParam",
        "name": "_sendParam",
        "type": "tuple"
      }, {
        "internalType": "bool",
        "name": "_payInLzToken",
        "type": "bool"
      }],
      "name": "quoteSend",
      "outputs": [{
        "components": [{
          "internalType": "uint256",
          "name": "nativeFee",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "lzTokenFee",
          "type": "uint256"
        }],
        "internalType": "struct MessagingFee",
        "name": "msgFee",
        "type": "tuple"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "renounceOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "to",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "amountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "bytes",
          "name": "extraOptions",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "composeMsg",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "oftCmd",
          "type": "bytes"
        }],
        "internalType": "struct SendParam",
        "name": "_sendParam",
        "type": "tuple"
      }, {
        "components": [{
          "internalType": "uint256",
          "name": "nativeFee",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "lzTokenFee",
          "type": "uint256"
        }],
        "internalType": "struct MessagingFee",
        "name": "_fee",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_refundAddress",
        "type": "address"
      }],
      "name": "send",
      "outputs": [{
        "components": [{
          "internalType": "bytes32",
          "name": "guid",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }, {
          "components": [{
            "internalType": "uint256",
            "name": "nativeFee",
            "type": "uint256"
          }, {
            "internalType": "uint256",
            "name": "lzTokenFee",
            "type": "uint256"
          }],
          "internalType": "struct MessagingFee",
          "name": "fee",
          "type": "tuple"
        }],
        "internalType": "struct MessagingReceipt",
        "name": "msgReceipt",
        "type": "tuple"
      }, {
        "components": [{
          "internalType": "uint256",
          "name": "amountSentLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "amountReceivedLD",
          "type": "uint256"
        }],
        "internalType": "struct OFTReceipt",
        "name": "oftReceipt",
        "type": "tuple"
      }],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_delegate",
        "type": "address"
      }],
      "name": "setDelegate",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "eid",
          "type": "uint32"
        }, {
          "internalType": "uint16",
          "name": "msgType",
          "type": "uint16"
        }, {
          "internalType": "bytes",
          "name": "options",
          "type": "bytes"
        }],
        "internalType": "struct EnforcedOptionParam[]",
        "name": "_enforcedOptions",
        "type": "tuple[]"
      }],
      "name": "setEnforcedOptions",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_msgInspector",
        "type": "address"
      }],
      "name": "setMsgInspector",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_peer",
        "type": "bytes32"
      }],
      "name": "setPeer",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_preCrime",
        "type": "address"
      }],
      "name": "setPreCrime",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [],
      "name": "sharedDecimals",
      "outputs": [{
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "symbol",
      "outputs": [{
        "internalType": "string",
        "name": "",
        "type": "string"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "token",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "totalSupply",
      "outputs": [{
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "transfer",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "from",
        "type": "address"
      }, {
        "internalType": "address",
        "name": "to",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }],
      "name": "transferFrom",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }],
      "name": "transferOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }],
    OFTAdapter: [{
      "inputs": [],
      "name": "SEND",
      "outputs": [{
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "SEND_AND_CALL",
      "outputs": [{
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "origin",
        "type": "tuple"
      }],
      "name": "allowInitializePath",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "approvalRequired",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "uint16",
        "name": "_msgType",
        "type": "uint16"
      }, {
        "internalType": "bytes",
        "name": "_extraOptions",
        "type": "bytes"
      }],
      "name": "combineOptions",
      "outputs": [{
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "decimalConversionRate",
      "outputs": [{
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "endpoint",
      "outputs": [{
        "internalType": "contract ILayerZeroEndpointV2",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }, {
        "internalType": "uint16",
        "name": "msgType",
        "type": "uint16"
      }],
      "name": "enforcedOptions",
      "outputs": [{
        "internalType": "bytes",
        "name": "enforcedOption",
        "type": "bytes"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "",
        "type": "tuple"
      }, {
        "internalType": "bytes",
        "name": "",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }],
      "name": "isComposeMsgSender",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_peer",
        "type": "bytes32"
      }],
      "name": "isPeer",
      "outputs": [{
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceive",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "components": [{
            "internalType": "uint32",
            "name": "srcEid",
            "type": "uint32"
          }, {
            "internalType": "bytes32",
            "name": "sender",
            "type": "bytes32"
          }, {
            "internalType": "uint64",
            "name": "nonce",
            "type": "uint64"
          }],
          "internalType": "struct Origin",
          "name": "origin",
          "type": "tuple"
        }, {
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "address",
          "name": "receiver",
          "type": "address"
        }, {
          "internalType": "bytes32",
          "name": "guid",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "value",
          "type": "uint256"
        }, {
          "internalType": "address",
          "name": "executor",
          "type": "address"
        }, {
          "internalType": "bytes",
          "name": "message",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "extraData",
          "type": "bytes"
        }],
        "internalType": "struct InboundPacket[]",
        "name": "_packets",
        "type": "tuple[]"
      }],
      "name": "lzReceiveAndRevert",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "srcEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "sender",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }],
        "internalType": "struct Origin",
        "name": "_origin",
        "type": "tuple"
      }, {
        "internalType": "bytes32",
        "name": "_guid",
        "type": "bytes32"
      }, {
        "internalType": "bytes",
        "name": "_message",
        "type": "bytes"
      }, {
        "internalType": "address",
        "name": "_executor",
        "type": "address"
      }, {
        "internalType": "bytes",
        "name": "_extraData",
        "type": "bytes"
      }],
      "name": "lzReceiveSimulate",
      "outputs": [],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [],
      "name": "msgInspector",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "",
        "type": "bytes32"
      }],
      "name": "nextNonce",
      "outputs": [{
        "internalType": "uint64",
        "name": "nonce",
        "type": "uint64"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oApp",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oAppVersion",
      "outputs": [{
        "internalType": "uint64",
        "name": "senderVersion",
        "type": "uint64"
      }, {
        "internalType": "uint64",
        "name": "receiverVersion",
        "type": "uint64"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [],
      "name": "oftVersion",
      "outputs": [{
        "internalType": "bytes4",
        "name": "interfaceId",
        "type": "bytes4"
      }, {
        "internalType": "uint64",
        "name": "version",
        "type": "uint64"
      }],
      "stateMutability": "pure",
      "type": "function"
    }, {
      "inputs": [],
      "name": "owner",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "eid",
        "type": "uint32"
      }],
      "name": "peers",
      "outputs": [{
        "internalType": "bytes32",
        "name": "peer",
        "type": "bytes32"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "preCrime",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "to",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "amountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "bytes",
          "name": "extraOptions",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "composeMsg",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "oftCmd",
          "type": "bytes"
        }],
        "internalType": "struct SendParam",
        "name": "_sendParam",
        "type": "tuple"
      }],
      "name": "quoteOFT",
      "outputs": [{
        "components": [{
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "maxAmountLD",
          "type": "uint256"
        }],
        "internalType": "struct OFTLimit",
        "name": "oftLimit",
        "type": "tuple"
      }, {
        "components": [{
          "internalType": "int256",
          "name": "feeAmountLD",
          "type": "int256"
        }, {
          "internalType": "string",
          "name": "description",
          "type": "string"
        }],
        "internalType": "struct OFTFeeDetail[]",
        "name": "oftFeeDetails",
        "type": "tuple[]"
      }, {
        "components": [{
          "internalType": "uint256",
          "name": "amountSentLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "amountReceivedLD",
          "type": "uint256"
        }],
        "internalType": "struct OFTReceipt",
        "name": "oftReceipt",
        "type": "tuple"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "to",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "amountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "bytes",
          "name": "extraOptions",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "composeMsg",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "oftCmd",
          "type": "bytes"
        }],
        "internalType": "struct SendParam",
        "name": "_sendParam",
        "type": "tuple"
      }, {
        "internalType": "bool",
        "name": "_payInLzToken",
        "type": "bool"
      }],
      "name": "quoteSend",
      "outputs": [{
        "components": [{
          "internalType": "uint256",
          "name": "nativeFee",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "lzTokenFee",
          "type": "uint256"
        }],
        "internalType": "struct MessagingFee",
        "name": "msgFee",
        "type": "tuple"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "renounceOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "dstEid",
          "type": "uint32"
        }, {
          "internalType": "bytes32",
          "name": "to",
          "type": "bytes32"
        }, {
          "internalType": "uint256",
          "name": "amountLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "minAmountLD",
          "type": "uint256"
        }, {
          "internalType": "bytes",
          "name": "extraOptions",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "composeMsg",
          "type": "bytes"
        }, {
          "internalType": "bytes",
          "name": "oftCmd",
          "type": "bytes"
        }],
        "internalType": "struct SendParam",
        "name": "_sendParam",
        "type": "tuple"
      }, {
        "components": [{
          "internalType": "uint256",
          "name": "nativeFee",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "lzTokenFee",
          "type": "uint256"
        }],
        "internalType": "struct MessagingFee",
        "name": "_fee",
        "type": "tuple"
      }, {
        "internalType": "address",
        "name": "_refundAddress",
        "type": "address"
      }],
      "name": "send",
      "outputs": [{
        "components": [{
          "internalType": "bytes32",
          "name": "guid",
          "type": "bytes32"
        }, {
          "internalType": "uint64",
          "name": "nonce",
          "type": "uint64"
        }, {
          "components": [{
            "internalType": "uint256",
            "name": "nativeFee",
            "type": "uint256"
          }, {
            "internalType": "uint256",
            "name": "lzTokenFee",
            "type": "uint256"
          }],
          "internalType": "struct MessagingFee",
          "name": "fee",
          "type": "tuple"
        }],
        "internalType": "struct MessagingReceipt",
        "name": "msgReceipt",
        "type": "tuple"
      }, {
        "components": [{
          "internalType": "uint256",
          "name": "amountSentLD",
          "type": "uint256"
        }, {
          "internalType": "uint256",
          "name": "amountReceivedLD",
          "type": "uint256"
        }],
        "internalType": "struct OFTReceipt",
        "name": "oftReceipt",
        "type": "tuple"
      }],
      "stateMutability": "payable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_delegate",
        "type": "address"
      }],
      "name": "setDelegate",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "components": [{
          "internalType": "uint32",
          "name": "eid",
          "type": "uint32"
        }, {
          "internalType": "uint16",
          "name": "msgType",
          "type": "uint16"
        }, {
          "internalType": "bytes",
          "name": "options",
          "type": "bytes"
        }],
        "internalType": "struct EnforcedOptionParam[]",
        "name": "_enforcedOptions",
        "type": "tuple[]"
      }],
      "name": "setEnforcedOptions",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_msgInspector",
        "type": "address"
      }],
      "name": "setMsgInspector",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_eid",
        "type": "uint32"
      }, {
        "internalType": "bytes32",
        "name": "_peer",
        "type": "bytes32"
      }],
      "name": "setPeer",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "_preCrime",
        "type": "address"
      }],
      "name": "setPreCrime",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }, {
      "inputs": [],
      "name": "sharedDecimals",
      "outputs": [{
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [],
      "name": "token",
      "outputs": [{
        "internalType": "address",
        "name": "",
        "type": "address"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }],
      "name": "transferOwnership",
      "outputs": [],
      "stateMutability": "nonpayable",
      "type": "function"
    }],
    Executor: [{
      "inputs": [{
        "internalType": "uint32",
        "name": "_dstEid",
        "type": "uint32"
      }, {
        "internalType": "address",
        "name": "_sender",
        "type": "address"
      }, {
        "internalType": "uint256",
        "name": "_calldataSize",
        "type": "uint256"
      }, {
        "internalType": "bytes",
        "name": "_options",
        "type": "bytes"
      }],
      "name": "getFee",
      "outputs": [{
        "internalType": "uint256",
        "name": "fee",
        "type": "uint256"
      }],
      "stateMutability": "view",
      "type": "function"
    }, {
      "inputs": [{
        "internalType": "uint32",
        "name": "_dstEid",
        "type": "uint32"
      }],
      "name": "dstConfig",
      "outputs": [{
        "components": [{
          "internalType": "uint64",
          "name": "baseGas",
          "type": "uint64"
        }, {
          "internalType": "uint16",
          "name": "multiplierBps",
          "type": "uint16"
        }, {
          "internalType": "uint128",
          "name": "floorMarginUSD",
          "type": "uint128"
        }, {
          "internalType": "uint128",
          "name": "nativeCap",
          "type": "uint128"
        }],
        "internalType": "struct IExecutor.DstConfig",
        "name": "",
        "type": "tuple"
      }],
      "stateMutability": "view",
      "type": "function"
    }]
  };
  const EMBEDDED_CHAINS = [{
    chainId: 1,
    name: 'Ethereum',
    chainKey: 'ethereum',
    rpc: 'https://eth.llamarpc.com',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 56,
    name: 'BNB Chain',
    chainKey: 'bsc',
    rpc: 'https://bsc-dataseed.binance.org',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 137,
    name: 'Polygon',
    chainKey: 'polygon',
    rpc: 'https://polygon-rpc.com',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 42161,
    name: 'Arbitrum One',
    chainKey: 'arbitrum',
    rpc: 'https://arb1.arbitrum.io/rpc',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 10,
    name: 'Optimism',
    chainKey: 'optimism',
    rpc: 'https://mainnet.optimism.io',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 8453,
    name: 'Base',
    chainKey: 'base',
    rpc: 'https://mainnet.base.org',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 43114,
    name: 'Avalanche',
    chainKey: 'avalanche',
    rpc: 'https://api.avax.network/ext/bc/C/rpc',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 250,
    name: 'Fantom',
    chainKey: 'fantom',
    rpc: 'https://rpc.ftm.tools',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 324,
    name: 'zkSync Era',
    chainKey: 'zksync',
    rpc: 'https://mainnet.era.zksync.io',
    endpointV2: '0xd07C30aF3Ff30D96BDc9c6044958230Eb797DDBF'
  }, {
    chainId: 59144,
    name: 'Linea',
    chainKey: 'linea',
    rpc: 'https://rpc.linea.build',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 5000,
    name: 'Mantle',
    chainKey: 'mantle',
    rpc: 'https://rpc.mantle.xyz',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 534352,
    name: 'Scroll',
    chainKey: 'scroll',
    rpc: 'https://rpc.scroll.io',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 1101,
    name: 'Polygon zkEVM',
    chainKey: 'polygon-zkevm',
    rpc: 'https://zkevm-rpc.com',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 81457,
    name: 'Blast',
    chainKey: 'blast',
    rpc: 'https://rpc.blast.io',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }, {
    chainId: 34443,
    name: 'Mode',
    chainKey: 'mode',
    rpc: 'https://mainnet.mode.network',
    endpointV2: '0x1a44076050125825900e736c501f859c50fE728c'
  }];
  const contractDeployments = {
    OApp: [{
      chainId: 1,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 10,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 56,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 137,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 8453,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 42161,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 43114,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }],
    OAppRead: [],
    OFT: [{
      chainId: 1,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 10,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 56,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 137,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 8453,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 42161,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }, {
      chainId: 43114,
      address: '0x6985884c4392d348587b19cb9eaaf157f13271cd',
      name: 'ZRO Token'
    }],
    OFTAdapter: [],
    EndpointV2: [{
      chainId: 1,
      address: '0x1a44076050125825900e736c501f859c50fE728c',
      name: 'Endpoint V2'
    }, {
      chainId: 56,
      address: '0x1a44076050125825900e736c501f859c50fE728c',
      name: 'Endpoint V2'
    }, {
      chainId: 42161,
      address: '0x1a44076050125825900e736c501f859c50fE728c',
      name: 'Endpoint V2'
    }, {
      chainId: 10,
      address: '0x1a44076050125825900e736c501f859c50fE728c',
      name: 'Endpoint V2'
    }, {
      chainId: 137,
      address: '0x1a44076050125825900e736c501f859c50fE728c',
      name: 'Endpoint V2'
    }, {
      chainId: 8453,
      address: '0x1a44076050125825900e736c501f859c50fE728c',
      name: 'Endpoint V2'
    }],
    Executor: [{
      chainId: 1,
      address: '0x173272739Bd7Aa6e4e214714048a9fE699453059',
      name: 'LZ Executor'
    }, {
      chainId: 56,
      address: '0x173272739Bd7Aa6e4e214714048a9fE699453059',
      name: 'LZ Executor'
    }, {
      chainId: 42161,
      address: '0x173272739Bd7Aa6e4e214714048a9fE699453059',
      name: 'LZ Executor'
    }, {
      chainId: 10,
      address: '0x173272739Bd7Aa6e4e214714048a9fE699453059',
      name: 'LZ Executor'
    }, {
      chainId: 137,
      address: '0x173272739Bd7Aa6e4e214714048a9fE699453059',
      name: 'LZ Executor'
    }, {
      chainId: 8453,
      address: '0x173272739Bd7Aa6e4e214714048a9fE699453059',
      name: 'LZ Executor'
    }]
  };
  const getPlaceholderForType = type => {
    if (type.includes('address')) return '0x...';
    if (type.includes('uint')) return '0';
    if (type.includes('int')) return '0';
    if (type.includes('bool')) return 'true or false';
    if (type.includes('bytes32')) return '0x... (32 bytes)';
    if (type.includes('bytes')) return '0x...';
    if (type.includes('string')) return 'text';
    return 'value';
  };
  const [isDark, setIsDark] = useState(true);
  const [selectedChain, setSelectedChain] = useState(disableChainSelection ? null : 1);
  const [contract, setContract] = useState(null);
  const [inputs, setInputs] = useState({});
  const [result, setResult] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [walletConnected, setWalletConnected] = useState(false);
  const [isExpanded, setIsExpanded] = useState(false);
  const [isChainDropdownOpen, setIsChainDropdownOpen] = useState(false);
  const [chainSearchTerm, setChainSearchTerm] = useState('');
  const [customAddress, setCustomAddress] = useState('');
  const [isTruncated, setIsTruncated] = useState(false);
  const [ethersLib, setEthersLib] = useState(null);
  const [isLoadingData, setIsLoadingData] = useState(false);
  const [iconErrors, setIconErrors] = useState({});
  const [loadedAbi, setLoadedAbi] = useState(null);
  const [signer, setSigner] = useState(null);
  const abi = abiProp || loadedAbi;
  const chainDropdownRef = useRef(null);
  const codeRef = useRef(null);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const checkTheme = () => setIsDark(document.documentElement.classList.contains('dark'));
    checkTheme();
    const observer = new MutationObserver(checkTheme);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  useEffect(() => {
    if (typeof window === 'undefined') return;
    if (window.ethers) {
      setEthersLib(window.ethers);
      return;
    }
    const script = document.createElement('script');
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/ethers/5.7.2/ethers.umd.min.js';
    script.async = true;
    script.onload = () => {
      if (window.ethers) {
        setEthersLib(window.ethers);
      }
    };
    document.head.appendChild(script);
  }, []);
  useEffect(() => {
    if (abiProp) return;
    const embeddedAbi = EMBEDDED_ABIS[contractName];
    if (embeddedAbi) {
      setLoadedAbi(embeddedAbi);
    } else {
      setError('No ABI available for ' + contractName);
    }
  }, [contractName, abiProp]);
  const availableChains = useMemo(() => {
    if (disableChainSelection) return [];
    return EMBEDDED_CHAINS.slice().sort((a, b) => a.name.localeCompare(b.name));
  }, [disableChainSelection]);
  const currentChain = availableChains.find(chain => chain.chainId === selectedChain);
  const chainDisplayName = currentChain?.name || 'Select Chain';
  const chainKey = currentChain?.chainKey || 'default';
  const functionAbi = abi?.find(item => item.type === 'function' && item.name === functionName);
  const getDeploymentAddress = () => {
    if (contractName === 'EndpointV2') {
      const chain = EMBEDDED_CHAINS.find(c => c.chainId === selectedChain);
      return chain?.endpointV2;
    }
    const deployments = contractDeployments[contractName] || [];
    const dep = deployments.find(d => d.chainId === selectedChain);
    return dep?.address;
  };
  const defaultDeploymentAddress = getDeploymentAddress();
  const deploymentAddress = defaultDeploymentAddress || customAddress;
  useEffect(() => {
    if (availableChains.length > 0 && !currentChain) {
      setSelectedChain(availableChains[0].chainId);
    }
  }, [availableChains, currentChain]);
  useEffect(() => {
    if (defaultDeploymentAddress) setCustomAddress('');
  }, [selectedChain, defaultDeploymentAddress]);
  useEffect(() => {
    const checkTruncation = () => {
      if (codeRef.current) {
        setIsTruncated(codeRef.current.scrollWidth > codeRef.current.clientWidth);
      }
    };
    checkTruncation();
    const timeout = setTimeout(checkTruncation, 100);
    return () => clearTimeout(timeout);
  }, [functionAbi, isExpanded]);
  const isReadOnly = functionAbi && (functionAbi.stateMutability === 'view' || functionAbi.stateMutability === 'pure');
  const NetworkIcon = ({chainKey, size = 20}) => {
    const hasError = iconErrors['network-' + chainKey];
    const iconUrl = hasError ? 'https://icons-ckg.pages.dev/lz-scan/networks/default.svg' : 'https://icons-ckg.pages.dev/lz-scan/networks/' + chainKey + '.svg';
    return <span style={{
      display: 'inline-block',
      width: size,
      height: size,
      flexShrink: 0,
      backgroundImage: 'url(' + iconUrl + ')',
      backgroundSize: 'contain',
      backgroundRepeat: 'no-repeat',
      backgroundPosition: 'center',
      borderRadius: 4
    }} className={isDark ? '' : 'invert brightness-110 contrast-100'} />;
  };
  const connectWallet = async () => {
    if (!ethersLib) {
      setError('Ethers library not loaded yet');
      return;
    }
    try {
      if (!window.ethereum) {
        setError('Please install MetaMask or another Web3 wallet');
        return;
      }
      const accounts = await window.ethereum.request({
        method: 'eth_requestAccounts'
      });
      if (accounts.length > 0) {
        try {
          await window.ethereum.request({
            method: 'wallet_switchEthereumChain',
            params: [{
              chainId: '0x' + selectedChain.toString(16)
            }]
          });
        } catch (switchError) {
          if (switchError.code === 4902 && currentChain) {
            const chainRpcData = chainlistRPCs.find(c => c.chainId === selectedChain);
            await window.ethereum.request({
              method: 'wallet_addEthereumChain',
              params: [{
                chainId: '0x' + selectedChain.toString(16),
                chainName: currentChain.name,
                rpcUrls: [currentChain.rpc],
                nativeCurrency: chainRpcData?.nativeCurrency,
                blockExplorerUrls: chainRpcData?.explorers?.map(e => e.url) || []
              }]
            });
          }
        }
        const web3Provider = new ethersLib.providers.Web3Provider(window.ethereum);
        const web3Signer = web3Provider.getSigner();
        setSigner(web3Signer);
        setWalletConnected(true);
        setError(null);
      }
    } catch (err) {
      setError(err.message);
    }
  };
  useEffect(() => {
    if (!ethersLib || !deploymentAddress || !currentChain || !currentChain.rpc || !abi) return;
    try {
      if (signer) {
        const contractInstance = new ethersLib.Contract(deploymentAddress, abi, signer);
        setContract(contractInstance);
      } else if (isReadOnly) {
        const readProvider = new ethersLib.providers.JsonRpcProvider(currentChain.rpc);
        const contractInstance = new ethersLib.Contract(deploymentAddress, abi, readProvider);
        setContract(contractInstance);
      }
    } catch (err) {
      console.error('Error initializing contract:', err);
    }
  }, [ethersLib, signer, deploymentAddress, abi, currentChain, isReadOnly]);
  useEffect(() => {
    const handleClickOutside = event => {
      if (chainDropdownRef.current && !chainDropdownRef.current.contains(event.target)) {
        setIsChainDropdownOpen(false);
        setChainSearchTerm('');
      }
    };
    if (isChainDropdownOpen) {
      document.addEventListener('mousedown', handleClickOutside);
    }
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, [isChainDropdownOpen]);
  const handleInputChange = (paramPath, value) => {
    setInputs(prev => ({
      ...prev,
      [paramPath]: value
    }));
  };
  const parseValue = (value, type) => {
    const val = (value || '').trim();
    if (type.endsWith('[]')) {
      if (!val || val === '[]') return [];
      try {
        return JSON.parse(val);
      } catch {
        return val.split(',').map(v => parseValue(v.trim(), type.slice(0, -2)));
      }
    }
    if (type === 'bool') {
      return val.toLowerCase() === 'true' || val === '1';
    }
    if (type.startsWith('uint') || type.startsWith('int')) {
      return val || '0';
    }
    if (type === 'address') {
      return val || '0x0000000000000000000000000000000000000000';
    }
    if (type === 'bytes32') {
      return val || '0x0000000000000000000000000000000000000000000000000000000000000000';
    }
    if (type.match(/^bytes\d+$/)) {
      const size = parseInt(type.slice(5));
      return val || '0x' + ('00').repeat(size);
    }
    if (type === 'bytes') {
      return val || '0x';
    }
    return val;
  };
  const prepareArguments = () => {
    const args = [];
    functionAbi.inputs.forEach((input, index) => {
      if (input.type.startsWith('tuple')) {
        const tupleValues = [];
        if (input.components) {
          input.components.forEach((component, compIndex) => {
            const path = (input.name || index) + '.' + (component.name || compIndex);
            tupleValues.push(parseValue(inputs[path], component.type));
          });
        }
        args.push(tupleValues);
      } else {
        const path = input.name || index.toString();
        args.push(parseValue(inputs[path], input.type));
      }
    });
    return args;
  };
  const executeFunction = async () => {
    if (!functionAbi) return;
    if (!isReadOnly && !walletConnected) {
      setError('Please connect your wallet to send transactions');
      return;
    }
    if (!contract) {
      setError('Contract not initialized. Please check the network connection.');
      return;
    }
    setLoading(true);
    setError(null);
    setResult(null);
    try {
      const args = prepareArguments();
      let tx;
      if (isReadOnly) {
        tx = await contract[functionName](...args);
        setResult({
          type: 'read',
          data: tx,
          formatted: formatResult(tx)
        });
      } else {
        tx = await contract[functionName](...args);
        setResult({
          type: 'write',
          hash: tx.hash,
          status: 'pending'
        });
        const receipt = await tx.wait();
        setResult({
          type: 'write',
          hash: tx.hash,
          status: 'confirmed',
          blockNumber: receipt.blockNumber
        });
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  const formatResult = data => {
    if (data === null || data === undefined) return 'null';
    if (typeof data === 'object' && data._isBigNumber) return data.toString();
    if (Array.isArray(data)) return data.map(formatResult);
    if (typeof data === 'object') {
      const result = {};
      Object.keys(data).forEach(key => {
        if (isNaN(parseInt(key))) result[key] = formatResult(data[key]);
      });
      return result;
    }
    return data;
  };
  const renderOutputType = (output, depth = 0) => {
    if (output.type.startsWith('tuple') && output.components) {
      return <div key={output.name || 'tuple-' + depth} style={{
        marginLeft: depth * 16
      }}>
          <span className={isDark ? 'text-purple-400' : 'text-purple-600'}>
            {output.name && output.name + ': '}{'struct {'}
          </span>
          {output.components.map((comp, idx) => renderOutputType(comp, depth + 1))}
          <div style={{
        marginLeft: depth * 16
      }}>
            <span className={isDark ? 'text-purple-400' : 'text-purple-600'}>{'}'}</span>
            {output.type.endsWith('[]') && <span className={isDark ? 'text-zinc-400' : 'text-zinc-500'}>[]</span>}
          </div>
        </div>;
    }
    return <div key={output.name || 'field-' + depth} style={{
      marginLeft: depth * 16
    }}>
        {output.name && <span className={isDark ? 'text-zinc-300' : 'text-zinc-600'}>{output.name}: </span>}
        <span style={{
      color: isDark ? '#f1df38' : '#e36209'
    }}>{output.type}</span>
      </div>;
  };
  if (typeof window === 'undefined') {
    return <div className="p-4 text-center text-zinc-500">Loading...</div>;
  }
  if (error && !abi) {
    return <div className="p-4 rounded-lg border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 mb-4">
        <div className="font-medium mb-1">Error loading {contractName}</div>
        <div className="text-sm">{error}</div>
      </div>;
  }
  if (isLoadingData) {
    return <div className="p-4 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 mb-4">
        <div className="text-center text-zinc-500">Loading chain data...</div>
      </div>;
  }
  if (!abi) {
    return <div className="p-4 rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 mb-4">
        <div className="text-center text-zinc-500">Loading {contractName} ABI...</div>
      </div>;
  }
  if (!functionAbi) {
    return <div className="p-4 rounded-lg border border-red-200 dark:border-red-800 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 mb-4">
        Function {functionName} not found in {contractName} ABI
      </div>;
  }
  const renderInputFields = () => {
    const fields = [];
    functionAbi.inputs.forEach((input, index) => {
      if (input.type.startsWith('tuple') && input.components) {
        fields.push(<div key={'tuple-' + index} className="mb-4">
            <div className={'text-xs font-semibold uppercase tracking-wide mb-2 ' + (isDark ? 'text-purple-400' : 'text-purple-600')}>
              {input.name || 'Param ' + index} (tuple)
            </div>
            <div className={'pl-4 border-l-2 ' + (isDark ? 'border-purple-500/30' : 'border-purple-300')}>
              {input.components.map((component, compIndex) => {
          const path = (input.name || index) + '.' + (component.name || compIndex);
          return <div key={path} className="mb-3">
                    <label className={'flex items-center gap-2 text-sm mb-1 ' + (isDark ? 'text-zinc-300' : 'text-zinc-700')}>
                      {component.name || 'field' + compIndex}
                      <span className={'text-xs ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>({component.type})</span>
                    </label>
                    <input type="text" placeholder={getPlaceholderForType(component.type)} value={inputs[path] || ''} onChange={e => handleInputChange(path, e.target.value)} className={'w-full px-3 py-2 rounded-lg border text-sm font-mono focus:outline-none focus:ring-1 focus:ring-blue-500 ' + (isDark ? 'bg-zinc-800 border-zinc-700 text-white placeholder-zinc-500' : 'bg-white border-zinc-300 text-zinc-900 placeholder-zinc-400')} />
                  </div>;
        })}
            </div>
          </div>);
      } else {
        const path = input.name || index.toString();
        fields.push(<div key={path} className="mb-3">
            <label className={'flex items-center gap-2 text-sm mb-1 ' + (isDark ? 'text-zinc-300' : 'text-zinc-700')}>
              {input.name || 'param' + index}
              <span className={'text-xs ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>({input.type})</span>
            </label>
            <input type="text" placeholder={getPlaceholderForType(input.type)} value={inputs[path] || ''} onChange={e => handleInputChange(path, e.target.value)} className={'w-full px-3 py-2 rounded-lg border text-sm font-mono focus:outline-none focus:ring-1 focus:ring-blue-500 ' + (isDark ? 'bg-zinc-800 border-zinc-700 text-white placeholder-zinc-500' : 'bg-white border-zinc-300 text-zinc-900 placeholder-zinc-400')} />
          </div>);
      }
    });
    return fields;
  };
  return <div className="rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 overflow-hidden mb-4">
      {}
      <div className={'flex items-center justify-between p-3 cursor-pointer transition-colors ' + (isDark ? 'bg-zinc-800 hover:bg-zinc-700' : 'bg-zinc-100 hover:bg-zinc-200')} onClick={() => setIsExpanded(!isExpanded)}>
        <div className="flex items-center gap-3 flex-1 min-w-0">
          <span className="px-2 py-1 rounded text-xs font-semibold text-white" style={{
    backgroundColor: isReadOnly ? '#6366f1' : '#22c55e'
  }}>
            {isReadOnly ? 'CALL' : 'SEND'}
          </span>
          <code ref={codeRef} className={'text-sm truncate ' + (isDark ? 'text-zinc-200' : 'text-zinc-800')} title={functionName + '(' + functionAbi.inputs.map(i => i.name + ': ' + i.type).join(', ') + ')'}>
            <span style={{
    color: '#6cadf5'
  }}>{functionName}</span>
            <span className={isDark ? 'text-zinc-500' : 'text-zinc-400'}>(</span>
            {functionAbi.inputs.map((input, idx) => <span key={idx}>
                <span className={isDark ? 'text-zinc-300' : 'text-zinc-600'}>{input.name}</span>
                <span className={isDark ? 'text-zinc-500' : 'text-zinc-400'}>: </span>
                <span style={{
    color: isDark ? '#f1df38' : '#e36209'
  }}>{input.type}</span>
                {idx < functionAbi.inputs.length - 1 && <span className={isDark ? 'text-zinc-500' : 'text-zinc-400'}>, </span>}
              </span>)}
            <span className={isDark ? 'text-zinc-500' : 'text-zinc-400'}>)</span>
          </code>
        </div>
        <div className={'transition-transform ' + (isExpanded ? 'rotate-180' : '')} style={{
    color: isDark ? '#71717a' : '#a1a1aa'
  }}>
          <svg viewBox="0 0 24 24" fill="none" width="20" height="20">
            <path d="M6 9L12 15L18 9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </div>
      </div>

      {}
      {isExpanded && <div className="p-4">
          {description && <div className={'text-sm mb-4 pb-4 border-b ' + (isDark ? 'text-zinc-400 border-zinc-800' : 'text-zinc-600 border-zinc-200')}>
              {description}
            </div>}

          {}
          <div className="mb-4">
            <label className={'text-xs font-semibold uppercase tracking-wide mb-2 block ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>CHAIN</label>
            <div ref={chainDropdownRef} className="relative">
              <div onClick={() => setIsChainDropdownOpen(!isChainDropdownOpen)} className={'flex items-center justify-between px-3 py-2 rounded-lg border cursor-pointer ' + (isDark ? 'bg-zinc-800 border-zinc-700 hover:border-zinc-600' : 'bg-white border-zinc-300 hover:border-zinc-400')}>
                <div className="flex items-center gap-2">
                  {currentChain && <NetworkIcon chainKey={chainKey} size={20} />}
                  <span className={isDark ? 'text-white' : 'text-zinc-900'}>{chainDisplayName}</span>
                </div>
                <svg width="10" height="6" viewBox="0 0 10 6" fill="none" className={isDark ? 'text-zinc-400' : 'text-zinc-500'}>
                  <path d="M1 1L5 5L9 1" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
                </svg>
              </div>

              {isChainDropdownOpen && <div className="absolute z-50 w-full mt-1 rounded-lg border shadow-lg" style={{
    backgroundColor: isDark ? '#27272a' : '#ffffff',
    borderColor: isDark ? '#3f3f46' : '#d4d4d8'
  }}>
                  <input type="text" placeholder="Search chains..." value={chainSearchTerm} onChange={e => setChainSearchTerm(e.target.value)} onClick={e => e.stopPropagation()} autoFocus className={'w-full px-3 py-2 text-sm border-b focus:outline-none ' + (isDark ? 'bg-transparent border-zinc-700 text-white placeholder-zinc-500' : 'bg-transparent border-zinc-200 text-zinc-900 placeholder-zinc-400')} />
                  <div style={{
    maxHeight: '240px',
    overflowY: 'auto',
    backgroundColor: isDark ? '#27272a' : '#ffffff'
  }}>
                    {availableChains.length === 0 ? <div className={'px-3 py-4 text-sm text-center ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>
                        No chains available
                      </div> : availableChains.filter(chain => chain.name.toLowerCase().includes(chainSearchTerm.toLowerCase())).map(chain => <div key={chain.chainId} onClick={() => {
    setSelectedChain(chain.chainId);
    setIsChainDropdownOpen(false);
    setChainSearchTerm('');
  }} className={'flex items-center gap-2 px-3 py-2 cursor-pointer ' + (selectedChain === chain.chainId ? isDark ? 'bg-blue-600/20 text-blue-400' : 'bg-blue-50 text-blue-600' : isDark ? 'hover:bg-zinc-700 text-white' : 'hover:bg-zinc-100 text-zinc-900')}>
                            <NetworkIcon chainKey={chain.chainKey} size={16} />
                            <span className="text-sm">{chain.name}</span>
                          </div>)}
                  </div>
                </div>}
            </div>
          </div>

          {}
          {showDeployments && <div className="mb-4">
              <label className={'text-xs font-semibold uppercase tracking-wide mb-2 block ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>
                {defaultDeploymentAddress ? 'CONTRACT' : 'CONTRACT ADDRESS'}
              </label>
              {defaultDeploymentAddress ? <div className={'px-3 py-2 rounded-lg text-sm font-mono ' + (isDark ? 'bg-zinc-800 text-zinc-300' : 'bg-zinc-100 text-zinc-700')}>
                  {defaultDeploymentAddress}
                </div> : <>
                  <input type="text" placeholder="0x..." value={customAddress} onChange={e => setCustomAddress(e.target.value)} className={'w-full px-3 py-2 rounded-lg border text-sm font-mono focus:outline-none focus:ring-1 focus:ring-blue-500 ' + (isDark ? 'bg-zinc-800 border-zinc-700 text-white placeholder-zinc-500' : 'bg-white border-zinc-300 text-zinc-900 placeholder-zinc-400')} />
                  <div className={'text-xs mt-1 ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>
                    Enter your {contractName} contract address
                  </div>
                </>}
            </div>}

          {}
          {functionAbi.inputs.length > 0 && <div className="mb-4">
              <div className={'text-xs font-semibold uppercase tracking-wide mb-3 ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>PARAMETERS</div>
              {renderInputFields()}
            </div>}

          {}
          {functionAbi.outputs && functionAbi.outputs.length > 0 && <div className="mb-4">
              <div className={'text-xs font-semibold uppercase tracking-wide mb-2 ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>RETURNS</div>
              <div className={'p-3 rounded-lg text-sm font-mono ' + (isDark ? 'bg-zinc-800' : 'bg-zinc-100')}>
                {functionAbi.outputs.map((output, idx) => renderOutputType(output, 0))}
              </div>
            </div>}

          {}
          <div className="mb-4">
            {!walletConnected && !isReadOnly ? <button onClick={connectWallet} className="w-full px-4 py-2 rounded-lg font-medium text-sm bg-blue-600 hover:bg-blue-700 text-white transition-colors">
                Connect Wallet
              </button> : <button onClick={executeFunction} disabled={loading || !deploymentAddress} className="w-full px-4 py-2 rounded-lg font-medium text-sm transition-colors text-white disabled:opacity-50 disabled:cursor-not-allowed" style={{
    backgroundColor: loading || !deploymentAddress ? '#52525b' : '#6cadf5'
  }}>
                {loading ? 'Processing...' : isReadOnly ? 'Send Request' : 'Send Transaction'}
              </button>}
          </div>

          {}
          {error && <div className={'p-3 rounded-lg text-sm mb-4 ' + (isDark ? 'bg-red-900/20 text-red-400' : 'bg-red-50 text-red-600')}>
              {error}
            </div>}

          {}
          {result && <div className="mb-4">
              <div className={'text-xs font-semibold uppercase tracking-wide mb-2 ' + (isDark ? 'text-zinc-500' : 'text-zinc-400')}>RESPONSE</div>
              <div className={'p-3 rounded-lg text-sm font-mono overflow-auto ' + (isDark ? 'bg-zinc-800' : 'bg-zinc-100')}>
                {result.type === 'read' ? <pre style={{
    margin: 0,
    whiteSpace: 'pre-wrap',
    color: isDark ? '#9EFC7E' : '#166534'
  }}>
                    {JSON.stringify(result.formatted, null, 2)}
                  </pre> : <div className={isDark ? 'text-zinc-300' : 'text-zinc-700'}>
                    <div>Tx: {result.hash}</div>
                    <div>Status: <span className={result.status === 'confirmed' ? 'text-green-500' : 'text-yellow-500'}>{result.status}</span></div>
                    {result.blockNumber && <div>Block: {result.blockNumber}</div>}
                  </div>}
              </div>
            </div>}

          {}
          {!deploymentAddress && <div className={'p-3 rounded-lg text-sm ' + (isDark ? 'bg-yellow-900/20 text-yellow-400' : 'bg-yellow-50 text-yellow-700')}>
              No deployment found for {contractName} on {chainDisplayName}
            </div>}
        </div>}
    </div>;
};

Create an **omnichain ERC-4626 vault** that enable users to deposit assets or redeem shares on any blockchain network through a single transaction.

<img src="https://mintcdn.com/layerzero/VohHXGobQ14zkw24/images/ovault-light-comparison.svg?fit=max&auto=format&n=VohHXGobQ14zkw24&q=85&s=c2d94ff0e98039d1dc65db828d3ef31b" alt="Architecture diagram comparing traditional ERC-4626 vault with OVault, showing how OVault enables crosschain deposits and redemptions through OFT assets, OFTAdapter for shares, and VaultComposerSync for orchestration" className="block dark:hidden" width="1920" height="907" data-path="images/ovault-light-comparison.svg" />

<img src="https://mintcdn.com/layerzero/VohHXGobQ14zkw24/images/ovault-dark-comparison.svg?fit=max&auto=format&n=VohHXGobQ14zkw24&q=85&s=019edf19377a652f9947204806fdf4d7" alt="Architecture diagram comparing traditional ERC-4626 vault with OVault, showing how OVault enables crosschain deposits and redemptions through OFT assets, OFTAdapter for shares, and VaultComposerSync for orchestration" className="hidden dark:block" width="1920" height="907" data-path="images/ovault-dark-comparison.svg" />

## Prerequisites

Before implementing OVault, you should understand:

1. [OFT Standard](../oft/quickstart): How **Omnichain Fungible Tokens** work and what the typical deployment looks like
2. [Composer Pattern](../composer/overview): Understanding of `composeMsg` encoding and crosschain message workflows
3. [ERC-4626 Vaults](https://eips.ethereum.org/EIPS/eip-4626): How the tokenized vault standard interface works for `deposit`/`redeem` operations

An **Omnichain Vault (OVault)** takes a new or existing ERC4626 vault, and connects the underlying asset or share to many blockchain networks using the **Omnichain Fungible Token (OFT) standard**.

An OVault implementation requires **five** core contracts to be deployed:

* an `OFT` asset
* an `ERC4626` vault
* an `OFTAdapter` to transform the vault's share into an omnichain token
* a `VaultComposerSync` to orchestrate omnichain deposits and redemptions between the asset and share
* an `OFT` to represent the shares on spoke chains

You can review the implementation of these contracts under [Contracts Overview](#contracts-overview).

## Step 1. Project Installation

To start using LayerZero OVault contracts in a new project, use the LayerZero CLI tool, [**create-lz-oapp**](../../../get-started/create-lz-oapp/start). The CLI tool allows developers to create any omnichain application in \<4 minutes! Get started by running the following from your command line:

```bash wrap theme={null}
LZ_ENABLE_OVAULT_EXAMPLE=1 npx create-lz-oapp@latest --example ovault-evm
```

After running, select the directory for the scaffold project to be cloned into:

```bash wrap theme={null}
✔ Where do you want to start your project? … ./<YOUR_DIRECTORY>
? Which example would you like to use as a starting point? › - Use arrow-keys. Return to submit.
❯   OVault EVM
    OApp
    OFT
    OFTAdapter
    ONFT721
```

After the installer completes, copy and paste the `.env.example` in the project root, add your `PRIVATE_KEY`, `RPC_URL` you will be working with, and rename the file to `.env`:

```bash wrap theme={null}
cp .env.example .env
```

You can find the sample codebase in [devtools/examples/ovault-evm](https://github.com/LayerZero-Labs/devtools/tree/main/examples/ovault-evm).

## Step 2. Network Configuration

Update `hardhat.config.ts` to include your desired networks. Modify your `.env` file or the URL directly to change network RPCs:

```typescript wrap theme={null}
const config: HardhatUserConfig = {
  networks: {
    base: {
      eid: EndpointId.BASESEP_V2_TESTNET,
      url: process.env.RPC_URL_BASESEP_TESTNET || 'https://base-sepolia.gateway.tenderly.co',
      accounts,
    },
    arbitrum: {
      eid: EndpointId.ARBSEP_V2_TESTNET,
      url: process.env.RPC_URL_ARBSEP_TESTNET || 'https://arbitrum-sepolia.gateway.tenderly.co',
      accounts,
    },
    optimism: {
      eid: EndpointId.OPTSEP_V2_TESTNET,
      url: process.env.RPC_URL_OPTSEP_TESTNET || 'https://optimism-sepolia.gateway.tenderly.co',
      accounts,
    },
  },
  // ... rest of config
};
```

## Step 3. Deployment Configuration

Configure your vault deployment in `devtools/deployConfig.ts`. This file controls which contracts to deploy and to what chains.

The `deployConfig` supports several modes, depending on what contracts already have been deployed on the hub chain. If your `asset` token is already an `OFT`, you do not need to deploy a new `OFT` contract mesh. Both [Stargate Hydra](https://docs.stargate.finance/primitives/routes/stargateV2#supported-assets-hydra) assets (e.g., `USDC.e`) and [standard OFTs](../oft/quickstart) (e.g., `USDT0`) can be used as the asset inside the `ERC4626` vault. To see a list of existing OFT-compatible assets, review the [LayerZero OFT API](/v2/tools/api/oft).

Pick the setup section that best aligns with your deployment needs:

* [3.1a Existing AssetOFT](#31a-existing-assetoft)
* [3.1b Existing AssetOFT and Vault](#31b-existing-assetoft-and-vault)
* [3.1c Existing AssetOFT, Vault, and ShareOFTAdapter](#31c-existing-assetoft-vault-and-shareoft)

For a completely fresh deployment of the **AssetOFT**, **OVault**, and **ShareOFT**:

* [3.1d New AssetOFT, Vault, and ShareOFTAdapter](#31d-new-assetoft-vault-and-shareoft)

### 3.1a Existing AssetOFT

The only requirement is that your `assetOFT` is deployed on the `hub` chain defined in the `deployConfig` file.

* Update the `_hubEid` and the `_spokeEids` for the networks you plan to deploy to accordingly.

* Add the `assetOFTAddress` contract for the `_hubEid` network under your `vault` config.

* Add any changes necessary to your `Vault` and `ShareOFT` config `contract` or `metadata`.

```typescript wrap theme={null}
// highlight-start
// Hub network where ERC4626 lives
const _hubEid = EndpointId.ARBSEP_V2_TESTNET;
// Spoke networks where ShareOFT lives (excluding hub)
const _spokeEids = [EndpointId.OPTSEP_V2_TESTNET, EndpointId.BASESEP_V2_TESTNET];
// highlight-end

// ============================================
// Deployment Export
// ============================================
// devtools/deployConfig.ts
export const DEPLOYMENT_CONFIG: DeploymentConfig = {
  // highlight-start
  vault: {
    contracts: {
      vault: 'MyERC4626',
      shareAdapter: 'MyShareOFTAdapter',
      composer: 'MyOVaultComposer',
    },
    deploymentEid: _hubEid,
    vaultAddress: undefined, // Existing ERC4626 vault
    assetOFTAddress: '<YOUR_ASSET_OFT_ADDRESS>', // Existing AssetOFT
    shareOFTAdapterAddress: undefined, // Deploy ShareOFTAdapter
  },
  // highlight-end

  // highlight-start
  // Share OFT configuration (only on spoke chains)
  shareOFT: {
    contract: 'MyShareOFT',
    metadata: {
      name: 'MyShareOFT',
      symbol: 'SHARE',
    },
    deploymentEids: _spokeEids,
  },
  // highlight-end

  // Asset OFT configuration (deployed on specified chains)
  assetOFT: {
    contract: 'MyAssetOFT',
    metadata: {
      name: 'MyAssetOFT',
      symbol: 'ASSET',
    },
    deploymentEids: [_hubEid, ..._spokeEids],
  },
} as const;
```

<Tip>
  `assetOFT` and `shareOFT` networks do not need to perfectly overlap, as long as both contain deployments on the `_hubEid`. Configure based on your deployment requirements.
</Tip>

### 3.1b Existing AssetOFT and Vault

If your `assetOFT` and `ERC4626` contracts are already deployed, you only need to deploy the `ShareOFTAdapter` and `Composer`.

* Update the `_hubEid` and the `_spokeEids` for the networks you plan to deploy to accordingly.

* Add the `vaultAddress` and `assetOFTAddress` for the `_hubEid` network under your `vault` config.

* Add any changes necessary to your `ShareOFT` config `contract` or `metadata`.

```typescript wrap theme={null}
// devtools/deployConfig.ts
// highlight-start
// Hub network where ERC4626 lives
const _hubEid = EndpointId.ARBSEP_V2_TESTNET;
// Spoke networks where ShareOFT lives (excluding hub)
const _spokeEids = [EndpointId.OPTSEP_V2_TESTNET, EndpointId.BASESEP_V2_TESTNET];
// highlight-end

// ============================================
// Deployment Export
// ============================================
export const DEPLOYMENT_CONFIG: DeploymentConfig = {
  vault: {
    contracts: {
      vault: 'MyERC4626',
      shareAdapter: 'MyShareOFTAdapter',
      composer: 'MyOVaultComposer',
    },
    deploymentEid: _hubEid,
    // highlight-start
    vaultAddress: '<YOUR_VAULT_ADDRESS>', // Existing ERC4626 vault
    assetOFTAddress: '<YOUR_ASSET_OFT_ADDRESS>', // Existing AssetOFT token
    shareOFTAdapterAddress: undefined, // Deploy ShareOFTAdapter
    // highlight-end
  },

  // highlight-start
  // Share OFT configuration (only on spoke chains)
  shareOFT: {
    contract: 'MyShareOFT',
    metadata: {
      name: 'MyShareOFT',
      symbol: 'SHARE',
    },
    deploymentEids: _spokeEids,
  },
  // highlight-end

  // Asset OFT configuration (deployed on specified chains)
  assetOFT: {
    contract: 'MyAssetOFT',
    metadata: {
      name: 'MyAssetOFT',
      symbol: 'ASSET',
    },
    deploymentEids: [_hubEid, ..._spokeEids],
  },
} as const;
```

<Info>
  This configuration will skip deploying the `AssetOFT` and `ERC4626 Vault` contracts, deploying only the `ShareOFTAdapter` and `Composer`.
</Info>

### 3.1c Existing AssetOFT, Vault, and ShareOFT

If your `assetOFT`, `ERC4626`, and `ShareOFTAdapter` have already been deployed, you only need to deploy the `Composer`.

* Update the `_hubEid` and the `_spokeEids` for the networks you plan to deploy to accordingly.

* Add the `vaultAddress`, `assetOFTAddress`, and `shareOFTAdapterAddress` for the `_hubEid` network under your `vault` config.

* Add any changes necessary to your `composer` config under `vault`.

The `Vault`, `ShareOFT`, and `AssetOFT` configs and deployments will be **skipped**.

```typescript wrap theme={null}
// devtools/deployConfig.ts
// highlight-start
// Hub network where ERC4626 lives
const _hubEid = EndpointId.ARBSEP_V2_TESTNET;
// Spoke networks where ShareOFT lives (excluding hub)
const _spokeEids = [EndpointId.OPTSEP_V2_TESTNET, EndpointId.BASESEP_V2_TESTNET];
// highlight-end

// ============================================
// Deployment Export
// ============================================
export const DEPLOYMENT_CONFIG: DeploymentConfig = {
    vault: {
    contracts: {
      vault: 'MyERC4626',
      shareAdapter: 'MyShareOFTAdapter',
      // highlight-start
      composer: 'MyOVaultComposer',
      // highlight-end
    },
    deploymentEid: _hubEid,
    // highlight-start
    vaultAddress: '<YOUR_VAULT_ADDRESS>',                       // Existing ERC4626 vault
    assetOFTAddress: '<YOUR_ASSET_OFT_ADDRESS>',                // Existing AssetOFT
    shareOFTAdapterAddress: <'YOUR_SHARE_OFT_ADAPTER_ADDRESS'>, // Existing ShareOFTAdapter
    // highlight-end
  },

  // Share OFT configuration (only on spoke chains)
  shareOFT: {
    contract: 'MyShareOFT',
    metadata: {
      name: 'MyShareOFT',
      symbol: 'SHARE',
    },
    deploymentEids: _spokeEids,
  },

  // Asset OFT configuration (deployed on specified chains OR use existing address)
  assetOFT: {
    contract: 'MyAssetOFT',
    metadata: {
      name: 'MyAssetOFT',
      symbol: 'ASSET',
    },
    deploymentEids: [_hubEid, ..._spokeEids],
  },
} as const;
```

<Warning>
  This configuration will skip deployment of the `AssetOFT`, `ERC4626` vault, and `ShareOFT` contracts. Only the `Composer` will be deployed.
</Warning>

### 3.1d New AssetOFT, Vault, and ShareOFT

If you have **no existing** OVault contracts, this configuration will deploy `AssetOFT`, `ERC4626` vault, `ShareOFTAdapter`, and the `Composer`.

```typescript wrap theme={null}
const _hubEid = EndpointId.ARBSEP_V2_TESTNET;
const _spokeEids = [EndpointId.OPTSEP_V2_TESTNET, EndpointId.BASESEP_V2_TESTNET];

// ============================================
// Deployment Export
// ============================================
export const DEPLOYMENT_CONFIG: DeploymentConfig = {
  vault: {
    contracts: {
      vault: 'MyERC4626',
      shareAdapter: 'MyShareOFTAdapter',
      composer: 'MyOVaultComposer',
    },
    deploymentEid: _hubEid,
    // highlight-start
    vaultAddress: undefined,
    assetOFTAddress: undefined,
    shareOFTAdapterAddress: undefined,
    // highlight-end
  },

  // Share OFT configuration (only on spoke chains)
  ShareOFT: {
    contract: 'MyShareOFT',
    metadata: {
      name: 'MyShareOFT',
      symbol: 'SHARE',
    },
    deploymentEids: _spokeEids,
  },

  // Asset OFT configuration (deployed on specified chains)
  AssetOFT: {
    contract: 'MyAssetOFT',
    metadata: {
      name: 'MyAssetOFT',
      symbol: 'ASSET',
    },
    deploymentEids: [_hubEid, ..._spokeEids],
  },
} as const;
```

<Info>
  This configuration will deploy all core OVault contracts for a full fresh setup.
</Info>

### 3.2 Build

Compile your contracts:

```bash wrap theme={null}
pnpm compile
```

<Info>
  If you're deploying the asset OFT from scratch for testing purposes, you'll need to mint an initial supply. Uncomment the `_mint` line in the `MyAssetOFT` constructor to provide initial liquidity. This ensures you have tokens to test deposit and crosschain transfer functionality.
</Info>

<Warning>
  Do NOT mint share tokens directly in `MyShareOFT`. Share tokens must only be minted by the vault contract during deposits to maintain the correct share-to-asset ratio. Manually minting share tokens breaks the vault's accounting and can lead to incorrect redemption values. The mint line in `MyShareOFT` should only be uncommented for UI/integration testing, never in production.
</Warning>

### 3.3 Deploy

Deploy all vault contracts across all configured chains:

```bash wrap theme={null}
pnpm hardhat lz:deploy --tags ovault
```

Based on your `deployConfig.ts`, this single command will begin deploying the defined contracts on your target `_hubEid` and `_spokeEids`.

The deployment scripts automatically skip existing deployments, so you can safely run this command when expanding to new chains. Simply add the new chain endpoints to your `deployConfig.ts` and run the deploy command again.

> **Tip**: To deploy to specific networks only, use the `--networks` flag:
>
> ```bash theme={null}
> pnpm hardhat lz:deploy --tags ovault --networks arbitrum,optimism
> ```

## Step 4. Wiring New Mesh

This establishes the peer relationships between each OFT deployment, enabling crosschain token transfers. See the [OFT Wiring Step](../oft/quickstart#2-wire-messaging-libraries-and-configurations) for more information.

Depending on your deployment configuration in [Step 3](#step-3-deployment-configuration), you will have to wire either your newly deployed `ShareOFT`, `AssetOFT`, or **both**.

### 4.1 Existing Asset

After modifying your `layerzero.share.config.ts`:

```bash wrap theme={null}
pnpm hardhat lz:oapp:wire --oapp-config layerzero.share.config.ts
```

### 4.2 Existing Asset & Share

No action needed.

### 4.3 New Asset & Share

After modifying your `layerzero.asset.config.ts` and `layerzero.share.config.ts`:

```bash wrap theme={null}
# Configure LayerZero connections
pnpm hardhat lz:oapp:wire --oapp-config layerzero.asset.config.ts
pnpm hardhat lz:oapp:wire --oapp-config layerzero.share.config.ts

```

## Step 5: Usage

OVault enables two main operation patterns: **deposits** and **redemptions**. Each uses the standard `OFT.send()` interface with the `composer` handling vault operations automatically.

The provided project scaffold demonstrates how to create send calls in [devtools/examples/ovault-evm/tasks/sendOVaultComposer.ts](https://github.com/LayerZero-Labs/devtools/blob/main/examples/ovault-evm/tasks/sendOVaultComposer.ts).

### Deposit Assets → Receive Shares

**Scenario**: Deposit `asset` from a `_spokeEid`, receive vault `shares` on **the same** `_spokeEid`

```bash wrap theme={null}
# Using the CLI task (recommended)
npx hardhat lz:ovault:send \
  --src-eid 30110 --dst-eid 30110 \
  --amount 100.0 --to 0xRecipient \
  --token-type asset
```

**Flow**:

```mermaid wrap theme={null}
sequenceDiagram
participant U as User(Arbitrum)
participant A_SRC as AssetOFT(Arbitrum)
participant A_HUB as AssetOFT(Hub)
participant C as Composer(Hub)
participant V as Vault(Hub)
participant S_HUB as ShareOFTAdapter(Hub)

    rect rgb(40, 169, 225)
        Note over U,A_SRC: Arbitrum Chain
    end
    rect rgb(243, 244, 246)
        Note over A_HUB,S_HUB: Hub Chain
    end

    U->>A_SRC: send(asset, dstEid, composer, composeMsg)
    A_SRC->>A_HUB: LayerZero transfer
    A_HUB->>C: lzReceive() → lzCompose()

    Note over C: Detects asset deposit operation
    C->>V: deposit(assets)
    V-->>C: shares minted

    C->>S_HUB: send(shares, arbitrum, recipient)
    S_HUB->>A_SRC: LayerZero transfer (shares)
    A_SRC->>U: shares delivered

    Note over U,S_HUB: Single transaction: Assets → Shares (same chain)
```

<br />

**Scenario**: Deposit `asset` from a `_spokeEid`, receive vault `shares` on **a different** `_spokeEid`

```bash wrap theme={null}
# Using the CLI task (recommended)
npx hardhat lz:ovault:send \
  --src-eid 30110 --dst-eid 30111 \
  --amount 100.0 --to 0xRecipient \
  --token-type asset
```

**Flow**:

```mermaid wrap theme={null}
sequenceDiagram
participant U as User(Arbitrum)
participant A_SRC as AssetOFT(Arbitrum)
 participant A_HUB as AssetOFT(Hub)
participant C as Composer(Hub)
participant V as Vault(Hub)
participant S_HUB as ShareOFTAdapter(Hub)
participant S_DST as ShareOFT(Optimism)
participant R as Recipient

    rect rgb(40, 169, 225)
        Note over U,A_SRC: Arbitrum Chain
    end
    rect rgb(243, 244, 246)
        Note over A_HUB,S_HUB: Hub Chain
    end
    rect rgb(255, 4, 32)
        Note over S_DST,R: Optimism Chain
    end

    U->>A_SRC: send(asset, dstEid, composer, composeMsg)
    A_SRC->>A_HUB: LayerZero transfer
    A_HUB->>C: lzReceive() → lzCompose()

    Note over C: Detects asset deposit operation
    C->>V: deposit(assets)
    V-->>C: shares minted

    C->>S_HUB: send(shares, optimism, recipient)
    S_HUB->>S_DST: LayerZero transfer
    S_DST->>R: shares delivered

    Note over U,R: Single transaction: Assets(Arbitrum) → Shares(Optimism)
```

<br />

**Scenario**: Deposit `asset` from `_spokeEid`, receive vault `shares` on the `_hubEid` chain

```bash wrap theme={null}
npx hardhat lz:ovault:send \
  --src-eid 30110 --dst-eid 30184 \
  --amount 100.0 --to 0xRecipient \
  --token-type asset
```

**Flow**:

```mermaid wrap theme={null}
sequenceDiagram
participant U as User(Arbitrum)
participant A_SRC as AssetOFT(Arbitrum)
participant A_HUB as AssetOFT(Hub)
participant C as Composer(Hub)
participant V as Vault(Hub)
participant R as Recipient(Hub)

    rect rgb(40, 169, 225)
        Note over U,A_SRC: Arbitrum Chain
    end
    rect rgb(243, 244, 246)
        Note over A_HUB,R: Hub Chain
    end

    U->>A_SRC: send(asset, dstEid, composer, composeMsg)
    A_SRC->>A_HUB: LayerZero transfer
    A_HUB->>C: lzReceive() → lzCompose()

    Note over C: Detects asset deposit operation
    C->>V: deposit(assets)
    V-->>C: shares minted

    Note over C: dstEid == hubEid (local delivery)
    C->>R: Direct ERC20 transfer (shares)

    Note over U,R: Single transaction: Assets(Arbitrum) → Shares(Hub)
```

### Redeem Shares → Receive Assets

**Scenario**: Redeem vault `shares` from `_spokeEid`, receive `asset` on different `_spokeEid`

```bash wrap theme={null}
npx hardhat lz:ovault:send \
  --src-eid 30111 --dst-eid 30110 \
  --amount 50.0 --to 0xRecipient \
  --token-type share
```

**Flow**:

```mermaid wrap theme={null}
sequenceDiagram
participant U as User(Optimism)
participant S_SRC as ShareOFT(Optimism)
participant S_HUB as ShareOFTAdapter(Hub)
participant C as Composer(Hub)
participant V as Vault(Hub)
participant A_HUB as AssetOFT(Hub)
participant A_DST as AssetOFT(Arbitrum)
participant R as Recipient(Arbitrum)

    rect rgb(255, 4, 32)
        Note over U,S_SRC: Optimism Chain
    end
    rect rgb(243, 244, 246)
        Note over S_HUB,A_HUB: Hub Chain
    end
    rect rgb(40, 169, 225)
        Note over A_DST,R: Arbitrum Chain
    end

    U->>S_SRC: send(shares, dstEid, composer, composeMsg)
    S_SRC->>S_HUB: LayerZero transfer
    S_HUB->>C: lzReceive() → lzCompose()

    Note over C: Detects share redeem operation
    C->>V: redeem(shares)
    V-->>C: assets returned

    C->>A_HUB: send(assets, arbitrum, recipient)
    A_HUB->>A_DST: LayerZero transfer
    A_DST->>R: assets delivered

    Note over U,R: Single transaction: Shares(Optimism) → Assets(Arbitrum)
```

<br />

**Scenario**: Redeem `vault` shares from `_spokeEid`, receive `asset` on the `_hubEid` chain

```bash wrap theme={null}
npx hardhat lz:ovault:send \
  --src-eid 30111 --dst-eid 30184 \
  --amount 50.0 --to 0xRecipient \
  --token-type share
```

**Flow**:

```mermaid wrap theme={null}
sequenceDiagram
participant U as User(Optimism)
participant S_SRC as ShareOFT(Optimism)
participant S_HUB as ShareOFTAdapter(Hub)
participant C as Composer(Hub)
participant V as Vault(Hub)
participant R as Recipient(Hub)

    rect rgb(255, 4, 32)
        Note over U,S_SRC: Optimism Chain
    end
    rect rgb(243, 244, 246)
        Note over S_HUB,R: Hub Chain
    end

    U->>S_SRC: send(shares, dstEid, composer, composeMsg)
    S_SRC->>S_HUB: LayerZero transfer
    S_HUB->>C: lzReceive() → lzCompose()

    Note over C: Detects share redeem operation
    C->>V: redeem(shares)
    V-->>C: assets returned

    Note over C: dstEid == hubEid (local delivery)
    C->>R: Direct ERC20 transfer (assets)

    Note over U,R: Single transaction: Shares(Optimism) → Assets(Hub)
```

### SDK Integration

For programmatic integration, use the official SDK [`@layerzerolabs/ovault-evm/src`](https://github.com/LayerZero-Labs/devtools/tree/main/packages/ovault-evm/src) which simplifies OVault operations by using [viem](https://viem.sh/) to generate the necessary calldata for calling `OFT.send()` with the proper `composeMsg` for the hub composer.

The SDK's `OVaultMessageBuilder.generateOVaultInputs()` method handles all the complex message encoding and returns ready-to-use transaction parameters for `viem` wallet clients.

```typescript wrap theme={null}
const input = {
  srcEid: 40245, // eid for base-sepolia
  hubEid: 40231, // eid for arbitrum-sepolia
  dstEid: 40245, // eid for base-sepolia

  // Optional. If dstAddress is not specified it will default to the walletAddress on the dst chain
  dstAddress: '0x0000000000000000000000000000000000000000',
  walletAddress: '0x0000000000000000000000000000000000000000',
  vaultAddress: '0x0000000000000000000000000000000000000000',

  // Address of the OVault Composer on the Hub Chain. Should implement IVaultComposerSync
  composerAddress: '0x0000000000000000000000000000000000000000',

  // Supply the Viem Chain Definitions for the hub and source chain. This is so the sdk can
  // quote fees and perform read operations
  hubChain: arbitrumSepolia,
  sourceChain: baseSepolia,
  operation: OVaultOperations.DEPOSIT,
  amount: 100000000000000000n,
  slippage: 0.01, // 1% slippage

  // Address of the token/oft. The token is an ERC20. They can be the same address.
  // If tokenAddress isn't specified it defaults to the oftAddress
  tokenAddress: '0x0000000000000000000000000000000000000000',
  oftAddress: '0x0000000000000000000000000000000000000000',
} as const;

const inputs = await OVaultMessageBuilder.generateOVaultInputs(input);
```

For complete usage examples, API reference, and advanced configuration, see the [SDK repository](https://github.com/LayerZero-Labs/devtools/blob/main/packages/ovault-evm/README).

For manual integration and advanced usage, see the [Technical Reference](#technical-reference) section below.

## Technical Reference

### Contracts Overview

OVault uses a **hub-and-spoke model**:

* **Hub Chain**: Hosts the `OFT` asset, `ERC4626` vault, the `VaultComposerSync`, and the share's `OFTAdapter` (lockbox)
* **Spoke Chains**: Host `OFT` assets and `OFT` shares that connect to the hub implementations

These connections enable a user to transfer an amount of the asset or share `OFT` from a **source** blockchain, **deposit** or **redeem** the token amount in the `ERC4626` vault, and receive the corresponding output token amount back on the **source** network.

```mermaid wrap theme={null}
sequenceDiagram
participant U as User(Arbitrum)
participant A_SRC as AssetOFT(Arbitrum)
participant A_HUB as AssetOFT(Hub)
participant C as Composer(Hub)
participant V as ERC4626(Hub)
participant S_HUB as ShareOFTAdapter(Hub)

    rect rgb(40, 169, 225)
        Note over U,A_SRC: Arbitrum Chain
    end
    rect rgb(243, 244, 246)
        Note over A_HUB,S_HUB: Hub Chain
    end

    U->>A_SRC: send(asset, dstEid, composer, composeMsg)
    A_SRC->>A_HUB: LayerZero transfer
    A_HUB->>C: lzReceive() → lzCompose()

    Note over C: Detects asset deposit operation
    C->>V: deposit(assets)
    V-->>C: shares minted

    C->>S_HUB: send(shares, arbitrum, recipient)
    S_HUB->>A_SRC: LayerZero transfer (shares)
    A_SRC->>U: shares delivered

    Note over U,S_HUB: Single transaction: Assets → Shares (source chain)
```

<br />

If you have an existing `assetOFT`, `vault`, or `ShareOFT` implementation, you may only need to deploy some of the contracts provided in the `ovault-evm` example repo:

##### Share OFT (Spoke Chains)

```solidity wrap theme={null}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol";

/**
 * @title MyShareOFT
 * @notice ERC20 representation of the vault's share token on a spoke chain for crosschain functionality
 * @dev This contract represents the vault's share tokens on spoke chains. It inherits from
 * LayerZero's OFT (Omnichain Fungible Token) to enable seamless crosschain transfers of
 * vault shares between the hub chain and spoke chains. This contract is designed to work
 * with ERC4626-compliant vaults, enabling standardized crosschain vault interactions.
 *
 * Share tokens represent ownership in the vault and can be redeemed for the underlying
 * asset on the hub chain. The OFT mechanism ensures that shares maintain their value and can be freely
 * moved across supported chains while preserving the vault's accounting integrity.
 */
contract MyShareOFT is OFT {
    /**
     * @notice Constructs the Share OFT contract
     * @dev Initializes the OFT with LayerZero endpoint and sets up ownership
     * @param _name The name of the share token
     * @param _symbol The symbol of the share token
     * @param _lzEndpoint The address of the LayerZero endpoint on this chain
     * @param _delegate The address that will have owner privileges
     */
    constructor(
        string memory _name,
        string memory _symbol,
        address _lzEndpoint,
        address _delegate
    ) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {
        // WARNING: Do NOT mint share tokens directly as this breaks the vault's share-to-asset ratio
        // Share tokens should only be minted by the vault contract during deposits to maintain
        // the correct relationship between shares and underlying assets
        // _mint(msg.sender, 1 ether); // ONLY uncomment for testing UI/integration, never in production
    }
}
```

Similar to `MyAssetOFT`, `MyShareOFT` is a standard `OFT` representation of the `share` token from the `ERC4626` vault to be used on other spoke chains. This contract requires `MyShareOFTAdapter` to be deployed on the hub chain using the `share` address as the `_token` argument.

<Info>
  If your intended vault `share` is already an `OFT` (e.g., [sUSDe](https://docs.ethena.fi/solution-overview/usde-overview)), you do not need to deploy this contract, and will only need to deploy `MyOVaultComposer`.
</Info>

<Warning>
  You should **NEVER** implement `_mint()` in the constructor or externally in `share` tokens. Since `shares` can be redeemed for `assets` on the hub chain, minting new supply breaks the conversion rate inside the `ERC4626` vault.
</Warning>

##### Asset OFT (All Chains)

```solidity wrap theme={null}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol";

/**
 * @title MyAssetOFT
 * @notice ERC20 representation of the vault's asset token on a spoke chain for crosschain functionality
 * @dev This contract represents the vault's underlying asset on spoke chains. It inherits from
 * LayerZero's OFT (Omnichain Fungible Token) to enable seamless crosschain transfers of the
 * vault's asset tokens between the hub chain and spoke chains.
 *
 * The asset OFT acts as a bridgeable ERC20 representation of the vault's collateral asset, allowing
 * users to move their assets across supported chains while maintaining fungibility.
 */
contract MyAssetOFT is OFT {
    /**
     * @notice Constructs the Asset OFT contract
     * @dev Initializes the OFT with LayerZero endpoint and sets up ownership
     * @param _name The name of the asset token
     * @param _symbol The symbol of the asset token
     * @param _lzEndpoint The address of the LayerZero endpoint on this chain
     * @param _delegate The address that will have owner privileges
     */
    constructor(
        string memory _name,
        string memory _symbol,
        address _lzEndpoint,
        address _delegate
    ) OFT(_name, _symbol, _lzEndpoint, _delegate) Ownable(_delegate) {
        // NOTE: Uncomment the line below if you need to mint initial supply
        // This can be useful for testing or if the asset needs initial liquidity
        // _mint(msg.sender, 1 ether);
    }
}
```

`MyAssetOFT` is an example of a standard `OFT` and `ERC20` token that will be the `asset` inside the `ERC4626` vault.

The `asset` token must be deployed on at least the `hub` and one `spoke` chain.

<Info>
  If your intended vault `asset` is already an `OFT` (e.g., [USDT0](https://docs.usdt0.to/technical-documentation/developer), [USDe](https://docs.ethena.fi/solution-overview/usde-overview)), you do not need to deploy this contract. if your vault `asset` is not an `OFT` (e.g., USDC via [CCTP](https://developers.circle.com/cctp)), you will need to convert the `asset` into an `OFT` compatible asset (e.g., USDC via [Stargate Hydra](https://docs.stargate.finance/primitives/routes/stargateV2#the-hydra-mechanism), [OFTAdapter](../oft/quickstart)).

  See the [OFT API `/list`](/v2/tools/api/oft) endpoint for a detailed list of all known tokens using the OFT standard.
</Info>

##### Vault + Share Adapter (Hub Chain)

```solidity wrap theme={null}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC4626 } from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";

import { OFTAdapter } from "@layerzerolabs/oft-evm/contracts/OFTAdapter.sol";

/**
 * @title MyERC4626
 * @notice ERC4626 tokenized vault implementation for crosschain vault operations
 * @dev SECURITY CONSIDERATIONS:
 *      - Donation/inflation attacks on empty or low-liquidity vaults
 *      - Share price manipulation via large donations before first deposit
 *      - Slippage during deposit/redeem operations in low-liquidity conditions
 *      - First depositor advantage scenarios
 *
 *      See OpenZeppelin ERC4626 documentation for full risk analysis:
 *      https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack
 *
 *      MITIGATIONS:
 *      - OpenZeppelin v4.9+ includes virtual assets/shares to mitigate inflation attacks
 *      - Deployers should consider initial deposits to prevent manipulation
 */
contract MyERC4626 is ERC4626 {
    /**
     * @notice Creates a new ERC4626 vault
     * @dev Initializes the vault with virtual assets/shares protection against inflation attacks
     * @param _name The name of the vault token
     * @param _symbol The symbol of the vault token
     * @param _asset The underlying asset that the vault accepts
     */
    constructor(string memory _name, string memory _symbol, IERC20 _asset) ERC20(_name, _symbol) ERC4626(_asset) {}
}

/**
 * @title MyShareOFTAdapter
 * @notice OFT adapter for vault shares enabling crosschain transfers
 * @dev The share token MUST be an OFT adapter (lockbox).
 * @dev A mint-burn adapter would not work since it transforms `ShareERC20::totalSupply()`
 */
contract MyShareOFTAdapter is OFTAdapter {
    /**
     * @notice Creates a new OFT adapter for vault shares
     * @dev Sets up crosschain token transfer capabilities for vault shares
     * @param _token The vault share token to adapt for crosschain transfers
     * @param _lzEndpoint The LayerZero endpoint for this chain
     * @param _delegate The account with administrative privileges
     */
    constructor(
        address _token,
        address _lzEndpoint,
        address _delegate
    ) OFTAdapter(_token, _lzEndpoint, _delegate) Ownable(_delegate) {}
}
```

`MyERC4626` is the standard tokenized vault contract. Given an `_asset` address for a valid `ERC20` contract in the constructor, the vault will create a corresponding `share` token using the vanilla `ERC4626` implementation.

This `share` must then be transformed into an **Omnichain Fungible Token** using `MyShareOFTAdapter`.

<Info>
  If you have an existing `ERC4626` vault deployed, you will only need to deploy `MyShareOFTAdapter` using the `share` token address as the `address _token` argument in the constructor.
</Info>

##### Composer (Hub Chain)

```solidity wrap theme={null}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { VaultComposerSync } from "@layerzerolabs/ovault-evm/contracts/VaultComposerSync.sol";

/**
 * @title MyOVaultComposer
 * @notice Crosschain vault composer enabling omnichain vault operations via LayerZero
 */
contract MyOVaultComposer is VaultComposerSync {
    /**
     * @notice Creates a new crosschain vault composer
     * @dev Initializes the composer with vault and OFT contracts for omnichain operations
     * @param _vault The vault contract implementing ERC4626 for deposit/redeem operations
     * @param _assetOFT The OFT contract for crosschain asset transfers
     * @param _shareOFT The OFT contract for crosschain share transfers
     */
    constructor(
        address _vault,
        address _assetOFT,
        address _shareOFT
    ) VaultComposerSync(_vault, _assetOFT, _shareOFT) {}
}
```

`VaultComposerSync` is the orchestrator contract that enables crosschain vault operations between the **OFT standard** and **ERC-4626 vaults**, automatically handling deposits and redemptions based on incoming token transfers.

The "Sync" in `VaultComposerSync` refers to **synchronous vault operations** - meaning the vault must support immediate, single-transaction deposits and redemptions without delays or waiting periods.

<Warning>
  ### **For Asynchronous Vaults**

  For asynchronous vaults that require multi-transaction redemptions, you will need to modify the `MyOVaultComposer` contract.
</Warning>

### VaultComposerSync Contract

The `VaultComposerSync` contract is the core orchestrator for omnichain vault operations. It handles deposits, redemptions, and automatic refunds for crosschain vault interactions.

#### Key Methods

##### depositAndSend()

<InteractiveContract contractName="VaultComposerSync" functionName="depositAndSend" description="Deposits ERC20 assets from the caller into the vault and sends shares to the recipient. The assetAmount is the number of ERC20 tokens to deposit, sendParam defines how to send the shares (destination chain, recipient, slippage), and refundAddress receives excess msg.value." />

##### redeemAndSend()

<InteractiveContract contractName="VaultComposerSync" functionName="redeemAndSend" description="Redeems vault shares and sends the resulting assets to the user. The shareAmount is the number of vault shares to redeem, sendParam defines how to send the assets (destination chain, recipient, slippage), and refundAddress receives excess LayerZero fees." />

##### quoteSend()

<InteractiveContract contractName="VaultComposerSync" functionName="quoteSend" description="Quotes the send operation for the given OFT and SendParam, including vault conversion preview. Provides the estimated fee for the send operation and previews the vault conversion (deposit or redeem) to help set appropriate slippage parameters." />

##### lzCompose()

<InteractiveContract contractName="VaultComposerSync" functionName="lzCompose" description="Handles LayerZero compose operations for vault transactions with automatic refund functionality. Called by the LayerZero endpoint when processing composed messages. Includes try-catch pattern for automatic refunds on failure." />

##### handleCompose()

<InteractiveContract contractName="VaultComposerSync" functionName="handleCompose" description="Handles the compose operation for OFT transactions. Can only be called by the contract itself through the try-catch pattern. Decodes the compose message and routes to either deposit or redeem flow based on the input OFT token type." />

#### State Variables

##### VAULT()

<InteractiveContract contractName="VaultComposerSync" functionName="VAULT" description="Returns the ERC4626 vault contract address. This is the vault where deposits and redemptions occur." />

##### ASSET\_OFT()

<InteractiveContract contractName="VaultComposerSync" functionName="ASSET_OFT" description="Returns the asset OFT contract address. This is the OFT representation of the vault's underlying asset." />

##### ASSET\_ERC20()

<InteractiveContract contractName="VaultComposerSync" functionName="ASSET_ERC20" description="Returns the underlying ERC20 asset token address. This is the actual token deposited into the vault." />

##### SHARE\_OFT()

<InteractiveContract contractName="VaultComposerSync" functionName="SHARE_OFT" description="Returns the share OFT adapter contract address. This adapter makes the vault shares omnichain-compatible." />

##### SHARE\_ERC20()

<InteractiveContract contractName="VaultComposerSync" functionName="SHARE_ERC20" description="Returns the underlying ERC20 share token address (vault token). This is the token representing ownership in the vault." />

##### ENDPOINT()

<InteractiveContract contractName="VaultComposerSync" functionName="ENDPOINT" description="Returns the LayerZero endpoint address. This is the endpoint used for crosschain messaging." />

##### VAULT\_EID()

<InteractiveContract contractName="VaultComposerSync" functionName="VAULT_EID" description="Returns the endpoint ID of the hub chain where the vault resides. Used to determine local vs crosschain delivery." />

#### Events

##### Sent - Vault Operation Success

<ContractInterface contractName="VaultComposerSync" interfaceType="event" interfaceName="Sent" description="Emitted when a vault operation (deposit or redeem) completes successfully. Contains the LayerZero GUID for tracking the crosschain transaction." />

##### Refunded - Operation Failed with Refund

<ContractInterface contractName="VaultComposerSync" interfaceType="event" interfaceName="Refunded" description="Emitted when a vault operation fails and tokens are refunded to the source chain. The original tokens are automatically returned to the sender." />

##### Deposited - Assets Deposited

<ContractInterface contractName="VaultComposerSync" interfaceType="event" interfaceName="Deposited" description="Emitted when assets are deposited into the vault and shares are sent to the recipient. Tracks the conversion from assets to shares including sender, recipient, destination chain, and amounts." />

##### Redeemed - Shares Redeemed

<ContractInterface contractName="VaultComposerSync" interfaceType="event" interfaceName="Redeemed" description="Emitted when shares are redeemed from the vault and assets are sent to the recipient. Tracks the conversion from shares to assets including sender, recipient, destination chain, and amounts." />

#### Error Messages

##### ShareOFTNotAdapter - Invalid Share Configuration

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="ShareOFTNotAdapter" description="Thrown when the share OFT is not configured as an adapter (lockbox). Share tokens must use OFTAdapter to preserve vault's totalSupply." />

##### ShareTokenNotVault - Share Token Mismatch

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="ShareTokenNotVault" description="Thrown when the share token address does not match the vault's share token." />

##### AssetTokenNotVaultAsset - Asset Token Mismatch

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="AssetTokenNotVaultAsset" description="Thrown when the asset token does not match the vault's underlying asset." />

##### OnlyEndpoint - Unauthorized Endpoint Call

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="OnlyEndpoint" description="Thrown when a function restricted to the LayerZero endpoint is called by an unauthorized address." />

##### OnlySelf - Invalid Self-Call

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="OnlySelf" description="Thrown when handleCompose() is called by an address other than the contract itself." />

##### OnlyValidComposeCaller - Invalid Compose Caller

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="OnlyValidComposeCaller" description="Thrown when lzCompose is called with a compose sender that is neither ASSET_OFT nor SHARE_OFT." />

##### InsufficientMsgValue - Not Enough Gas for Delivery

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="InsufficientMsgValue" description="Thrown when msg.value is less than the minimum required for destination delivery. This error triggers an automatic refund." />

##### NoMsgValueExpected - Unexpected Payment

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="NoMsgValueExpected" description="Thrown when msg.value is provided for a local transfer (same chain delivery)." />

##### SlippageExceeded - Slippage Protection Triggered

<ContractInterface contractName="VaultComposerSync" interfaceType="error" interfaceName="SlippageExceeded" description="Thrown when the actual vault output (shares or assets) is less than the minimum acceptable amount." />

#### Integration Notes

When integrating with VaultComposerSync:

1. **Always use quoteSend()** before operations to get accurate fee estimates and preview vault conversions
2. **Set appropriate slippage** in the compose message's `minAmountLD` parameter (2-5% recommended)
3. **Monitor events** to track operation success (`Sent`) or refunds (`Refunded`)
4. **Handle refund scenarios** by monitoring both source and hub chains for refunded tokens
5. **Check vault limits** before operations using `vault.maxDeposit()` and `vault.maxRedeem()`

For direct contract interaction without the SDK, encode the compose message as:

```solidity wrap theme={null}
bytes memory composeMsg = abi.encode(
    SendParam({
        dstEid: targetChainEid,
        to: bytes32(recipientAddress),
        amountLD: 0, // Will be updated by composer
        minAmountLD: minOutputAmount, // Critical slippage protection
        extraOptions: optionsBuilder.addExecutorLzReceiveOption(200000, 0).build(),
        composeMsg: "",
        oftCmd: ""
    }),
    minMsgValue // Minimum msg.value for destination delivery
);
```

### Two-Phase Operation Flow

OVault operations follow a **two-phase architecture** where failures and slippage protection occur in distinct stages:

**Phase 1: Source → Hub (Standard OFT)**

* User calls `OFT.send()` targeting the hub composer
* Standard LayerZero transfer with compose message
* Reliable transfer with minimal failure modes

**Phase 2: Hub Operations + Output Routing**

* Composer executes vault operations (`deposit`/`redeem`)
* **Critical slippage point**: Vault conversion rates may have changed
* Output tokens routed to final destination (local or crosschain)

#### Operation Detection

The composer automatically determines the vault operation based on which OFT sent the tokens:

* **AssetOFT caller** → Triggers `deposit` operation (`assets` → `shares`)
* **ShareOFT caller** → Triggers `redeem` operation (`shares` → `assets`)

#### Slippage Protection Strategy

Since the real slippage occurs during vault operations on the hub, the `composeMsg` contains the critical slippage parameters:

* **Phase 1** `minAmountLD`: Set for source token (not critical for vault rates)
* **Phase 2** `minAmountLD`: Set in `composeMsg` for vault output (critical protection)

#### 1. Standard OFT Transfer Initiation

Users call the standard OFT interface with compose instructions:

```solidity wrap theme={null}
// Standard OFT send with compose message
assetOFT.send(
    SendParam({
        dstEid: hubEid,           // Always send to hub first
        to: bytes32(composer),    // VaultComposerSync address
        amountLD: depositAmount,
        minAmountLD: minDepositAmount,    // Slippage protection
        extraOptions: "...",      // Gas for compose + second hop
        composeMsg: composeMsg,  // Second SendParam + minMsgValue
        oftCmd: ""
    }),
    MessagingFee(msg.value, 0),
    refundAddress
);
```

#### 2a. Composer Message Reception

When tokens arrive at the hub via `lzReceive()`, the composer is triggered via `lzCompose()`:

```solidity wrap theme={null}
function lzCompose(
    address _composeCaller,  // Either ASSET_OFT or SHARE_OFT
    bytes32 _guid,
    bytes calldata _message, // Contains routing instructions
    address _executor,
    bytes calldata _extraData
) external payable
```

The compose message contains the **Phase 2 routing instructions** with critical slippage protection:

```solidity wrap theme={null}
// Decoded in handleCompose() - this controls Phase 2 behavior
(SendParam memory sendParam, uint256 minMsgValue) = abi.decode(
    _composeMsg,
    (SendParam, uint256)
);

// SendParam for vault output routing:
// - dstEid: Target chain for output tokens
// - to: Final recipient address
// - amountLD: Updated by composer to actual vault output
// - minAmountLD: CRITICAL - protects against vault rate slippage
// - extraOptions: Gas settings for destination transfer
// - composeMsg: Empty (no nested compose)
// - oftCmd: Empty (no OFT commands)
```

#### 2b. Operation Detection & Execution

The composer automatically determines the vault operation based on which OFT sent the tokens:

**Asset Deposit Flow (AssetOFT → Composer):**

```solidity wrap theme={null}
function _depositAndSend() {
    // 1. Deposit assets into vault
    uint256 shareAmount = VAULT.deposit(_assetAmount, address(this));

    // 2. Verify slippage protection
    _assertSlippage(shareAmount, _sendParam.minAmountLD);

    // 3. Route shares to final destination
    _send(SHARE_OFT, shareAmount, _refundAddress);
}
```

**Share Redemption Flow (ShareOFT → Composer):**

```solidity wrap theme={null}
function _redeemAndSend() {
    // 1. Redeem shares from vault
    uint256 assetAmount = VAULT.redeem(_shareAmount, address(this), address(this));

    // 2. Verify slippage protection
    _assertSlippage(assetAmount, _sendParam.minAmountLD);

    // 3. Route assets to final destination
    _send(ASSET_OFT, assetAmount, _refundAddress);
}
```

#### 2c. Smart Output Routing

The `_send()` function handles both local and crosschain delivery:

```solidity wrap theme={null}
function _send(address _oft, SendParam memory _sendParam, address _refundAddress) {
    if (_sendParam.dstEid == VAULT_EID) {
        // Same chain: Direct ERC20 transfer (no LayerZero fees)
        address erc20 = _oft == ASSET_OFT ? ASSET_ERC20 : SHARE_ERC20;
        IERC20(erc20).safeTransfer(_sendParam.to.bytes32ToAddress(), _sendParam.amountLD);
    } else {
        // Crosschain: Standard OFT send
        IOFT(_oft).send{ value: msg.value }(_sendParam, MessagingFee(msg.value, 0), _refundAddress);
    }
}
```

#### Key Implementation Tips

1. **Start Simple**: Deploy a basic vault first, add yield strategies later
2. **Test Thoroughly**: Each operation type has different gas requirements
3. **Monitor Closely**: Set up alerts for failed compose messages
4. **Plan Recovery**: Document procedures for each failure scenario
5. **Optimize Gas**: Use the task's automatic optimization, adjust as needed

## Troubleshooting

OVault operations have only **two possible final outcomes**: `Success` or `Failed` (but Refunded). Understanding the failure flow helps determine appropriate recovery actions.

```mermaid wrap theme={null}
flowchart LR
Start([User calls OFT.send<br/>with composeMsg]) --> OFTTransfer[LayerZero OFT transfer<br/>Source → Hub chain]

    OFTTransfer --> LayerZeroCheck{LayerZero transfer<br/>successful?}
    LayerZeroCheck -->|Yes| LzReceive[LZ Executor calls lzReceive<br/>OFT credits tokens to composer]
    LayerZeroCheck -->|No| TxRevert[Transaction reverts<br/>No tokens moved]

    LzReceive --> SendCompose[OFT calls endpoint.sendCompose<br/>Stores composeMsg for execution]
    SendCompose --> LzCompose[LZ Executor calls lzCompose<br/>on VaultComposerSync]

    LzCompose --> TryCatch[try-catch around handleCompose<br/>Protects against composer failures]
    TryCatch --> MsgValueCheck{msg.value sufficient<br/>for destination delivery?}
    MsgValueCheck -->|Yes| VaultExecution[Vault operation executes<br/>vault.deposit or vault.redeem]
    MsgValueCheck -->|No| AutoRefund[InsufficientMsgValue revert<br/>try-catch triggers _refund]

    VaultExecution --> ActualAmount[actualAmount from vault output<br/>shares or assets received]
    ActualAmount --> SlippageCheck{actualAmount >= minAmountLD<br/>from composeMsg?}
    SlippageCheck -->|Yes| UpdateParam[Update SendParam.amountLD<br/>Reset minAmountLD to zero]
    SlippageCheck -->|No| SlippageRevert[revert SlippageExceeded<br/>try-catch triggers _refund]

    UpdateParam --> OutputDelivery[_send executes<br/>Hub → Destination or local transfer]
    OutputDelivery --> Success[✓ Operation complete<br/>Tokens delivered to recipient]

    TxRevert --> RetryState[User retries with<br/>correct gas/fees]
    AutoRefund --> RetryState
    SlippageRevert --> RetryState[User retries with<br/>adjusted slippage tolerance]

```

### Refund Scenarios and Recovery

The `VaultComposerSync` uses a try-catch pattern around `handleCompose()` to ensure robust error handling:

```solidity wrap theme={null}
try this.handleCompose{ value: msg.value }(/*...*/) {
    emit Sent(_guid);
} catch (bytes memory _err) {
    // Automatic refund for any handleCompose failures
    _refund(_composeCaller, _message, amount, tx.origin);
    emit Refunded(_guid);
}
```

**Common scenarios caught by try-catch**:

* `InsufficientMsgValue` - insufficient gas for destination delivery → Auto refund
* `SlippageExceeded` - vault output below minimum → Manual refund available
* Vault operational errors (paused, insufficient liquidity) → Manual refund available

##### Transaction Revert: Gas or Fee Issues

**What happens**: OFT transfer fails on source chain before any tokens move

**Common causes**:

* Insufficient native tokens for LayerZero fees
* Invalid destination endpoint configuration
* Gas estimation errors

**User experience**: Transaction reverts immediately, no tokens transferred

**Recovery**: User can retry immediately after fixing the issue

* Use `quoteSend()` to get accurate fee estimation
* Verify destination chain configuration
* Ensure sufficient native tokens for crosschain fees

##### Automatic Refund: Insufficient msg.value for Second Hop

**What happens**: LayerZero completes lzReceive and lzCompose successfully, but insufficient gas for destination delivery

**Technical flow**:

1. LZ Executor calls `lzReceive()` - tokens credited to composer ✓
2. OFT calls `endpoint.sendCompose()` - composeMsg stored ✓
3. LZ Executor calls `lzCompose()` on VaultComposerSync ✓
4. Try-catch around `handleCompose()` catches `InsufficientMsgValue` revert
5. Automatic `_refund()` triggered back to source chain

**Common causes**:

* Underestimated gas for second hop during `quoteSend()`
* Gas price fluctuations between quote and execution
* Complex destination chain operations requiring more gas

**User experience**:

* Crosschain transfer appears successful initially
* Composer automatically triggers refund to source chain
* Original tokens returned within minutes

**Recovery**: Automatic - no user action required

* Monitor source chain for refunded tokens
* Retry with higher gas estimate from `quoteSend()`

##### Manual Refund: Vault Operation Issues

**What happens**: LayerZero flow completes successfully, but vault operation fails slippage check

**Technical flow**:

1. LZ Executor calls `lzReceive()` - tokens credited to composer ✓
2. OFT calls `endpoint.sendCompose()` - composeMsg stored ✓
3. LZ Executor calls `lzCompose()` on VaultComposerSync ✓
4. Try-catch around `handleCompose()` executes vault operation ✓
5. Vault returns `actualAmount` (shares or assets)
6. Slippage check: `actualAmount >= minAmountLD` **FAILS**
7. `SlippageExceeded` revert caught by try-catch
8. Manual `_refund()` available (user must trigger)

**Common causes**:

* Vault share/asset price changed during crosschain transfer
* Vault hit deposit/withdrawal limits between quote and execution
* `minAmountLD` set too high based on stale `previewDeposit/previewRedeem` data

**User experience**:

* Crosschain transfer succeeds
* Vault operation fails on hub with slippage error
* Tokens held by composer awaiting user action

**Recovery**: User must manually trigger refund from hub chain

1. Switch wallet to hub chain network
2. Call composer refund function
3. Original tokens returned to source chain
4. Retry operation with adjusted slippage tolerance

**Prevention**:

* Use wider slippage tolerance (2-5% for volatile vaults)
* Check vault limits: `vault.maxDeposit()`, `vault.maxRedeem()`
* Monitor vault state with `vault.previewDeposit()` before large operations
* Account for time delays in crosschain operations when setting `minAmountLD`
