Zum Hauptinhalt springen

Cross-VM-Leitfaden

QoreChain betreibt EVM, SVM und CosmWasm nebeneinander, und das x/crossvm-Modul ermöglicht es einem einzigen nativen Konto, einen Vertrag auf jeder davon aufzurufen. Das SDK bietet einen High-Level-Helfer createCrossVMClient, um diese Aufrufe zu erstellen, zu signieren und zu senden — einschließlich des Bündelns mehrerer Aufrufe in eine atomare Transaktion über alle drei VMs — plus typisierte Lese-Helfer, um den Nachrichtenzustand zu verfolgen. EVM→native-Routing, das innerhalb der EVM initiiert wird, läuft weiterhin on-chain über das Cross-VM-Bridge-Precompile in @qorechain/evm.

Vereinheitlichte Cross-VM-Aufrufe

createCrossVMClient umschließt MsgCrossVMCall (Type-URL /qorechain.crossvm.v1.MsgCrossVMCall), sodass du niemals ein { typeUrl, value } von Hand baust oder selbst einen Payload kodierst. Der Sender des Clients ist die Adresse des verbundenen TxClient; sourceVm ist standardmäßig "evm".

import { createClient, createCrossVMClient } from "@qorechain/sdk";

const client = createClient({ network: "testnet", endpoints: { rpc, rest } });
const tx = await client.connectTx(signer);
const xvm = createCrossVMClient(tx);

// Call an EVM contract from a native account — the payload is ABI-encoded.
const { messageId, result } = await xvm.call({
sourceVm: "cosmwasm",
targetVm: "evm",
targetContract: "0xToken",
evm: { abi: erc20Abi, functionName: "transfer", args: ["0xRecipient", 1n] },
});

VMType ist eines von "evm" | "cosmwasm" | "svm" (auch als das VM_TYPES-Array exportiert).

Payload-Kodierung pro VM

Wähle genau eine Payload-Form pro Aufruf:

FormKodierung
{ payload: Uint8Array | Hex }rohe Bytes, unverändert durchgereicht
{ evm: { abi, functionName, args } }ABI-kodiert mit viems encodeFunctionData (Selector + Args)
{ cosmwasm: object }JSON.stringify, dann UTF-8-Bytes (die CosmWasm-Execute-Msg-Konvention)
{ svm: { data: Uint8Array | Hex } }rohe Bytes (ein SVM-Instruktions-Blob)

Der EVM-Pfad importiert viem lazy, sodass der optionale viem-Peer nur benötigt wird, wenn du tatsächlich einen { evm: ... }-Payload verwendest.

Nur erstellen (offline)

buildCall gibt das EncodeObject ohne Senden zurück — praktisch für die Inspektion, manuelles Batching oder das Signieren an anderer Stelle. (EVM-Payloads werden asynchron ABI-kodiert, verwende für diese also call/callAtomic, oder kodiere vorab und übergib { payload }.)

const msg = xvm.buildCall({
targetVm: "svm",
targetContract: "Prog...",
svm: { data: new Uint8Array([1, 2, 3]) },
});

Atomare Triple-VM-Transaktionen

callAtomic packt mehrere MsgCrossVMCall-Nachrichten in einen Transaktions-Body, sodass sie atomar unter einer einzigen Signatur ausgeführt werden — die Triple-VM-Schlagzeile. Eine Signatur, Aufrufe über EVM + SVM + CosmWasm, die alle zusammen oder gar nicht landen:

const { messageIds, result } = await xvm.callAtomic([
{
targetVm: "evm",
targetContract: "0xToken",
evm: { abi: erc20Abi, functionName: "transfer", args: ["0xRecipient", 2n] },
},
{ targetVm: "svm", targetContract: "Prog...", svm: { data: instructionBytes } },
{ targetVm: "cosmwasm", targetContract: "qor1c...", cosmwasm: { ping: {} } },
]);

Eine Nachricht zurücklesen

getMessage liest eine Cross-VM-Nachricht anhand der ID, wobei der typisierte Query-Client bevorzugt wird und auf die qor_getCrossVMMessage-JSON-RPC-Methode zurückgegriffen wird:

import { connectQueryClients } from "@qorechain/sdk";

const queries = connectQueryClients(rpcClient);
const reader = createCrossVMClient(tx, { query: queries.crossvm });

const msg = await reader.getMessage(messageId);

Cross-VM-Nachrichtenzustand lesen

@qorechain/sdk exportiert typisierte REST-Wrapper über x/crossvm:

import {
createClient,
getCrossVmMessage,
getPendingCrossVmMessages,
getCrossVmParams,
} from "@qorechain/sdk";

const client = createClient({
endpoints: { rest: "https://rest.testnet.example" },
});

// A single message by id.
const msg = await getCrossVmMessage(client.rest, messageId);

// All pending messages.
const pending = await getPendingCrossVmMessages(client.rest);

// Module parameters.
const params = await getCrossVmParams(client.rest);

Diese geben typisierte Formen zurück: CrossVmMessage, CrossVmMessageResponse, PendingCrossVmMessagesResponse und CrossVmParamsResponse.

Du kannst eine Nachricht auch über den qor_-JSON-RPC-Namensraum lesen:

const m = await client.qor.getCrossVMMessage(messageId);

Das EVM-Bridge-Precompile

EVM→native-Routing wird on-chain über das Cross-VM-Bridge-Precompile ausgeführt, das in @qorechain/evm bereitgestellt wird:

import { PRECOMPILE_ADDRESSES } from "@qorechain/evm";

console.log(PRECOMPILE_ADDRESSES.crossVmBridge);
// 0x0000000000000000000000000000000000000901

Rufe das Precompile aus einem Solidity-Vertrag (oder über viem) unter dieser Adresse auf, um eine Nachricht an die native Schicht zu routen, und verfolge dann ihren Zustand mit den obigen Lese-Helfern. Siehe den EVM-Leitfaden für die vollständige Precompile-Liste.