Ontology Developer Center
DISCOVERCOMMUNITYSUPPORT
  • Introduction
  • Discover Ontology
  • Getting Started
  • Glossary
  • Decentralized Identity and Data
    • ONT ID
      • Decentralized Identifiers
        • Method Specification for Ontology
        • Method Specification for Ethereum
        • Method Specification for BSC
      • Verifiable Credentials
        • Anonymous Credentials
      • ONT Login
        • Scenarios
        • Protocol Specification
        • Front-end JavaScript SDK
          • Integration and Usage
          • API Reference
        • Front-end UI SDK
          • Integration and Usage
          • API Reference
        • Back-end Go SDK
          • Integration and Usage
          • API Reference
        • Back-end Java SDK
          • Integration and Usage
          • API Reference
      • ONT TAG
        • Workflow
        • API Reference
      • Mercury
      • OScore
    • DDXF
      • Components and Interfaces
      • GREP
      • Overall Scheme
      • Solutions
        • Marketplace
          • Deployment
          • Scenarios
          • SaaS Tenant
          • Java SDK
        • Data Storage
          • Deployment
          • Java SDK
        • Resource Auditor
        • Offline Judge
      • Use Cases
        • E-Shops
  • ONTOLOGY ELEMENTS
    • Smart Contracts
      • Types of smart contracts
    • Token Protocols
    • Consensus Mechanism
    • Ontology Oracle
      • Oracle Process Flow
  • GUIDES & TUTORIALS
    • Development Guides
      • dApp Development
        • Using the dAPI
        • Data Synchronization
      • Smart Contract Development
        • EVM Contract
          • Development Environment and Tools
          • Wallet Setup
          • Contract Development
          • How to Deploy a Smart Contract with GetBlock
        • NeoVM Contract
          • Development tools and environment
          • Launching the IDE
          • Writing and editing program logic
          • Deploying and testing on private net
        • WASM Contract
          • Development Environment
          • Project Initiation - Hello World
          • Creating your own project
          • Development using SmartX
          • Runtime API
          • Contract Fundamentals
          • Inter-contract Interaction
          • Developing Contracts in C++
        • Publish Contract Source Code
    • Integration Guides
      • dApp Integration
        • dAPI Integration
          • Chrome Plugin
          • Mobile wallet dApp
          • QR code mechanism
          • Wake call mechanism
        • Cocos 2D-x
        • Unity 3D applications
      • Mobile Wallet Integration
        • SDK integration
        • dAPI Integration
          • In-wallet applications
          • QR code mechanism
          • Wake call mechanism
        • Stake
      • Using ONT ID
      • Exchange Integration
        • Exchange Docking Guide
        • Exchange API
      • Ontology for dApp Stores
    • EVM & Token Decimals Upgrade
  • ONTOLOGY NODE
    • Abstract
    • Node Deployment
      • Standard Node
      • Rosetta Node
    • Interacting with a Public Node
  • DEVELOPER TOOLS
    • dApp Development Framework
      • Punica CLI
      • Punica boxes
      • Solo Chain
    • IDE
    • APIs
      • HTTP API
        • Restful
        • WebSocket
        • Remote Procedure Call (RPC)
      • Explorer v2 API
        • Block
        • Address
        • Contract
        • Token
        • Transactions
        • ONT ID
        • Summary
        • Node
      • Native Token API
        • ONT Contract API
        • ONG Contract API
      • ONT ID Contract API
      • Web3 API
      • OScore Open API
      • Rosetta Node API
        • Data API
        • Construction API
      • DToken Contract API
      • DDXF
        • Marketplace Contract API
        • Storage API
      • Governance API
    • Digital Wallet
      • Chrome Plugin provider
      • Chrome Plugin dAPI
      • Mobile version provider
      • Mobile version dAPI
    • SDKs
    • Signing Server
      • Installation
      • API reference
  • COMMUNITY
    • Ecosystem Programs
    • Community Libraries
    • Community Events
    • Community Channels
    • Core Contributors
  • SUPPORT
    • FAQ
      • Basic blockchain concepts
      • Ontology Nodes
      • Ontology token protocols
      • Smart contracts
      • SDKs and APIs
    • Contact Us
Powered by GitBook
On this page
  • Cross Contract API Call Using the Runtime Module
  • WASM Contract Invokes a Native Contract
  • WASM Contract Invokes a NeoVM Contract

Was this helpful?

  1. GUIDES & TUTORIALS
  2. Development Guides
  3. Smart Contract Development
  4. WASM Contract

Inter-contract Interaction

Ontology WASM, NeoVM, and native smart contract interaction

Ontology mainnet currently supports three kinds of smart contracts-

Native contract: The contract native to Ontology system, implemented in Golang and deployed in the Genesis block. Native contracts offer quick execution times.

NeoVM contract: A NeoVM contract is run on the NeoVM engine. Certain characteristics of NeoVM smart contract are small contract file size, simple bytecode, and high performance.

WASM contract: WASM contracts support multiple high level languages that can be compiled to bytecode. WASM contracts provide rich functionality, natively support several third-party database. The WASM development community is also very active.

