Skip to content

wassimans/subxt-ffi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

subxt-ffi

This example shows how to expose a small piece of Subxt functionality, in our case, a single balance-transfer call, as a native C-ABI library, consumable from Python and Node.js.

Overview

  • We want to let non-Rust clients interact with any Substrate-based node (Polkadot in this example) via a tiny FFI layer.
  • Instead of exposing Subxt’s full, Rust-centric API, we build a thin facade crate that:
    1. Calls Subxt under the hood
    2. Exposes just the functions we need via pub extern "C" fn …
  • Client languages (Python, JavaScript, Swift, Kotlin, etc.) load the compiled .so/.dylib/.dll and call these C-ABI functions directly.
flowchart LR
  subgraph Rust side
    subxt[Subxt Public API]
    facade[Facade crate]
    node[Substrate node]
    cabi[C ABI library]
    subxt --> facade
    facade --> node
    facade --> cabi
  end

  subgraph Client side
    swift[Swift client]
    python[Python client]
    kotlin[Kotlin client]
    js[JavaScript client]
    swift --> cabi
    python --> cabi
    kotlin --> cabi
    js --> cabi
  end
Loading

Our one example function is:

pub extern "C" fn do_transfer(dest_hex: *const c_char, amount: u64) -> i32

which does a single balance transfer and returns 0 on success, –1 on error.

Prerequisites

  • Rust toolchain (with cargo)
  • Python 3
  • Node.js (for the JS example)
  • A running Substrate node (Polkadot) on ws://127.0.0.1:8000. We recommend using Chopsticks for a quick local Polkadot node:
npx @acala-network/chopsticks \
  --config=https://raw.githubusercontent.com/AcalaNetwork/chopsticks/master/configs/polkadot.yml
  • In our Python and Javascript files, we introduce a dest variable that represents the destination account for the transfer, we gave it a hard coded value (Bob's account public key) from the running Chopsticks node. Feel free to change it to any other account, or better yet, make it generic!

Building

Build the Rust facade library

cargo build

This will produce a dynamic library in target/debug/ (or target/release/ if you pass --release):

  • macOS: libsubxt_ffi.dylib
  • Linux: libsubxt_ffi.so
  • Windows: subxt_ffi.dll

To run the example files (main.py and main.js) on Windows, just copy the dll file to the folder where main files are.

Running

Python

on macOS / Linux

export LD_LIBRARY_PATH=$PWD/target/debug  # or DYLD_LIBRARY_PATH on macOS
python3 src/main.py

Expected output:

✓ transfer succeeded

Node.js

Install npm dependencies

In the root of the project run:

npm install

then:

export LD_LIBRARY_PATH=$PWD/target/debug   # or DYLD_LIBRARY_PATH on macOS
node src/main.js

Expected output:

✓ transfer succeeded

Development notes

  • Hex handling: We strip an optional 0x prefix and decode into 32 bytes.
  • FFI safety: We only pass pointers and primitive types (u64, i32) across the boundary.
  • Error codes: We return 0 on success, -1 on any kind of failure (decode error, RPC error, etc.).
  • You can extend this facade crate with any additional functions you need—just expose them as pub extern "C" and follow the same pattern.

Limitations

Translating a complex Rust API like Subxt to a bare bones C ABI ready to be consumed by foreign languages has its limitations. Here's a few of them:

  • Complex types (strings, structs) require to design C-safe representations.
  • Only C primitive types (integers, pointers) are FFI-safe; anything else must be translated.
  • Manual memory management glue code is needed if owned data is returned.
  • Needs a manual translation to every foreign language we export to, every time the Rust library changes.

About

Example of exposing some Subxt functionality to other languages through FFI

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published