> ## 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.

# Interactive Solana Program Playground

> Test LayerZero Solana programs directly from your browser. Build and send instructions without writing code. Explore key program instructions for message...

export const InteractiveSolanaInstruction = ({programName, instructionName, description = null, accounts = [], args = []}) => {
  const [solanaWeb3, setSolanaWeb3] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [isOpen, setIsOpen] = useState(false);
  const [network, setNetwork] = useState('');
  const [customRpc, setCustomRpc] = useState('');
  const [customProgramId, setCustomProgramId] = useState('');
  const [isWalletConnected, setIsWalletConnected] = useState(false);
  const [walletAddress, setWalletAddress] = useState(null);
  const [inputValues, setInputValues] = useState({});
  const [accountValues, setAccountValues] = useState({});
  const [logs, setLogs] = useState([]);
  const [status, setStatus] = useState('idle');
  const [errorMessage, setErrorMessage] = useState(null);
  const READ_INSTRUCTIONS = ['quote', 'quoteOft', 'quoteSend', 'oftVersion', 'lzReceiveTypes'];
  const isRead = READ_INSTRUCTIONS.includes(instructionName);
  const badgeText = isRead ? 'READ' : 'WRITE';
  const badgeClasses = isRead ? "bg-[#6366f1] text-white" : "bg-[#10b981] text-white";
  useEffect(() => {
    if (window.solanaWeb3) {
      setSolanaWeb3(window.solanaWeb3);
      setIsLoaded(true);
      return;
    }
    const script = document.createElement('script');
    script.src = 'https://unpkg.com/@solana/web3.js@1.91.0/lib/index.iife.min.js';
    script.async = true;
    script.onload = () => {
      setSolanaWeb3(window.solanaWeb3);
      setIsLoaded(true);
    };
    document.body.appendChild(script);
    return () => {};
  }, []);
  useEffect(() => {
    const initialArgs = {};
    args.forEach(arg => {
      initialArgs[arg.name] = '';
    });
    setInputValues(initialArgs);
    const initialAccounts = {};
    accounts.forEach(acc => {
      initialAccounts[acc.name] = '';
    });
    setAccountValues(initialAccounts);
  }, [args, accounts]);
  const connectWallet = async () => {
    try {
      const {solana} = window;
      if (solana && solana.isPhantom) {
        const response = await solana.connect();
        setWalletAddress(response.publicKey.toString());
        setIsWalletConnected(true);
      } else {
        alert('Solana object not found! Get a Phantom Wallet 👻');
      }
    } catch (err) {
      console.error(err);
      setErrorMessage(err.message);
    }
  };
  const getRpcUrl = () => {
    if (network === 'mainnet-beta') return 'https://api.mainnet-beta.solana.com';
    return 'https://api.devnet.solana.com';
  };
  const handleArgChange = (name, value) => {
    setInputValues(prev => ({
      ...prev,
      [name]: value
    }));
  };
  const handleAccountChange = (name, value) => {
    setAccountValues(prev => ({
      ...prev,
      [name]: value
    }));
  };
  const executeInstruction = async () => {
    if (!solanaWeb3 || !isWalletConnected) return;
    setStatus('loading');
    setLogs([]);
    setErrorMessage(null);
    try {
      const connection = new solanaWeb3.Connection(getRpcUrl(), 'confirmed');
      const publicKey = new solanaWeb3.PublicKey(walletAddress);
      const keys = accounts.map(acc => {
        const pubkeyStr = accountValues[acc.name];
        if (!pubkeyStr) throw new Error(`Missing account: ${acc.name}`);
        return {
          pubkey: new solanaWeb3.PublicKey(pubkeyStr),
          isSigner: acc.type === 'Signer',
          isWritable: acc.description?.toLowerCase().includes('mutable') || false
        };
      });
      const dataBuffer = Buffer.alloc(0);
      const progIdStr = "11111111111111111111111111111111";
      const transaction = new solanaWeb3.Transaction().add(new solanaWeb3.TransactionInstruction({
        keys,
        programId: new solanaWeb3.PublicKey(progIdStr),
        data: dataBuffer
      }));
      transaction.feePayer = publicKey;
      const {blockhash} = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;
      const {signature} = await window.solana.signAndSendTransaction(transaction);
      setStatus('success');
      setLogs([`Transaction sent: ${signature}`]);
      const confirmation = await connection.confirmTransaction(signature);
      if (confirmation.value.err) {
        throw new Error("Transaction failed onchain");
      }
      setLogs(prev => [...prev, "Transaction confirmed!"]);
    } catch (err) {
      console.error(err);
      setStatus('error');
      setErrorMessage(err.message);
    }
  };
  if (!isLoaded) {
    return <div className="p-4 bg-gray-50 rounded-lg animate-pulse">Loading Solana Web3...</div>;
  }
  return <div className="border border-[#e0e0e0] dark:border-[#272727] rounded-lg overflow-hidden bg-[#fafafa] dark:bg-[#0a0a0a] shadow-sm my-4 text-sm font-sans">
      
      {}
      <div className="flex items-center justify-between p-4 cursor-pointer hover:bg-gray-100 dark:hover:bg-zinc-900 transition-colors" onClick={() => setIsOpen(!isOpen)}>
        <div className="flex items-center gap-3 overflow-hidden flex-1 min-w-0">
          <div className="px-2 py-[2px] rounded-[4px] min-w-[3rem] text-[0.625rem] font-semibold tracking-[0.05em] flex justify-center items-center uppercase flex-shrink-0 text-white" style={{
    backgroundColor: isRead ? '#6366f1' : '#10b981'
  }}>
            {badgeText}
          </div>
          <div className="font-mono text-[#7d7d7d] dark:text-[#757575] overflow-x-auto whitespace-nowrap scrollbar-hide text-xs" title={`${instructionName}(${accounts.map(a => `${a.name}: ${a.type}`).join(', ')}, ${args.map(a => `${a.name}: ${a.type}`).join(', ')})`}>
            <span className="text-[#0a0a0a] dark:text-[#f2f2f2] font-normal">{instructionName}</span>
            <span className="text-[#7d7d7d] dark:text-[#757575]">(</span>
             {accounts.map((acc, i) => <span key={`acc-${i}`} className="text-[#7d7d7d] dark:text-[#757575]">
                  <span className="text-[#0a0a0a] dark:text-[#f2f2f2]">{acc.name}</span>: <span className="text-[#e36209] dark:text-[#f1df38]">{acc.type}</span>
                  {i < accounts.length - 1 ? ', ' : ''}
                </span>)}
             {accounts.length > 0 && args.length > 0 && ', '}
             {args.map((arg, i) => <span key={`arg-${i}`} className="text-[#7d7d7d] dark:text-[#757575]">
                  <span className="text-[#0a0a0a] dark:text-[#f2f2f2]">{arg.name}</span>: <span className="text-[#e36209] dark:text-[#f1df38]">{arg.type}</span>
                  {i < args.length - 1 ? ', ' : ''}
                </span>)}
            <span className="text-[#7d7d7d] dark:text-[#757575]">)</span>
          </div>
        </div>
        <div className={`text-gray-400 transform transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}>
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M6 9L12 15L18 9" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </div>
      </div>

      {}
      {isOpen && <div className="content_RXk0" style={{
    animation: '.2s ease-out j',
    background: 'rgba(255, 255, 255, 0.02)',
    border: '1px solid rgba(255, 255, 255, 0.08)',
    borderTop: 'none',
    borderRadius: '0 0 8px 8px',
    marginBottom: '1rem',
    padding: '1.5rem'
  }}>
          
          {description && <div className="description_UDHP text-[#7d7d7d] dark:text-[#a3a3a3] text-[0.8125rem] font-light leading-6 mb-5 p-0">
              {description}
            </div>}

          {}
          <div className="fieldGroup_Wlo7 mb-4">
            <label className="fieldLabel_kbcb block text-[0.625rem] font-semibold text-[#7d7d7d] dark:text-[#a3a3a3] uppercase tracking-wider mb-2">NETWORK</label>
            <div className="chainSelectorWrapper_JWxm max-w-[300px] relative w-full">
              <div className="chainSelectorInput_JzEF flex items-center justify-between w-full p-2 pl-3 border !border-[#d1d5db] dark:!border-[#404040] rounded-md !bg-[#f3f4f6] dark:!bg-[#1a1a1a] cursor-pointer hover:border-[#6cadf5] transition-colors" onClick={e => {}}>
                <div className="selectedChainDisplay_disp flex items-center gap-2">
                  <span className="text-[#0a0a0a] dark:text-[#f2f2f2] text-sm font-normal">
                    {!network ? 'Select Network' : network === 'devnet' ? 'Solana Devnet' : 'Solana Mainnet'}
                  </span>
                </div>
                <div className="dropdownArrow_Xh5h text-[#7d7d7d] transition-transform">
                  <svg width="10" height="6" viewBox="0 0 10 6" fill="none"><path d="M1 1L5 5L9 1" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"></path></svg>
                </div>
              </div>
              {}
              <select value={network} onChange={e => setNetwork(e.target.value)} className="absolute inset-0 opacity-0 cursor-pointer w-full h-full">
                {}
              </select>
            </div>
          </div>

          {}
          <div className="fieldGroup_Wlo7 mb-4">
            <label className="fieldLabel_kbcb block text-[0.625rem] font-semibold text-[#7d7d7d] dark:text-[#a3a3a3] uppercase tracking-wider mb-2">CUSTOM RPC URL (OPTIONAL)</label>
            <div className="inputGroup_KcB5 addressInput_VOhc mb-1">
              <input type="text" placeholder="https://mainnet.helius-rpc.com/?api-key=YOUR_KEY" value={customRpc} onChange={e => setCustomRpc(e.target.value)} className="input_Om6f w-full !bg-[#f3f4f6] dark:!bg-[#1a1a1a] border !border-[#d1d5db] dark:!border-[#404040] text-[#0a0a0a] dark:text-[#f2f2f2] rounded-md py-2 px-3 text-[0.8125rem] transition-colors focus:outline-none focus:border-[#6cadf5] focus:shadow-[0_0_0_2px_rgba(108,173,245,0.1)] placeholder-[#7d7d7d] dark:placeholder-[#525252]" />
            </div>
            <div className="fieldHint_K4uC text-xs text-[#7d7d7d] dark:text-[#757575] leading-relaxed">
              <span>Using <a href="https://solana-rpc.publicnode.com" target="_blank" rel="noopener noreferrer" className="text-[#3986dc] dark:text-[#6cadf5] hover:underline">PublicNode</a> by default.</span><br />
              For production, consider premium RPCs from <a href="https://helius.dev" target="_blank" rel="noopener noreferrer" className="text-[#3986dc] dark:text-[#6cadf5] hover:underline">Helius</a>, <a href="https://quicknode.com" target="_blank" rel="noopener noreferrer" className="text-[#3986dc] dark:text-[#6cadf5] hover:underline">QuickNode</a>, or <a href="https://alchemy.com" target="_blank" rel="noopener noreferrer" className="text-[#3986dc] dark:text-[#6cadf5] hover:underline">Alchemy</a>.
            </div>
          </div>

          {}
           <div className="fieldGroup_Wlo7 mb-4">
            <label className="fieldLabel_kbcb block text-[0.625rem] font-semibold text-[#7d7d7d] dark:text-[#a3a3a3] uppercase tracking-wider mb-2">CUSTOM PROGRAM ID</label>
            <div className="inputGroup_KcB5 addressInput_VOhc mb-1">
              <input type="text" placeholder="11111111111111111111111111111111" value={customProgramId} onChange={e => setCustomProgramId(e.target.value)} className="input_Om6f w-full !bg-[#f3f4f6] dark:!bg-[#1a1a1a] border !border-[#d1d5db] dark:!border-[#404040] text-[#0a0a0a] dark:text-[#f2f2f2] rounded-md py-2 px-3 text-[0.8125rem] transition-colors focus:outline-none focus:border-[#6cadf5] focus:shadow-[0_0_0_2px_rgba(108,173,245,0.1)] placeholder-[#7d7d7d] dark:placeholder-[#525252]" />
            </div>
            <div className="fieldHint_K4uC text-xs text-[#7d7d7d] dark:text-[#757575]">
              No default endpoint program for Select Network. Enter a custom program ID.
            </div>
          </div>

          {}
          {accounts.length > 0 && <div className="parametersSection_oilk mt-6">
              <div className="sectionTitle_EISu text-[0.625rem] font-semibold text-[#7d7d7d] dark:text-[#a3a3a3] uppercase tracking-wider mb-4 border-b border-[#e0e0e0] dark:border-[#272727] pb-2">ACCOUNTS</div>
              <div className="inputs_Yw34 space-y-4">
                {accounts.map((acc, idx) => <div key={idx} className="inputGroup_KcB5">
                    <label className="block text-[0.75rem] font-medium text-[#0a0a0a] dark:text-[#f2f2f2] mb-1.5">
                      {acc.name}
                      <span className="typeLabel_PVtY text-[#7d7d7d] dark:text-[#a3a3a3] font-normal text-[0.6875rem] ml-1">({acc.type})</span>
                      {acc.description && <div className="fieldHint_K4uC text-[0.6875rem] text-[#7d7d7d] dark:text-[#757575] font-normal mt-0.5">{acc.description}</div>}
                    </label>
                    <input type="text" placeholder={acc.name === 'systemProgram' ? '11111111111111111111111111111111' : 'value'} value={accountValues[acc.name] || ''} onChange={e => handleAccountChange(acc.name, e.target.value)} className="input_Om6f w-full !bg-[#f3f4f6] dark:!bg-[#1a1a1a] border !border-[#d1d5db] dark:!border-[#404040] text-[#0a0a0a] dark:text-[#f2f2f2] rounded-md py-2 px-3 text-[0.8125rem] transition-colors focus:outline-none focus:border-[#6cadf5] focus:shadow-[0_0_0_2px_rgba(108,173,245,0.1)] font-mono" />
                  </div>)}
              </div>
            </div>}

          {}
          {args.length > 0 && <div className="parametersSection_oilk mt-6">
              <div className="sectionTitle_EISu text-[0.625rem] font-semibold text-[#7d7d7d] dark:text-[#a3a3a3] uppercase tracking-wider mb-4 border-b border-[#e0e0e0] dark:border-[#272727] pb-2">ARGUMENTS</div>
              <div className="inputs_Yw34 space-y-4">
                {args.map((arg, idx) => <div key={idx} className="inputGroup_KcB5">
                     <label className="block text-[0.75rem] font-medium text-[#0a0a0a] dark:text-[#f2f2f2] mb-1.5">
                      {arg.name}
                      <span className="typeLabel_PVtY text-[#7d7d7d] dark:text-[#a3a3a3] font-normal text-[0.6875rem] ml-1">({arg.type})</span>
                      {arg.description && <div className="fieldHint_K4uC text-[0.6875rem] text-[#7d7d7d] dark:text-[#757575] font-normal mt-0.5">{arg.description}</div>}
                    </label>
                    <input type="text" placeholder={arg.placeholder || 'value'} value={inputValues[arg.name] || ''} onChange={e => handleArgChange(arg.name, e.target.value)} className="input_Om6f w-full !bg-[#f3f4f6] dark:!bg-[#1a1a1a] border !border-[#d1d5db] dark:!border-[#404040] text-[#0a0a0a] dark:text-[#f2f2f2] rounded-md py-2 px-3 text-[0.8125rem] transition-colors focus:outline-none focus:border-[#6cadf5] focus:shadow-[0_0_0_2px_rgba(108,173,245,0.1)]" />
                  </div>)}
              </div>
            </div>}

          {}
          <div className="actions_dJ_o mt-6">
            {!isWalletConnected ? <button onClick={connectWallet} className="actionButton_kY6_ w-full py-3 px-4 bg-transparent hover:bg-[#6b2fe6] font-medium rounded-md transition-colors shadow-sm focus:outline-none focus:ring-2 focus:ring-[#7a3dfc] focus:ring-offset-2">
                Connect Phantom Wallet
              </button> : <button onClick={executeInstruction} disabled={status === 'loading'} className={`actionButton_kY6_ w-full py-3 px-4 font-medium rounded-md transition-colors shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2
                  ${status === 'loading' ? 'bg-blue-400 cursor-wait' : 'bg-[#3986dc] hover:bg-[#3273bf] text-[#3986dc] focus:ring-[#3986dc]'}`}>
                {status === 'loading' ? 'Processing...' : 'Execute Transaction'}
              </button>}
          </div>

          {}
          {errorMessage && <div className="messageBox_hz_w mt-4">
              <div className="messageError_L5lq p-3 bg-[#ffebeb] dark:bg-[#281919] border border-[#f56868] rounded-md text-sm text-[#d84c4c]">
                Please install Phantom wallet: {errorMessage}
              </div>
            </div>}
          {logs.length > 0 && <div className="messageBox_hz_w mt-4">
               <div className="p-3 bg-[#fafafa] dark:bg-[#0a0a0a] border border-[#e0e0e0] dark:border-[#272727] rounded-md text-xs font-mono max-h-40 overflow-y-auto">
                 {logs.map((L, i) => <div key={i} className="text-[#0a0a0a] dark:text-[#f2f2f2]">&gt; {L}</div>)}
               </div>
             </div>}

        </div>}
    </div>;
};

Test LayerZero Solana programs directly from your browser. Build and send instructions without writing code. Explore key program instructions for message fee calculation, sending, receiving, and configuration management.

For production use, please use the official SDKs which provides proper type-safe instruction builders:

* [@layerzerolabs/lz-solana-sdk-v2](https://www.npmjs.com/package/@layerzerolabs/lz-solana-sdk-v2)
* [@layerzerolabs/oft-v2-solana-sdk](https://www.npmjs.com/package/@layerzerolabs/oft-v2-solana-sdk)

<Tip>
  ### Real Onchain Methods

  All instructions shown in this playground are **real methods** available in the LayerZero Solana programs today:

  * **Endpoint Program**: [Source Code](https://github.com/LayerZero-Labs/LayerZero-v2/tree/main/packages/layerzero-v2/solana/programs/programs/endpoint/src)
  * **OFT Program**: [Source Code](https://github.com/LayerZero-Labs/devtools/blob/main/examples/oft-solana/programs/oft/src)

  We only document OApp-relevant instructions, excluding admin-only functions. State variables are clearly marked as direct account data reads, not instructions.
</Tip>

## LayerZero EndpointV2

The main entry point for all crosschain messaging operations on Solana. This program handles message routing, fee calculation, and configuration management.

### Message Routing

Core functions for sending and receiving messages between smart contracts.

#### quote() - Get Fee Estimates

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="quote"
  description="Get a quote for the LayerZero messaging fees required to send a crosschain message from Solana. Returns the native fee and lzToken fee required."
  accounts={[
{ name: "sendLibraryProgram", type: "AccountInfo", description: "Send library program" },
{ name: "sendLibraryConfig", type: "AccountInfo", description: "Send library configuration for the OApp" },
{ name: "defaultSendLibraryConfig", type: "AccountInfo", description: "Default send library configuration" },
{ name: "sendLibraryInfo", type: "AccountInfo", description: "PDA signer to the send library" },
{ name: "endpoint", type: "AccountInfo", description: "Endpoint settings account" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account" }
]}
  args={[
{ name: "sender", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "dstEid", type: "u32", placeholder: "30101" },
{ name: "receiver", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "message", type: "bytes", placeholder: "0x48656c6c6f" },
{ name: "options", type: "bytes", placeholder: "0x00030100110100000000000000000000000000030d40" },
{ name: "payInLzToken", type: "bool", placeholder: "false" }
]}
/>

#### send() - Send Messages

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="send"
  description="Send a crosschain message from Solana to another blockchain. Returns a MessagingReceipt with guid, nonce, and fee information."
  accounts={[
{ name: "sender", type: "Signer", description: "OApp sending the message" },
{ name: "sendLibraryProgram", type: "AccountInfo", description: "Send library program" },
{ name: "sendLibraryConfig", type: "AccountInfo", description: "Send library configuration" },
{ name: "defaultSendLibraryConfig", type: "AccountInfo", description: "Default send library configuration" },
{ name: "sendLibraryInfo", type: "AccountInfo", description: "PDA signer to the send library" },
{ name: "endpoint", type: "AccountInfo", description: "Endpoint settings account" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority account" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "dstEid", type: "u32", placeholder: "30101" },
{ name: "receiver", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "message", type: "bytes", placeholder: "0x48656c6c6f" },
{ name: "options", type: "bytes", placeholder: "0x00030100110100000000000000000000000000030d40" },
{ name: "nativeFee", type: "u64", placeholder: "1000000" },
{ name: "lzTokenFee", type: "u64", placeholder: "0" }
]}
/>

#### clear() - Clear Payload

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="clear"
  description="Clear a nilified message payload and recover the rent. Returns the original payload hash."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account" },
{ name: "payloadHash", type: "AccountInfo", description: "Payload hash account to clear (mutable)" },
{ name: "endpoint", type: "AccountInfo", description: "Endpoint account (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "nonce", type: "u64", placeholder: "1" },
{ name: "guid", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "message", type: "bytes", placeholder: "0x" }
]}
/>

<Info>
  ### Why is clear() under Message Routing instead of Message Recovery?

  On Solana, the `clear()` instruction placement differs from EVM:

  * **EVM**: The Endpoint handles clearing payloads directly during message delivery
  * **Solana**: The OApp must explicitly call `clear()` via CPI in its `lzReceive` implementation

  This architectural difference means Solana developers need to implement the CPI call to `clear()` within their OApp's message handling logic, giving them more control over the message lifecycle but requiring explicit implementation. See the architectural note at the top of this page for the complete message flow.
</Info>

#### sendCompose() - Send Compose Messages

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="sendCompose"
  description="Send a composed message for sequential execution after the primary message. Used for complex crosschain workflows."
  accounts={[
{ name: "from", type: "Signer", description: "Sender of the compose message" },
{ name: "payer", type: "Signer", description: "Transaction fee payer" },
{ name: "composeMessage", type: "AccountInfo", description: "Compose message state (mutable)" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "to", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "guid", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "index", type: "u16", placeholder: "0" },
{ name: "message", type: "bytes", placeholder: "0x" }
]}
/>

#### clearCompose() - Clear Compose Message

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="clearCompose"
  description="Clear a compose message after it has been processed. This cleans up the compose message state."
  accounts={[
{ name: "to", type: "Signer", description: "Recipient of the compose message" },
{ name: "composeMessage", type: "AccountInfo", description: "Compose message state (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "from", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "guid", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "index", type: "u16", placeholder: "0" },
{ name: "message", type: "bytes", placeholder: "0x" }
]}
/>

<Info>
  ### Architectural Difference: lzReceive

  If you're looking for `lzReceive` or `lzCompose` instructions in the Endpoint (as you might expect from EVM), note that on Solana these are implemented directly in the OApp (e.g., OFT program) due to architectural differences.

  **On Solana:**

  * The executor calls `lzReceive` directly on the OApp/OFT program
  * The OApp then makes a CPI (Cross-Program Invocation) call to the Endpoint's `clear` instruction to validate and clear the payload
  * After validation, the OApp continues with its business logic (e.g., minting tokens)

  This is different from EVM where the Endpoint calls back to the OApp's `_lzReceive` function. On Solana, the flow is inverted with the OApp calling into the Endpoint.

  See the [OFT lzReceive implementation](https://github.com/LayerZero-Labs/devtools/blob/73f732acf683ca18b74cd6c6adb3d656d2e0f36a/examples/oft-solana/programs/oft/src/instructions/lz_receive.rs#L72-L94) for an example.
</Info>

### Configuration Management

Functions for setting custom verification, execution, and pathway management.

#### registerOapp() - Register OApp

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="registerOapp"
  description="Register an OApp with the endpoint. This establishes the OApp's delegate who can manage configurations."
  accounts={[
{ name: "payer", type: "Signer", description: "Transaction fee payer" },
{ name: "oapp", type: "Signer", description: "The PDA of the OApp" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry account (mutable)" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "delegate", type: "Pubkey", placeholder: "11111111111111111111111111111111" }
]}
/>

#### setDelegate() - Set Delegate Address

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="setDelegate"
  description="Set the delegate address that can configure settings on behalf of the OApp."
  accounts={[
{ name: "oapp", type: "Signer", description: "OApp PDA" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "delegate", type: "Pubkey", placeholder: "11111111111111111111111111111111" }
]}
/>

#### setSendLibrary() - Configure Send Library

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="setSendLibrary"
  description="Set the messaging library to use for sending messages to a specific destination."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "sendLibraryConfig", type: "AccountInfo", description: "Send library config (mutable)" },
{ name: "messageLibInfo", type: "AccountInfo?", description: "Message library info (optional)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "sender", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30101" },
{ name: "newLib", type: "Pubkey", placeholder: "7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH" }
]}
/>

#### setReceiveLibrary() - Configure Receive Library

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="setReceiveLibrary"
  description="Set the messaging library to use for receiving messages from a specific source."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "receiveLibraryConfig", type: "AccountInfo", description: "Receive library config (mutable)" },
{ name: "messageLibInfo", type: "AccountInfo?", description: "Message library info (optional)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30106" },
{ name: "newLib", type: "Pubkey", placeholder: "7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH" },
{ name: "gracePeriod", type: "u64", placeholder: "0" }
]}
/>

#### setConfig() - Set Configuration Parameters

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="setConfig"
  description="Set configuration for DVNs, executors, and other protocol parameters."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "messageLibInfo", type: "AccountInfo", description: "Message library info" },
{ name: "messageLib", type: "AccountInfo", description: "Message library PDA" },
{ name: "messageLibProgram", type: "AccountInfo", description: "Message library program" }
]}
  args={[
{ name: "oapp", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30101" },
{ name: "configType", type: "u32", placeholder: "1", description: "Config type (e.g., 1 for executor)" },
{ name: "config", type: "bytes", placeholder: "0x", description: "Configuration data" }
]}
/>

#### initNonce() - Initialize Nonce

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="initNonce"
  description="Initialize nonce tracking for messages between OApps. Must be called before sending messages."
  accounts={[
{ name: "delegate", type: "Signer", description: "OApp delegate (only delegate can initialize)" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account (mutable)" },
{ name: "pendingInboundNonce", type: "AccountInfo", description: "Pending inbound nonce (mutable)" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" }
]}
  args={[
{ name: "localOapp", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "remoteEid", type: "u32", placeholder: "30101" },
{ name: "remoteOapp", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" }
]}
/>

#### initVerify() - Initialize Verification

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="initVerify"
  description="Initialize the payload hash account for message verification. Must be called before verify()."
  accounts={[
{ name: "payer", type: "Signer", description: "Transaction fee payer" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account" },
{ name: "payloadHash", type: "AccountInfo", description: "Payload hash account (mutable)" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" }
]}
  args={[
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "nonce", type: "u64", placeholder: "1" }
]}
/>

#### initSendLibrary() - Initialize Send Library

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="initSendLibrary"
  description="Initialize send library configuration for an OApp on a specific destination chain."
  accounts={[
{ name: "delegate", type: "Signer", description: "OApp delegate (only delegate can initialize)" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "sendLibraryConfig", type: "AccountInfo", description: "Send library config (mutable)" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" }
]}
  args={[
{ name: "sender", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30101" }
]}
/>

#### initReceiveLibrary() - Initialize Receive Library

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="initReceiveLibrary"
  description="Initialize receive library configuration for an OApp on a specific source chain."
  accounts={[
{ name: "delegate", type: "Signer", description: "OApp delegate (only delegate can initialize)" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "receiveLibraryConfig", type: "AccountInfo", description: "Receive library config (mutable)" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30106" }
]}
/>

#### initConfig() - Initialize Configuration

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="initConfig"
  description="Initialize configuration accounts for message library settings (DVN, executor, etc)."
  accounts={[
{ name: "delegate", type: "Signer", description: "OApp delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "messageLibInfo", type: "AccountInfo", description: "Message library info" },
{ name: "messageLib", type: "AccountInfo", description: "Message library PDA" },
{ name: "messageLibProgram", type: "AccountInfo", description: "Message library program" }
]}
  args={[
{ name: "oapp", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30101" }
]}
/>

#### setReceiveLibraryTimeout() - Set Library Timeout

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="setReceiveLibraryTimeout"
  description="Set a timeout for transitioning between receive libraries. Allows graceful library upgrades."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "receiveLibraryConfig", type: "AccountInfo", description: "Receive library config (mutable)" },
{ name: "messageLibInfo", type: "AccountInfo", description: "Message library info" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "eid", type: "u32", placeholder: "30106" },
{ name: "lib", type: "Pubkey", placeholder: "7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH" },
{ name: "expiry", type: "u64", placeholder: "1735689600" }
]}
/>

### Message Recovery & Security

Functions for handling message exceptions, security threats, and recovery scenarios.

#### burn() - Permanently Block Message

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="burn"
  description="Permanently marks a message as unexecutable and un-verifiable. This irreversible action prevents the message from ever being executed."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account" },
{ name: "payloadHash", type: "AccountInfo", description: "Payload hash account to burn (mutable)" },
{ name: "endpoint", type: "AccountInfo", description: "Endpoint account (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "nonce", type: "u64", placeholder: "1" },
{ name: "payloadHash", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" }
]}
/>

#### skip() - Skip Inbound Nonce

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="skip"
  description="Skip a specific inbound message nonce. This allows the OApp to continue processing subsequent messages if one is stuck or problematic."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account (mutable)" },
{ name: "pendingInboundNonce", type: "AccountInfo", description: "Pending inbound nonce (mutable)" },
{ name: "payloadHash", type: "AccountInfo", description: "Payload hash account (mutable)" },
{ name: "endpoint", type: "AccountInfo", description: "Endpoint account (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "nonce", type: "u64", placeholder: "1" }
]}
/>

#### nilify() - Mark Message as Nil

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="nilify"
  description="Mark a message payload as nil, allowing it to be cleared later. This is a two-step process for safely handling problematic messages."
  accounts={[
{ name: "signer", type: "Signer", description: "OApp or delegate" },
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry" },
{ name: "nonce", type: "AccountInfo", description: "Nonce account (mutable)" },
{ name: "pendingInboundNonce", type: "AccountInfo", description: "Pending inbound nonce (mutable)" },
{ name: "payloadHash", type: "AccountInfo", description: "Payload hash account (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "receiver", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "nonce", type: "u64", placeholder: "1" },
{ name: "payloadHash", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" }
]}
/>

#### withdrawRent() - Withdraw Rent

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="withdrawRent"
  description="Admin function to withdraw accumulated rent from closed accounts."
  accounts={[
{ name: "admin", type: "Signer", description: "Endpoint admin" },
{ name: "endpoint", type: "AccountInfo", description: "Endpoint account (mutable)" },
{ name: "receiver", type: "AccountInfo", description: "Receiver account (mutable)" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "Endpoint program ID" }
]}
  args={[
{ name: "amount", type: "u64", placeholder: "1000000000" }
]}
/>

### Status Checks

Functions for querying current configuration settings, library assignments, nonce tracking, and message states.

#### eid() - Get Endpoint ID

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="eid"
  description="Reads the endpoint ID (EID) from the EndpointSettings account. Each blockchain in the LayerZero network has a unique endpoint ID."
  accounts={[
{ name: "endpoint", type: "AccountInfo", description: "Endpoint settings account" }
]}
  args={[]}
/>

#### endpointAdmin() - Get Endpoint Admin

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="endpointAdmin"
  description="Reads the admin address from the EndpointSettings account."
  accounts={[
{ name: "endpoint", type: "AccountInfo", description: "Endpoint settings account" }
]}
  args={[]}
/>

#### oappDelegate() - Get OApp Delegate

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="oappDelegate"
  description="Reads the delegate address for an OApp from its registry entry."
  accounts={[
{ name: "oappRegistry", type: "AccountInfo", description: "OApp registry account for the specific OApp" }
]}
  args={[]}
/>

#### outboundNonce() - Get Outbound Nonce

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="outboundNonce"
  description="Reads the current outbound nonce for messages to a specific destination."
  accounts={[
{ name: "nonce", type: "AccountInfo", description: "Nonce account for the OApp and destination" }
]}
  args={[]}
/>

#### inboundNonce() - Get Inbound Nonce

<InteractiveSolanaInstruction
  programName="endpoint"
  instructionName="inboundNonce"
  description="Reads the current inbound nonce for messages from a specific source."
  accounts={[
{ name: "nonce", type: "AccountInfo", description: "Nonce account for the OApp and source" }
]}
  args={[]}
/>

## Omnichain Fungible Token (OFT)

Omnichain Fungible Token (OFT) enables seamless crosschain token transfers. Deploy once and bridge your SPL tokens to any supported blockchain.

Since Solana uses a rent-based storage model rather than EVM's gas-per-bytecode deployment costs, and has no restrictive contract size limits (like EVM's 24KB limit), we can include all of these extensions in the same program. While some OFT instances may not utilize all features (like fees or rate limits), having them built-in provides maximum flexibility without the contract splitting requirements common in EVM development.

<Info>
  ### Default OFT Program

  For Solana Mainnet, we use **PENGU OFT** as the default example:

  * **Program**: `EfRMrTJWU2CYm52kHmRYozQNdF8RH5aTi3xyeSuLAX2Y`
  * **OFT Store**: `qMNo1RFo11J9ZLGuq7dVmWAssuCZaNsSamk8g2q4UZA`

  You can replace these with your own OFT deployment addresses.
</Info>

<Info>
  ### lzReceive Implementation

  The `lzReceive` instruction is implemented here in the OFT program (not in the Endpoint). This is a key architectural difference from EVM:

  * On Solana, executors call `lzReceive` directly on the OApp/OFT
  * The OFT program then makes a CPI call to the Endpoint's `clear` instruction to validate the message
  * After successful validation, the OFT continues with its logic (minting tokens, updating balances, etc.)

  The flow is: Executor → OFT.lzReceive → Endpoint.clear (via CPI) → Continue OFT logic
</Info>

### Send Tokens

#### quoteSend() - Get Transfer Fees

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="quoteSend"
  description="Get messaging fees for sending tokens crosschain. Returns native fee and lzToken fee."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account" },
{ name: "peer", type: "AccountInfo", description: "Peer configuration account" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint" }
]}
  args={[
{ name: "dstEid", type: "u32", placeholder: "30101" },
{ name: "to", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "amountLd", type: "u64", placeholder: "1000000000" },
{ name: "minAmountLd", type: "u64", placeholder: "900000000" },
{ name: "options", type: "bytes", placeholder: "0x00030100110100000000000000000000000000030d40" },
{ name: "composeMsg", type: "bytes?", placeholder: "0x", description: "Optional compose message" },
{ name: "payInLzToken", type: "bool", placeholder: "false" }
]}
/>

#### quoteOft() - Get Detailed Transfer Quote

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="quoteOft"
  description="Get a detailed quote for crosschain token transfer including fees, limits, and expected amounts."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account" },
{ name: "peer", type: "AccountInfo", description: "Peer configuration account" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint" }
]}
  args={[
{ name: "dstEid", type: "u32", placeholder: "30101" },
{ name: "to", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "amountLd", type: "u64", placeholder: "1000000000" },
{ name: "minAmountLd", type: "u64", placeholder: "900000000" },
{ name: "options", type: "bytes", placeholder: "0x00030100110100000000000000000000000000030d40" },
{ name: "composeMsg", type: "bytes?", placeholder: "0x", description: "Optional compose message" },
{ name: "payInLzToken", type: "bool", placeholder: "false" }
]}
/>

#### send() - Transfer Tokens

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="send"
  description="Send SPL tokens from Solana to another blockchain. The tokens will be locked on Solana and minted on the destination chain."
  accounts={[
{ name: "signer", type: "Signer", description: "Account sending tokens" },
{ name: "peer", type: "AccountInfo", description: "Peer configuration (mutable)" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account (mutable)" },
{ name: "tokenSource", type: "AccountInfo", description: "Source token account (mutable)" },
{ name: "tokenEscrow", type: "AccountInfo", description: "Token escrow account (mutable)" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint (mutable)" },
{ name: "tokenProgram", type: "AccountInfo", description: "SPL token program" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "OFT program ID" }
]}
  args={[
{ name: "dstEid", type: "u32", placeholder: "30101" },
{ name: "to", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "amountLd", type: "u64", placeholder: "1000000000" },
{ name: "minAmountLd", type: "u64", placeholder: "900000000" },
{ name: "options", type: "bytes", placeholder: "0x00030100110100000000000000000000000000030d40" },
{ name: "composeMsg", type: "bytes?", placeholder: "0x", description: "Optional compose message" },
{ name: "nativeFee", type: "u64", placeholder: "1000000" },
{ name: "lzTokenFee", type: "u64", placeholder: "0" }
]}
/>

#### lzReceiveTypes() - Get Receive Account Types

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="lzReceiveTypes"
  description="Get the required account types for lzReceive instruction. Returns array of account metadata. This is a Solana-specific helper for dynamic account resolution."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint" }
]}
  args={[
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "nonce", type: "u64", placeholder: "1" },
{ name: "guid", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "message", type: "bytes", placeholder: "0x" },
{ name: "extraData", type: "bytes", placeholder: "0x" }
]}
/>

#### lzReceive() - Receive Tokens

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="lzReceive"
  description="Receive tokens from another blockchain. This instruction is called by the executor to deliver tokens to the recipient on Solana."
  accounts={[
{ name: "payer", type: "Signer", description: "Transaction fee payer" },
{ name: "peer", type: "AccountInfo", description: "Peer configuration (mutable)" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account (mutable)" },
{ name: "tokenEscrow", type: "AccountInfo", description: "Token escrow account (mutable)" },
{ name: "toAddress", type: "AccountInfo", description: "Recipient address" },
{ name: "tokenDest", type: "AccountInfo", description: "Destination token account (mutable)" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint (mutable)" },
{ name: "mintAuthority", type: "AccountInfo?", description: "Mint authority (optional)" },
{ name: "tokenProgram", type: "AccountInfo", description: "SPL token program" },
{ name: "associatedTokenProgram", type: "AccountInfo", description: "Associated token program" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" },
{ name: "eventAuthority", type: "AccountInfo", description: "Event authority" },
{ name: "program", type: "AccountInfo", description: "OFT program ID" }
]}
  args={[
{ name: "srcEid", type: "u32", placeholder: "30106" },
{ name: "sender", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "nonce", type: "u64", placeholder: "1" },
{ name: "guid", type: "bytes32", placeholder: "0x0000000000000000000000000000000000000000000000000000000000000000" },
{ name: "message", type: "bytes", placeholder: "0x" },
{ name: "extraData", type: "bytes", placeholder: "0x" }
]}
/>

### Token Details

The **OFT Store** account is a [Program Derived Address (PDA)](https://solana.com/docs/core/pda), not the token mint itself. This account stores essential OFT related state variables.

#### oftAdmin() - Get OFT Admin

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="oftAdmin"
  description="Reads the admin address from the OFT store account. The admin can configure settings and pause/unpause the OFT."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the admin address" }
]}
  args={[]}
/>

#### tokenMint() - Get Token Mint

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="tokenMint"
  description="Reads the SPL token mint address from the OFT store account. Note: The OFT store is NOT the token mint address itself, but a separate PDA account created by the OFT program."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store PDA account (NOT the token mint address)" }
]}
  args={[]}
/>

#### tokenEscrow() - Get Token Escrow

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="tokenEscrow"
  description="Reads the token escrow account address from the OFT store account. This is where locked tokens are held."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the escrow address" }
]}
  args={[]}
/>

#### sharedDecimals() - Get Shared Decimals

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="sharedDecimals"
  description="Reads the shared decimal precision from the OFT store account. This is typically 6 or 8 decimals to ensure compatibility across different chains. Note: This reads directly from account data at the correct offset."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the data" }
]}
  args={[]}
/>

#### decimalConversionRate() - Get Decimal Conversion Rate

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="decimalConversionRate"
  description="Reads the ld2sdRate (local decimals to shared decimals rate) from the OFT store account. This is 10^(localDecimals - sharedDecimals). Note: This reads directly from offset 9 in the OFTStore account data."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the ld2sdRate" }
]}
  args={[]}
/>

#### oftVersion() - Get OFT Version

<InteractiveSolanaInstruction programName="oft" instructionName="oftVersion" description="Returns the OFT implementation version as a Version struct with interface version (2) and message version (1)." accounts={[]} args={[]} />

#### tvlLd() - Get Total Value Locked

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="tvlLd"
  description="Reads the total value locked in local decimals from the OFT store account. This represents the total amount of tokens currently locked in the OFT."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the TVL data" }
]}
  args={[]}
/>

#### isPaused() - Check Pause State

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="isPaused"
  description="Checks if the OFT is currently paused. This is an optional feature - many OFTs operate without pause functionality. When enabled and paused, no transfers can be initiated."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the pause state" }
]}
  args={[]}
/>

#### defaultFeeBps() - Get Default Fee

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="defaultFeeBps"
  description="Reads the default fee in basis points (bps) from the OFT store account. This is an optional feature - many OFTs operate with zero fees. When enabled, 100 bps = 1%."
  accounts={[
{ name: "oftStore", type: "AccountInfo", description: "OFT store account containing the fee configuration" }
]}
  args={[]}
/>

### Peer Configuration

These functions read peer-specific configuration from PeerConfig accounts:

#### peerAddress() - Get Peer Address

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="peerAddress"
  accounts={[
{ name: 'peerConfig', type: 'AccountInfo', description: 'PeerConfig account for a specific destination' }
]}
  description="Get the peer address for a specific destination chain (32 bytes)"
/>

#### enforcedOptions() - Check Enforced Options

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="enforcedOptions"
  accounts={[
{ name: 'peerConfig', type: 'AccountInfo', description: 'PeerConfig account for a specific destination' }
]}
  description="Check enforced LayerZero message options for this peer"
/>

#### peerFeeBps() - Get Peer Fee

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="peerFeeBps"
  accounts={[
{ name: 'peerConfig', type: 'AccountInfo', description: 'PeerConfig account for a specific destination' }
]}
  description="Get the peer-specific fee in basis points. This is an optional feature that allows different fees for different destination chains. Returns 'None' if no peer-specific fee is set."
/>

#### outboundRateLimiter() - Check Outbound Rate Limiter

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="outboundRateLimiter"
  accounts={[
{ name: 'peerConfig', type: 'AccountInfo', description: 'PeerConfig account for a specific destination' }
]}
  description="Check if outbound rate limiter is configured for this peer. This is an optional feature for controlling token flow rates. Most OFTs operate without rate limits."
/>

#### inboundRateLimiter() - Check Inbound Rate Limiter

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="inboundRateLimiter"
  accounts={[
{ name: 'peerConfig', type: 'AccountInfo', description: 'PeerConfig account for a specific destination' }
]}
  description="Check if inbound rate limiter is configured for this peer. This is an optional feature for controlling token flow rates. Most OFTs operate without rate limits."
/>

<Tip>
  ### Finding PeerConfig Account

  The PeerConfig account is a PDA (Program Derived Address) derived from:

  * OFT program ID
  * Seeds: `[b"peer_config", oft_store.key().as_ref(), &dst_eid.to_be_bytes()]`

  You'll need to derive this address using the OFT store account and the destination chain's endpoint ID.
</Tip>

### Management Functions

#### initOft() - Initialize OFT

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="initOft"
  description="Initialize a new Omnichain Fungible Token. This sets up an SPL token to be transferable across multiple blockchains."
  accounts={[
{ name: "payer", type: "Signer", description: "Account paying for initialization" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account (PDA)" },
{ name: "lzReceiveTypesAccounts", type: "AccountInfo", description: "LZ receive types accounts" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint" },
{ name: "tokenEscrow", type: "Signer", description: "Token escrow account" },
{ name: "tokenProgram", type: "AccountInfo", description: "SPL token program" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" }
]}
  args={[
{ name: "oftType", type: "u8", placeholder: "0", description: "0 for Native, 1 for Adapter" },
{ name: "admin", type: "Pubkey", placeholder: "11111111111111111111111111111111" },
{ name: "sharedDecimals", type: "u8", placeholder: "6", description: "Shared decimals (typically 6)" },
{ name: "endpointProgram", type: "Pubkey?", placeholder: "76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6", description: "Optional endpoint program" }
]}
/>

#### setPeerConfig() - Configure Remote Peer

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="setPeerConfig"
  description="Configure the connection to an OFT on another chain. This can set the peer address (required), and optionally configure peer-specific fees, enforced options, and rate limits. Many OFTs only set the peer address without using the optional features."
  accounts={[
{ name: "admin", type: "Signer", description: "OFT admin account" },
{ name: "peer", type: "AccountInfo", description: "Peer configuration account (mutable)" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account" },
{ name: "systemProgram", type: "AccountInfo", description: "System program" }
]}
  args={[
{ name: "remoteEid", type: "u32", placeholder: "30101" },
{ name: "configType", type: "u8", placeholder: "0", description: "0=PeerAddress (required), 1=FeeBps (optional), 2=EnforcedOptions, 3=OutboundLimit (optional), 4=InboundLimit (optional)" },
{ name: "configData", type: "bytes", placeholder: "0x", description: "Configuration data based on type" }
]}
/>

#### setOftConfig() - Update OFT Settings

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="setOftConfig"
  description="Update various OFT configuration settings. Admin changes are common, while features like fees (DefaultFee) and pausability (Paused, Pauser, Unpauser) are optional and not used by all OFTs."
  accounts={[
{ name: "admin", type: "Signer", description: "OFT admin account" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account (mutable)" }
]}
  args={[
{ name: "configType", type: "u8", placeholder: "0", description: "0=Admin, 1=Delegate, 2=DefaultFee (optional feature), 3=Paused (optional), 4=Pauser (optional), 5=Unpauser (optional)" },
{ name: "configData", type: "bytes", placeholder: "0x", description: "Configuration data based on type" }
]}
/>

#### setPause() - Pause/Unpause OFT

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="setPause"
  description="Pause or unpause the OFT. This is an optional feature - many OFTs operate without pause functionality. When enabled and paused, no transfers can be initiated. Only the designated pauser (to pause) or unpauser (to unpause) can call this."
  accounts={[
{ name: "signer", type: "Signer", description: "Pauser or unpauser account" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account (mutable)" }
]}
  args={[
{ name: "paused", type: "bool", placeholder: "true", description: "true to pause, false to unpause" }
]}
/>

#### withdrawFee() - Withdraw Collected Fees

<InteractiveSolanaInstruction
  programName="oft"
  instructionName="withdrawFee"
  description="Withdraw collected fees from the OFT. This is an optional feature - many OFTs operate with zero fees and never use this function. When fees are enabled, only the admin can withdraw them."
  accounts={[
{ name: "admin", type: "Signer", description: "OFT admin account" },
{ name: "oftStore", type: "AccountInfo", description: "OFT store account" },
{ name: "tokenMint", type: "AccountInfo", description: "SPL token mint" },
{ name: "tokenEscrow", type: "AccountInfo", description: "Token escrow account (mutable)" },
{ name: "tokenDest", type: "AccountInfo", description: "Destination token account (mutable)" },
{ name: "tokenProgram", type: "AccountInfo", description: "SPL token program" }
]}
  args={[
{ name: "feeLd", type: "u64", placeholder: "1000000", description: "Amount of fees to withdraw in local decimals" }
]}
/>

## Events and Errors

### Endpoint Events

Key events emitted by the Endpoint program during crosschain operations.

#### PacketSentEvent - Message Sent

Emitted when a packet is sent through the endpoint. Contains the encoded packet data and execution options for tracking crosschain messages.

#### PacketVerifiedEvent - Message Verified

Emitted when an inbound message has been verified by the DVNs and is ready for execution. Indicates the message passed all security checks.

#### PacketDeliveredEvent - Message Delivered

Emitted when a message is successfully delivered to the destination OApp. This confirms the crosschain transaction completed.

#### ComposeSentEvent - Compose Message Queued

Emitted when a compose message is queued for sequential execution after the primary message. Used for complex multi-step operations.

#### ComposeDeliveredEvent - Compose Message Executed

Emitted when a compose message is successfully executed. Indicates the secondary operation completed successfully.

#### DelegateSetEvent - Delegate Updated

Emitted when an OApp delegate is set or changed. The delegate can configure settings on behalf of the OApp.

#### SendLibrarySetEvent - Send Library Configured

Emitted when the send library is configured for a specific destination. Tracks library changes for outbound messages.

#### ReceiveLibrarySetEvent - Receive Library Configured

Emitted when the receive library is configured for a specific source. Tracks library changes for inbound messages.

#### OAppRegisteredEvent - OApp Registration

Emitted when a new OApp is registered with the endpoint. This establishes the OApp's ability to send and receive messages.

### Endpoint Errors

Common errors returned by the Endpoint program.

#### Unauthorized - Permission Denied

Thrown when the caller lacks required permissions for the operation. Only authorized addresses can perform certain actions.

#### InvalidNonce - Nonce Mismatch

Thrown when processing a message with an invalid nonce. Ensures messages are processed in the correct sequential order.

#### InvalidSender - Unauthorized Sender

Thrown when receiving a message from an unauthorized sender. Only configured peers can send messages to the OApp.

#### InvalidReceiver - Invalid Destination

Thrown when the specified receiver address is invalid or not configured properly for the destination chain.

#### LzTokenUnavailable - LayerZero Token Error

Thrown when LayerZero token operations fail or tokens are unavailable for fee payment.

### OFT Events

Key events emitted by the OFT program during token operations. ([Source](https://github.com/LayerZero-Labs/devtools/blob/main/examples/oft-solana/programs/oft/src/events.rs))

#### OFTSent - Tokens Sent Crosschain

Emitted when tokens are sent to another chain. Contains the message GUID, destination chain ID, sender and recipient addresses, and the amount sent in both shared and local decimals.

#### OFTReceived - Tokens Received

Emitted when tokens are received from another chain. Contains the source chain ID, sender address, and the amount received after decimal conversion.

### OFT Errors

Common errors returned by the OFT program. ([Source](https://github.com/LayerZero-Labs/devtools/blob/main/examples/oft-solana/programs/oft/src/errors.rs))

#### Paused - OFT Operations Paused

Thrown when attempting operations while the OFT is paused. No transfers can be initiated until unpaused by the designated unpauser.

#### SlippageExceeded - Insufficient Received Amount

Thrown when the received amount after crosschain transfer falls below the minimum acceptable amount due to decimal conversions or fees.

#### RateLimitExceeded - Transfer Rate Limit Hit

Thrown when a transfer exceeds the configured rate limits for the peer connection. Wait for the rate limit window to reset.

#### InvalidOptions - Invalid Message Options

Thrown when the provided LayerZero message options are invalid or incompatible with the OFT configuration.

#### InvalidAmount - Invalid Transfer Amount

Thrown when the transfer amount is zero, exceeds limits, or is otherwise invalid for the operation.

#### InvalidPeer - Peer Not Configured

Thrown when attempting to interact with a destination chain where no peer OFT has been configured. Use setPeerConfig() first.

## Usage Tips

### Getting Started

1. **Connect Your Wallet**: Click "Connect Phantom Wallet" to connect your Solana wallet
2. **Select Network**: Choose between Solana Mainnet and Devnet
3. **Custom RPC (Optional)**: If you encounter rate limits (403 errors), add a custom RPC URL:
   * [Helius](https://helius.dev) - Generous free tier
   * [QuickNode](https://quicknode.com) - Free tier available
   * [Alchemy](https://alchemy.com) - Professional services

### Common Workflows

#### Sending Tokens Crosschain (OFT)

1. Initialize your OFT with `initOft()`
2. Configure peers with `setPeerConfig()`
3. Get a quote with `quoteOft()` or `quoteSend()`
4. Send tokens with `send()`

#### Setting Up Messaging (Endpoint)

1. Register your OApp with `registerOapp()`
2. Initialize nonce tracking with `initNonce()`
3. Set up libraries with `initSendLibrary()` and `initReceiveLibrary()`
4. Configure DVNs/executors with `setConfig()`
5. Get quotes with `quote()` and send messages with `send()`

### Troubleshooting

* **403 Errors**: Use a custom RPC URL instead of public endpoints
* **"Account does not exist"**: Ensure all required accounts have been initialized
* **"Invalid arguments"**: Check that byte arrays are properly formatted (0x prefix)
* **Simulation failures**: This playground uses simplified encoding - use official SDKs for production

For production applications, always use the official LayerZero SDKs which provide proper type safety and encoding.