But how do WASM contracts invoke native and NeoVM contracts? Here we illustrate how the mechanism is implemented.

Developers can clone the contract template, edit the lib.rs file, and start testing.

Cross Contract API Call Using the Runtime Module

A general API has been encapsulated in the ontology-wasm-cdt-rust library, which can be used as follows:

pub fn call_contract(addr: &Address, input: &[u8]) -> Option<Vec<u8>>

This method takes two parameters. The addr parameter indicates the target contract address, and the input parameter is the name of the method to be invoked from the target contract and it's parameters. The name and parameters of the function should be correctly serialized. There are a few differences between the serialization process for NeoVM and native contract method and parameters. The details regarding serialization will be specified below.

WASM Contract Invokes a Native Contract

The ontology-wasm-cdt-rust library includes API that can be used to invoke the ONT and ONG contracts. The use ostd::contract::ont; declaration can be used to import it and use it conveniently. An example of implementing an ONT transfer can be referred to below:

use ostd::contract::ont;
...
let (from, to, amount) = source.read().unwrap();
sink.write(ont::transfer(from, to, amount));

The source code for ont::transfer method is as follows:

pub fn transfer(from: &Address, to: &Address, val: U128) -> bool {
    let state = [TransferParam { from: *from, to: *to, amount: val }];
    super::util::transfer_inner(&ONT_CONTRACT_ADDRESS, state.as_ref())
}

The code above clearly illustrates that first an instance of TransferParam type is created, and then an array is defined. This is done to support multi account transfer. Next, the ONT contract address and the array created are passed to util::transfer_inner method. The definition for the util::transfer_inner method is as follows:

pub(crate) fn transfer_inner(
    contract_address: &Address, transfer: &[super::TransferParam],
) -> bool {
    let mut sink = Sink::new(64);
    sink.write_native_varuint(transfer.len() as u64);

    for state in transfer.iter() {
        sink.write_native_address(&state.from);
        sink.write_native_address(&state.to);
        sink.write(u128_to_neo_bytes(state.amount));
    }
    let mut sink_param = Sink::new(64);
    sink_param.write(VERSION);
    sink_param.write("transfer");
    sink_param.write(sink.bytes());
    let res = runtime::call_contract(contract_address, sink_param.bytes());
    if let Some(data) = res {
        if !data.is_empty() {
            return true;
        }
    }
    false
}

The sample code above clearly illustrates that the tool used to serialize the parameters is a Sink instance. Since the parameter to be serialized is an array, the array length is serialized, and the type is converted to U64 array before invoking the sink.write_native_varuint method to carry out serialization. Each element of the array is serialized after the array length serialized. The address is serialized using the sink.write_native_address. The data of U128 type is first converted to bytearray and then serialized. This conversion can be carried out using the u128_to_neo_bytes method.

At this point, the parameters have been serialized. To serialize the method name we first need to create a serialization instance that will be used to serialize the method name. Before the method name is serialized, the version is serialized first. This field is set to 0 by default. Next the method name is serialized and parameters are serialized again. Here, the conditions to invoke a native contract have been fulfilled and the runtime APIs method can be used to invoke the contract.

WASM Contract Invokes a NeoVM Contract

When a NeoVM contract is invoked by a WASM contract, the VmValueEncoder and the VmValueDecoder API can be implemented to transfer the parameters. The ontology-wasm-cdt-rust library supports most commonly used data types, for e.g. &str, &[u8], bool, H256, U128, Address. The contract module encasuplates the neo module and allows developers to use it's corresponding methods to invoke NeoVM contract. Refer to the sample code below:

use ostd::contract::neo;
...
let res = neo::call_contract(&NEO_CONTRACT_ADDR, ("init", ()));
match res {
    Some(res2) => {
        let mut parser = VmValueParser::new(res2.as_slice());
        let r = parser.bool();
        sink.write(r.unwrap_or(false));
    }
    _ => sink.write(false),
}

The neo::call_contract method takes two parameters. The first parameter is the target contract's address, and the second parameter is the name and parameters of the method to be called from the target contract. In the sample code above the method name is init, and the parameter passed is an empty tuple. The return value from the method needs to be serialized using the VmValueParser to obtain the final result.

The neo::call_contract method is defined as follows:

pub fn call_contract<T: crate::abi::VmValueEncoder>(
        contract_address: &Address, param: T,
) -> Option<Vec<u8>> {
    let mut builder = crate::abi::VmValueBuilder::new();
    param.serialize(&mut builder);
    crate::runtime::call_contract(contract_address, &builder.bytes())
}

The code above shows that the neo::call_contract takes two parameters. The first parameter is the target contract address and the second parameter is the method name and and the required parameters. The method name and the parameters must implement the VmValueEncoder API.

The VmValueBuilder method should be used to serialize method name and the parameters instead of using Sink.

The macro function is a powerful feature of the Rust programming language. Macros are used to implement VmValueEncoder and VmValueDecoder for tuple type data. Tuple data ("inti",()) is imported when invoking the method.

PreviousContract FundamentalsNextDeveloping Contracts in C++

Last updated 5 years ago

Was this helpful?