Aller au contenu principal

Guide Cross-VM

QoreChain exécute EVM, SVM et CosmWasm côte à côte, et le module x/crossvm permet à un seul compte natif d'invoquer un contrat sur n'importe lequel d'entre eux. Le SDK fournit un assistant de haut niveau createCrossVMClient pour construire, signer et diffuser ces appels — y compris en empaquetant plusieurs d'entre eux dans une seule transaction atomique à travers les trois VM — plus des assistants de lecture typés pour suivre l'état des messages. Le routage EVM→natif initié depuis l'intérieur de l'EVM s'exécute toujours sur la chaîne via le précompilé du pont cross-VM dans @qorechain/evm.

Appels cross-VM unifiés

createCrossVMClient encapsule MsgCrossVMCall (URL de type /qorechain.crossvm.v1.MsgCrossVMCall) de sorte que vous n'ayez jamais à construire manuellement un { typeUrl, value } ni à encoder une charge utile vous-même. L'expéditeur du client est l'adresse du TxClient connecté ; sourceVm vaut par défaut "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 est l'un de "evm" | "cosmwasm" | "svm" (également exporté sous forme du tableau VM_TYPES).

Encodage de la charge utile par VM

Choisissez exactement une forme de charge utile par appel :

FormeEncodage
{ payload: Uint8Array | Hex }octets bruts, transmis tels quels
{ evm: { abi, functionName, args } }encodé en ABI avec encodeFunctionData de viem (sélecteur + args)
{ cosmwasm: object }JSON.stringify puis octets UTF-8 (la convention execute-msg de CosmWasm)
{ svm: { data: Uint8Array | Hex } }octets bruts (un blob d'instruction SVM)

Le chemin EVM importe viem de manière paresseuse, de sorte que le pair optionnel viem n'est nécessaire que lorsque vous utilisez réellement une charge utile { evm: ... }.

Construction seule (hors ligne)

buildCall retourne l'EncodeObject sans diffuser — pratique pour l'inspection, le regroupement manuel ou la signature ailleurs. (Les charges utiles EVM sont encodées en ABI de manière asynchrone, donc pour celles-ci utilisez call/callAtomic, ou pré-encodez et passez { payload }.)

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

Transactions atomiques tri-VM

callAtomic empaquette plusieurs messages MsgCrossVMCall dans un seul corps de transaction afin qu'ils s'exécutent atomiquement sous une seule signature — l'argument phare tri-VM. Une seule signature, des appels à travers EVM + SVM + CosmWasm qui aboutissent tous ensemble ou pas du tout :

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: {} } },
]);

Relire un message

getMessage lit un message cross-VM par id, en privilégiant le client de requête typé et en se rabattant sur la méthode JSON-RPC qor_getCrossVMMessage :

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

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

const msg = await reader.getMessage(messageId);

Lire l'état des messages cross-VM

@qorechain/sdk exporte des wrappers REST typés au-dessus de 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);

Ceux-ci retournent des formes typées : CrossVmMessage, CrossVmMessageResponse, PendingCrossVmMessagesResponse et CrossVmParamsResponse.

Vous pouvez également lire un message via l'espace de noms JSON-RPC qor_ :

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

Le précompilé du pont EVM

Le routage EVM→natif s'exécute sur la chaîne via le précompilé du pont cross-VM, exposé dans @qorechain/evm :

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

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

Appelez le précompilé depuis un contrat Solidity (ou via viem) à cette adresse pour router un message vers la couche native, puis suivez son état avec les assistants de lecture ci-dessus. Voir le guide EVM pour la liste complète des précompilés.