Detailed architecture documentation for SCAPI project.
SCAPI/
โโโ src/
โ โโโ lib.rs # Library entry point, exports modules
โ โโโ main.rs # CLI application entry point
โ โโโ sc_api.rs # Core API implementation
โ โโโ wasm.rs # WASM bindings and JavaScript interop
โโโ pkg/ # WASM build output (after wasm-pack build)
โ โโโ scapi.js # JavaScript/TypeScript wrapper
โ โโโ scapi_bg.wasm # Compiled WebAssembly binary
โ โโโ scapi.d.ts # TypeScript type definitions
โ โโโ package.json # NPM package metadata
โโโ Cargo.toml # Rust project configuration
โโโ README.md # Main documentation
โโโ EXAMPLES.md # Usage examples
โโโ QUICKSTART.md # Quick start guide
โโโ CONTRIBUTING.md # Contribution guidelines
โโโ CHANGELOG.md # Version history
โโโ LICENSE # MIT License
Purpose: Fluent API for building smart contract queries
Responsibilities:
- Managing binary buffer for input data
- Encoding various data types (u8, u16, u32, u64, i8, i16, i32, i64, f32, f64)
- Supporting arrays and special types (m256i for IDs)
- Converting to base64 for HTTP requests
- Sending requests to RPC endpoint
Key Methods:
pub struct RequestDataBuilder {
contract_index: u32,
input_type: u32,
buffer: Vec<u8>,
little_endian: bool,
}
impl RequestDataBuilder {
pub fn new() -> Self
pub fn set_contract_index(mut self, index: u32) -> Self
pub fn set_input_type(mut self, input_type: u32) -> Self
pub fn add_uint64(mut self, value: u64) -> Self
pub fn to_bytes(&self) -> Vec<u8>
pub async fn send(&self) -> Result<Vec<u8>>
}Data Flow:
Input โ add_* methods โ buffer โ to_bytes() โ send() โ RPC โ Response bytes
Purpose: Declarative decoding of binary contract responses
Responsibilities:
- Reading binary data with correct endianness
- Parsing various data types
- Validating data availability
- Creating structured JSON representation
- Supporting arrays and nested structures
Key Methods:
pub struct ResponseDecoder<'a> {
reader: BinaryReader<'a>,
obj: serde_json::Map<String, serde_json::Value>,
}
impl<'a> ResponseDecoder<'a> {
pub fn new(data: &'a [u8]) -> Self
pub fn u8(mut self, field: &str) -> Result<Self>
pub fn u64(mut self, field: &str) -> Result<Self>
pub fn array_m256i(mut self, field: &str, count: usize) -> Result<Self>
pub fn array_struct_bytes(mut self, field: &str, count: usize, struct_size: usize) -> Result<Self>
pub fn to_value(self) -> serde_json::Value
}Data Flow:
Response bytes โ BinaryReader โ read_* methods โ JSON Map โ to_value() โ JavaScript object
Purpose: Low-level binary data reading
Responsibilities:
- Managing read position in buffer
- Handling Little Endian / Big Endian
- Validating data bounds
- Primitive read operations
Key Methods:
pub struct BinaryReader<'a> {
data: &'a [u8],
position: usize,
pub endianness: Endianness,
}
impl<'a> BinaryReader<'a> {
pub fn new(data: &'a [u8]) -> Self
pub fn read_u8(&mut self) -> Result<u8>
pub fn read_u64(&mut self) -> Result<u64>
pub fn read_bytes(&mut self, len: usize) -> Result<&'a [u8]>
fn ensure_available(&self, len: usize) -> Result<()>
}Purpose: JavaScript/TypeScript interface for WASM module
Responsibilities:
- Exporting Rust functions to JavaScript
- Converting between Rust and JS types
- Handling asynchronous operations (Promises)
- Managing memory between WASM and JS
Key Components:
#[wasm_bindgen]
pub struct RequestDataBuilderHandle {
inner: RequestDataBuilder,
}
#[wasm_bindgen]
pub struct ResponseDecoderHandle {
data: Vec<u8>,
decoder: ResponseDecoder<'static>,
}
#[wasm_bindgen]
impl RequestDataBuilderHandle {
#[wasm_bindgen(constructor)]
pub fn new() -> Self
pub fn set_contract_index(mut self, index: u32) -> Self
pub async fn send(&self) -> Result<Vec<u8>, JsValue>
}JavaScript Integration:
// JavaScript side
const builder = new RequestDataBuilder()
.set_contract_index(16)
.send(); // Returns Promise<Uint8Array>โโโโโโโโโโโโโโโโโโโ
โ JavaScript โ
โ Application โ
โโโโโโโโโโฌโโโโโโโโโ
โ
โ 1. new RequestDataBuilder()
โ .set_contract_index(16)
โ .set_input_type(1)
โ .send()
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WASM Binding Layer โ
โ (wasm.rs) โ
โ โ
โ - RequestDataBuilder โ
โ Handle โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 2. Convert JS params to Rust
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Core API Layer โ
โ (sc_api.rs) โ
โ โ
โ - RequestDataBuilder โ
โ - BinaryWriter โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 3. Build binary payload
โ - Contract index
โ - Input type
โ - Encoded parameters
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HTTP Client โ
โ (reqwest) โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 4. POST to https://rpc.qubic.org
โ Body: base64 encoded payload
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Qubic RPC Endpoint โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 5. Execute view function
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Smart Contract โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 6. Return response bytes
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HTTP Client โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 7. Decode base64 response
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Core API Layer โ
โ โ
โ - ResponseDecoder โ
โ - BinaryReader โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 8. Parse binary response
โ - Read types sequentially
โ - Build JSON object
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WASM Binding Layer โ
โ โ
โ - Convert to JsValue โ
โโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ
โ 9. Return JavaScript object
โผ
โโโโโโโโโโโโโโโโโโโ
โ JavaScript โ
โ Application โ
โโโโโโโโโโโโโโโโโโโ
- Stack-allocated for simple types
- Heap-allocated
Vec<u8>for buffers - Borrowing (
&[u8]) for zero-copy operations - RAII ensures cleanup
- Linear memory shared between JS and WASM
wasm-bindgenhandles marshalling- JavaScript owns returned data
- Rust side cleaned up when handles drop
// โ
Good: zero-copy with borrowing
pub fn new(data: &'a [u8]) -> Self
// โ
Good: move ownership to JavaScript
pub fn to_value(self) -> serde_json::Value
// โ Bad: unnecessary copy
pub fn to_value(&self) -> serde_json::Valuelet request = RequestDataBuilder::new()
.set_contract_index(16)
.set_input_type(1)
.add_uint64(1000);Benefits:
- Fluent, readable API
- Compile-time validation
- Flexible parameter order
let result = ResponseDecoder::new(&bytes)
.u8("field1")
.u64("field2")
.to_value();Benefits:
- Declarative data structure
- Order matches binary layout
- Easy to read and maintain
pub struct ResponseDecoderHandle {
data: Vec<u8>,
decoder: ResponseDecoder<'static>,
}Benefits:
- Safely manages lifetimes across WASM boundary
- Ensures data outlives decoder
- JavaScript-friendly interface
pub fn read_u64(&mut self) -> Result<u64> {
self.ensure_available(8)?;
// ...
}pub fn u64(self, field: &str) -> Result<Self, JsValue> {
ResponseDecoderHandle::map_result(data, decoder.u64(field))
}try {
const result = await builder.send();
} catch (error) {
console.error('Failed:', error);
}โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Contract Index (u32) โ 4 bytes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Input Type (u32) โ 4 bytes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Parameter 1 โ variable
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Parameter 2 โ variable
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Example: GetFees
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ teamFeePercent (u8) โ 1 byte
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ distributionFeePercent โ 1 byte
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ winnerFeePercent (u8) โ 1 byte
โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ burnPercent (u8) โ 1 byte
โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Default: Little Endian (Qubic standard)
- Configurable: via
with_endianness()
// Little Endian: 0x1234 โ [0x34, 0x12]
// Big Endian: 0x1234 โ [0x12, 0x34]ResponseDecoderborrows input data- No unnecessary allocations
- Efficient for large responses
[profile.release]
lto = "thin" # Link-time optimization- Non-blocking HTTP requests
- JavaScript Promise integration
- Tokio runtime for Rust native
- Custom RPC endpoint configuration
- Transaction execution support
- Event subscription
- Contract ABI parsing
- Automatic type generation
- Response caching
- Request batching
- Compression support
- WebSocket support for subscriptions
For more details, see:
Made with โค๏ธ for Qubic ecosystem