DOCUMENTATION
Protocol specification and developer reference for the VTT blockchain. Covers architecture, consensus, tokenomics, smart contracts, compliance, governance, cross-chain messaging, and the full JSON-RPC API.
PROTOCOL OVERVIEW
Vettor (VTT) is a Layer 1 blockchain designed for tokenizing real-world assets (RWA). It combines protocol-level compliance controls with the permissionless innovation of public blockchains.
The target architecture follows a relay chain + application chains model. The relay chain provides shared security, consensus finality, and cross-chain message routing. Application chains connect to the relay chain for interoperability while maintaining their own state and execution environments.
VTT uses an account-based model (not UTXO). Each account has a balance, nonce (sequence number), and optional contract code. This model is simpler for token management and smart contract interactions.
CRYPTOGRAPHIC PRIMITIVES
ARCHITECTURE DIAGRAM
STORAGE (ROCKSDB)
Chain state and block data are persisted to disk using RocksDB. The --data-dir <path> flag enables persistent storage. Without it, state is held in-memory only (useful for dev/test).
StateDB uses a write-through cache: reads check the in-memory cache first, then fall back to RocksDB. On restart with an existing data directory, the node resumes from the last persisted state.
CONSENSUS (DPOS)
VTT uses Delegated Proof of Stake (DPoS) consensus. Token holders delegate their VTT to validators who produce blocks and validate transactions. The protocol elects the top 21 validators by total stake each epoch.
CONSENSUS PARAMETERS
VALIDATOR ELECTION
At the start of each epoch, the protocol selects the top 21 validators by total stake (self-stake + delegations). In case of a tie in total stake, validators are ordered by their address (lexicographic, ascending). Validators must maintain at least 100,000 VTT in self-stake to be eligible.
Block production follows a round-robin schedule. Each validator gets assigned specific slots within the epoch. If a validator misses their slot, the slot is skipped and the next validator produces on their turn.
SLASHING
| OFFENSE | PENALTY | DETAILS |
|---|---|---|
| Double signing | 5% of stake | Signing two different blocks at the same height. Immediate slash, validator jailed. |
| Downtime (>50%) | 0.1% per epoch | Missing more than 50% of assigned slots in an epoch. Accumulates each epoch. |
BFT FINALITY
Vettor uses a BFT-style finality tracker requiring 2/3+1 validator votes to finalize a block. After producing or importing a block, validators broadcast FinalityVote messages containing the voter address, block hash, block number, and signature.
A block is considered finalized when it receives votes from more than two-thirds of active validators. Finalized blocks cannot be reverted by chain reorganizations.
TOKEN ECONOMICS
The VTT token has 18 decimal places (like Ethereum wei). All amounts in the protocol are stored as unsigned 128-bit integers. For display, 1.0 VTT = 1,000,000,000,000,000,000 raw units (1e18).
SUPPLY & ALLOCATION
| ALLOCATION | % | AMOUNT | NOTES |
|---|---|---|---|
| Validator rewards | 30% | 300,000,000 VTT | Emitted over time as block rewards |
| Ecosystem | 20% | 200,000,000 VTT | Grants, partnerships, integrations |
| Team | 15% | 150,000,000 VTT | 4-year vesting schedule |
| Public sale | 15% | 150,000,000 VTT | Launchpad distribution |
| Foundation | 10% | 100,000,000 VTT | Long-term protocol stewardship |
| Treasury | 10% | 100,000,000 VTT | On-chain governance controlled |
INFLATION & STAKING REWARDS
REWARD DISTRIBUTION
GAS & FEES
Every transaction consumes gas. The total fee is gas_used * gas_price. Gas prices are denominated in gwei (1 gwei = 1e9 raw units). The minimum gas price is 1 gwei.
GAS COST TABLE
| OPERATION | GAS COST | NOTES |
|---|---|---|
| min_gas_price | 1 gwei (1e9) | Minimum accepted gas price per unit |
| base_transfer | 21,000 | Simple VTT transfer (no contract call) |
| cost_per_byte | 16 | Per byte of transaction calldata |
| storage_read | 200 | Read a storage slot |
| storage_write | 5,000 | Write to an existing storage slot |
| storage_write_new | 20,000 | Write to a new (previously empty) slot |
| host_call_base | 100 | Base cost for any host function call |
| transfer | 2,100 | Internal VTT transfer from contract |
| log_base | 375 | Base cost to emit a log event |
| log_per_byte | 8 | Per byte of log data |
| compliance_check | 1,000 | Run a compliance validation |
| asset_mint | 10,000 | Mint new asset tokens |
| asset_transfer | 5,000 | Transfer tokenized asset between accounts |
| oracle_read | 500 | Read an oracle price feed |
FEE CALCULATION EXAMPLE
// Simple transfer
gas_used = 21,000 (base_transfer)
gas_price = 1 gwei = 1,000,000,000 raw units
total_fee = 21,000 * 1,000,000,000 = 21,000,000,000,000 raw units
= 0.000021 VTT
// Of that fee:
// 70% burned = 0.0000147 VTT (removed from supply)
// 30% producer = 0.0000063 VTT (paid to block producer)SMART CONTRACTS
VTT executes smart contracts via a WASM virtual machine powered by Wasmer 5. Contracts are compiled to WebAssembly and deployed on-chain. Any language that compiles to WASM can be used (Rust recommended).
CONTRACT REQUIREMENTS
ARGUMENT PASSING
When calling a contract method, arguments are written into the contract's linear memory starting at offset 0:
HOST FUNCTIONS
Contracts interact with the blockchain through imported host functions. Each call has a base gas cost of 100 plus operation-specific costs.
| FUNCTION | SIGNATURE | DESCRIPTION |
|---|---|---|
| host_storage_read | (key_ptr: i32, key_len: i32, val_ptr: i32, val_max: i32) -> i32 | Read a value from contract storage. Returns bytes written, 0 if not found, -1 on error (buffer too small or out of gas). |
| host_storage_write | (key_ptr, key_len, val_ptr, val_len) -> () | Write a value to contract storage. No return value. Traps on out of gas. |
| host_caller | (no params) -> i64 | Returns first 8 bytes of caller address as i64. Use host_caller_address for the full 20-byte address. |
| host_caller_address | (out_ptr: i32) -> i32 | Writes the full 20-byte caller address to out_ptr. Returns 0 on success, -1 on error. |
| host_block_number | () -> i64 | Returns the current block number. |
| host_block_timestamp | () -> i64 | Returns the current block Unix timestamp (milliseconds). |
| host_chain_id | () -> i32 | Returns the chain ID. |
| host_emit_log | (data_ptr, data_len) -> () | Emit a log event with arbitrary data. |
| host_gas_remaining | () -> i64 | Returns remaining gas for this execution. |
| host_consume_gas | (amount: i64) -> i32 | Manually consume gas (for custom metering). Returns 0 on success, -1 if out of gas. |
DEPLOYMENT & EXECUTION
Contracts are deployed and called through dedicated transaction actions:
| ACTION | FIELDS | DESCRIPTION |
|---|---|---|
| DeployContract | code: Vec<u8>, init_data: Vec<u8> | Deploy WASM bytecode. Contract address derived from BLAKE3(sender + nonce). Code hash stored as BLAKE3 of bytecode. |
| CallContract | contract: Address, method: String, args: Vec<u8>, value: Amount | Invoke an exported method on a deployed contract. Can attach VTT value. |
CONTRACT STORAGE
MINIMAL CONTRACT EXAMPLE (RUST)
#![no_std]
#![no_main]
extern "C" {
fn host_storage_read(key_ptr: *const u8, key_len: i32, val_ptr: *mut u8, val_max: i32) -> i32;
fn host_storage_write(key_ptr: *const u8, key_len: i32, val_ptr: *const u8, val_len: i32);
fn host_emit_log(data_ptr: *const u8, data_len: i32);
}
#[no_mangle]
pub extern "C" fn increment() -> i32 {
let key = b"counter";
let mut buf = [0u8; 8];
unsafe {
// Read current value
let len = host_storage_read(key.as_ptr(), key.len() as i32, buf.as_mut_ptr(), 8);
let current = if len > 0 {
u64::from_le_bytes(buf)
} else {
0
};
// Increment and write back
let next = (current + 1).to_le_bytes();
host_storage_write(key.as_ptr(), key.len() as i32, next.as_ptr(), 8);
// Log the event
let msg = b"incremented";
host_emit_log(msg.as_ptr(), msg.len() as i32);
}
0 // success
}ASSET TOKENIZATION (RWA)
Vettor is a DLT infrastructure layer for the distribution, trading and governance of regulated financial instruments that represent economic rights over real world assets. The platform is explicitly not a substitute for the regulated vehicles that legally hold the underlying assets, nor for the authorized registrars that maintain the legal ledger of ownership.
A Vettor token represents an economic right (cash flow, capital gain, limited voting rights) over a regulated vehicle that holds or controls the underlying asset. Tokens do not grant direct ownership or any real right over the asset itself. Each on-chain asset records its jurisdiction (ISO 3166-1 alpha-2 code) and the legal entity issuing the financial instrument, anchoring the token to its off-chain legal wrapper.
THREE-LAYER ARCHITECTURE
The Vettor model separates three distinct layers. Each must be built on the right foundation; blurring them is the primary source of regulatory and operational risk in tokenization projects.
VEHICLE OPTIONS
The first production deployment targets Italian jurisdiction and the SPV framework introduced by art. 7.1 of Law 130/1999(added by the 2019 Decreto Crescita), which enables securitization of proceeds from real estate. The vehicle issues notes that are natively financial instruments, eligible for DLT registration under Italian Law 52/2023. Alternative vehicles for future deployments include closed-end real estate AIFs managed by authorized SGRs and, for smaller operations (< 25M AUM), Simple Investment Companies (SIS).
REGISTRAR PARTNERSHIP MODEL
In its initial configuration Vettor operates in partnership with entities already authorized by Consob as registrars for the digital circulation of financial instruments. The partner maintains the legal registry and provides the regulated settlement layer; Vettor provides the chain, wallet infrastructure, secondary trading venue and on-chain governance tooling. Each legal transfer on the partner's registry is mirrored by a corresponding token transfer on Vettor, preserving 1:1 alignment between on-chain state and legal ownership.
ONBOARDING FLOW
ASSET CLASSIFICATIONS
ASSET LIFECYCLE
ASSET METADATA
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| jurisdiction | String | ISO 3166-1 alpha-2 code (e.g. IT, LU, CH, US) |
| legal_entity | String | SPV name and registration number holding the underlying asset |
| decimals | u8 | Decimal places for display (max 18) |
| origin_chain | u32 | Chain ID where the asset was originally issued |
| compliance_policy | String | Policy identifier for transfer restrictions |
| valuation_oracle | String | Oracle feed ID for price/valuation data |
| documents | Document[] | Attached legal/regulatory documents |
| metadata_uri | String | IPFS/HTTP URI for extended off-chain metadata |
DOCUMENT TYPES
COMPLIANCE & IDENTITY
VTT supports both permissioned and permissionless chains. Application chains can enforce identity requirements and transfer restrictions at the protocol level.
Identity is managed through an on-chain DID (Decentralized Identifier) system. Authorized issuers (KYC providers, regulators) attach verifiable claims to identities. Asset transfers are validated against both sender and receiver claims before execution.
CLAIM TYPES
| CLAIM | DESCRIPTION |
|---|---|
| KYC | Know Your Customer verification completed |
| AMLCleared | Anti-Money Laundering check passed |
| AccreditedInvestor | Verified accredited investor status (SEC Rule 501) |
| QualifiedPurchaser | Qualified purchaser status for private funds |
| Jurisdiction | Country/region jurisdiction claim (ISO 3166) |
| Custom | Application-specific claim with string label |
CLAIM STRUCTURE
interface IdentityClaim {
claim_type: ClaimType; // KYC | AMLCleared | AccreditedInvestor | ...
issuer: Address; // Address of the claim issuer
subject: Address; // Address the claim is about
issued_at: u64; // Unix timestamp of issuance
expires_at: u64; // Unix timestamp of expiration (0 = no expiry)
revoked: boolean; // Whether the claim has been revoked
data: Vec<u8>; // Optional additional data (e.g. jurisdiction code)
}TRANSFER VALIDATION
When a tokenized asset transfer is submitted, the protocol validates both parties against the asset's compliance policy. The check verifies:
- Sender has required claims (not expired, not revoked)
- Receiver has required claims (not expired, not revoked)
- Neither party is on a deny list
- Transfer amount is within any configured limits
- Asset is in
Activestatus (not frozen/matured/redeemed)
GOVERNANCE
VTT includes on-chain governance allowing stakers to propose and vote on protocol changes. Voting power is proportional to staked VTT. Only staked tokens count for voting.
GOVERNANCE PARAMETERS
PROPOSAL TYPES
| TYPE | DESCRIPTION |
|---|---|
| ParameterChange | Modify a whitelisted protocol parameter. Unknown keys are rejected at proposal creation time. See the full key list below. |
| RegisterChain | Register a new application chain on the relay. On execution a RegisteredChain record is persisted and exposed via vtt_listRegisteredChains. Cross-chain message routing is not yet live — see Multichain notes below. |
| TreasurySpend | Allocate funds from the on-chain treasury to an address |
| ProtocolUpgrade | Schedule a runtime upgrade at a specific block height |
PARAMETERCHANGE WHITELIST
The executor rejects ParameterChange proposals whose key is not in this whitelist (prevents typos from quietly becoming no-op signals). Values are parsed according to the type shown.
| KEY | TYPE | MEANING |
|---|---|---|
| bridge_relayer | Address | Only address allowed to submit BridgeDeposit. |
| treasury_address | Address | Receives burned gas share, drives SetKycApproval + SetAddressJurisdiction. |
| min_gas_price | u128 (raw Amount) | Minimum accepted gas price for transactions. |
| base_transfer_cost | u64 | Baseline gas cost charged for a Transfer. |
| cost_per_byte | u64 | Gas charged per byte of transaction payload. |
| slash_double_sign_bps | u16 (0..=10000) | Penalty in basis points when a double-sign is detected. |
| slash_downtime_bps | u16 (0..=10000) | Penalty in basis points for downtime beyond threshold. |
| downtime_threshold_pct | u8 (0..=100) | % of expected slots a validator can miss before being slashed. |
| unbonding_period_secs | u64 | Seconds a stake remains locked after unbonding. |
| max_holders_per_asset | u32 | Cap on unique non-zero holders per asset (0 = unlimited). |
| jurisdiction_whitelist | CSV ISO 3166-1 α-2 | Only allow AssetTransfers where both parties have a whitelisted country. |
| jurisdiction_blacklist | CSV ISO 3166-1 α-2 | Reject AssetTransfers where either party is in a blacklisted country. |
PROPOSAL LIFECYCLE
ASSET GOVERNANCE
Beyond protocol-level governance, VTT supports per-asset governance allowing token holders of any tokenized asset to propose and vote on actions specific to that asset. Voting power is proportional to token holdings (not staked VTT).
This enables asset-specific decisions such as revenue distribution, issuer changes, and signal votes without requiring protocol-wide governance proposals.
ASSET GOVERNANCE PARAMETERS
PROPOSAL ACTION TYPES
| ACTION | THRESHOLD | DESCRIPTION |
|---|---|---|
| DistributeRevenue | >50% | Distribute VTT revenue to all holders pro-rata. Amount taken from proposer on execution. |
| ChangeIssuer | 67% supermajority | Transfer the issuer role to a new address. Critical action requiring higher threshold. |
| Signal | >50% | Generic text proposal (signal vote). No on-chain execution, used for community sentiment. |
| DisposeAsset | 67% supermajority | Authorize disposition of the underlying asset or wind-down of the regulated vehicle. Freezes the token and marks it Redeemed. Actual sale is executed off-chain by the vehicle's directors following normal legal procedure. |
| FinalizeRedemption | 67% supermajority | Force-close a RedemptionPending asset once the redemption window has elapsed. Transitions the asset to Redeemed; any unclaimed balance in the redemption pool is swept to the treasury so it isn't locked forever. |
TRANSACTION ACTIONS
Asset governance uses four dedicated transaction actions:
| ACTION | FIELDS | DESCRIPTION |
|---|---|---|
| DistributeRevenue | asset_id: H256, total_amount: Amount | Direct distribution by issuer (no vote required) |
| ProposeAssetAction | asset_id: H256, action: AssetProposalAction, description: String | Create a new proposal (only token holders) |
| VoteAssetProposal | proposal_id: H256, vote: Vote (Yes/No/Abstain) | Cast vote weighted by token holdings |
| FinalizeAssetProposal | proposal_id: H256 | Execute a passed proposal after voting ends |
REVENUE DISTRIBUTION
Revenue distribution sends VTT to all holders of a tokenized asset, pro-rata based on their holdings at the time of execution. There are two paths:
- Direct (issuer) — The asset issuer submits a
DistributeRevenuetransaction directly. The amount is deducted from the sender's balance and distributed immediately. - Governed (proposal) — Any token holder proposes a
DistributeRevenueaction viaProposeAssetAction. If the proposal passes quorum and threshold, the distribution executes on finalization.
BLOCK REWARDS
VTT uses an inflation-based reward model to incentivize block production and staking. Block rewards are newly minted tokens distributed each block. The inflation rate is dynamic, targeting a specific staking ratio to balance security with token supply growth.
INFLATION MODEL
EPOCH REWARD CALCULATION
// Epoch reward formula (from vtt-consensus/src/rewards.rs) // // base_rate = 50 per mille (5% annual) // // If staking_ratio == 0%: // adjusted = base_rate × 2 (max incentive) // Else: // adjusted = clamp(60 × base_rate / staking_ratio, base_rate/2, base_rate×2) // // epoch_reward = total_supply × adjusted / (1000 × 8760) // Example at 60% staking (target): // adjusted = 60 × 50 / 60 = 50 // epoch_reward = 1,000,000,000 × 50 / (1000 × 8760) ≈ 5,708 VTT per epoch
BLOCK REWARD SPLIT
GAS FEE SPLIT
The burn mechanism creates deflationary pressure that partially offsets the inflation from block rewards. At high network utilization, the 70% gas burn can exceed new token issuance, making VTT net-deflationary.
BRIDGE SYSTEM
The VTT bridge enables cross-chain asset transfers between the VTT chain and external EVM chains (Ethereum, Base). It uses a lock/mint and burn/release model with a relayer service monitoring events on both sides.
ARCHITECTURE
BRIDGE WITHDRAW (VTT → ETHEREUM)
To move tokens from VTT to an external chain, submit a BridgeWithdraw transaction. This burns the tokens on the VTT chain. The relayer detects the event and calls the bridge contract on the destination chain to release or mint equivalent tokens.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| token | H256 | H256::ZERO for native VTT, or asset ID for tokens (e.g. vUSDT) |
| amount | Amount (u128) | Amount to withdraw (burned on VTT chain) |
| destination_chain | u32 | EVM chain ID (1 = Ethereum mainnet, 8453 = Base, 11155111 = Sepolia) |
| destination_address | Address (20 bytes) | Recipient address on the external EVM chain |
RELAYER FLOW
BRIDGE DEPOSIT (ETHEREUM → VTT)
To move tokens from Ethereum to VTT, the user deposits wVTT (or supported tokens) into the VTTBridge contract on Ethereum. The relayer detects the deposit event and mints equivalent tokens on the VTT chain to the specified recipient address.
CROSS-CHAIN MESSAGING
Vettor's architecture supports cross-chain communication through a relay-mediated messaging protocol. Messages are routed through the relay chain, which verifies inclusion proofs before delivering to the destination chain.
PAYLOAD TYPES
| TYPE | DESCRIPTION |
|---|---|
| VttTransfer | Transfer native VTT tokens to another chain |
| AssetTransfer | Transfer a tokenized asset to another chain |
| DataMessage | Arbitrary data message (for contract-to-contract calls) |
MESSAGE LIFECYCLE
MECHANICS
Each chain maintains an outbox and an inbox. Outgoing messages are collected in the outbox and their Merkle root is included in the block header. The relay chain reads these roots and forwards messages to destination chain inboxes with inclusion proofs.
Validators are assigned to relay duties through a deterministic shuffle per epoch, ensuring fair distribution of cross-chain relay work among the active set.
ORACLE SYSTEM
VTT includes a native oracle module for bringing off-chain data on-chain. Oracles are critical for RWA valuation, compliance checks, and DeFi applications. The system uses quorum-based aggregation with multiple authorized sources.
FEED TYPES
AGGREGATION MECHANICS
DEX / AMM
VTT includes a native Automated Market Maker (AMM) built directly into the protocol layer. It implements a constant-product formula identical to Uniswap v2, giving any application chain access to trustless token swaps without deploying external contracts.
Pools are identified by a deterministic pool_id derived from the BLAKE3 hash of the two sorted token identifiers (H256). This means a given token pair always maps to exactly one pool address — there are no duplicate markets.
CONSTANT PRODUCT FORMULA
Δx in: Δy = y · Δx_after_fee / (x + Δx_after_fee)FEE STRUCTURE
LP TOKENS
When liquidity is added to a pool, the provider receives LP tokens representing their proportional share of the reserves. LP tokens are first-class on-chain assets (same type as tokenized RWA assets) and are transferable.
The first depositor receives sqrt(amount_a * amount_b) LP tokens. The initial 1,000 LP tokens are permanently burned to prevent price manipulation on empty pools. Subsequent depositors receive LP tokens proportional to their contribution relative to total reserves.
LIQUIDITY MINING
REVENUE SHARE (VTT-REV)
VTT-REV is a fixed-supply on-chain asset representing a perpetual claim on protocol fee revenue. Holders receive a pro-rata share of the 0.05% protocol fee collected from every swap across all pools.
vUSDT — NATIVE STABLECOIN
vUSDT is the native stablecoin of the VTT ecosystem. It is a protocol-minted asset pegged 1:1 to USD, used as the primary quote currency in DEX pools and as a settlement medium for RWA transactions.
POOL CREATION
NETWORK
VTT nodes communicate over a peer-to-peer network built on libp2p. The network layer handles peer discovery, block propagation, transaction gossip, and cross-chain relay messages.
NETWORK STACK
BLOCK SYNC PROTOCOL
When a new peer connects, both sides exchange a StatusMessage containing best block number, best block hash, and genesis hash. If a node is behind, it sends a BlockRangeRequest for missing blocks (up to 100 per batch). The peer responds with a BlockRangeResponse containing the requested blocks.
Blocks are imported sequentially and sync continues until the node is caught up. Single block requests (BlockRequest / BlockResponse) are also supported for on-demand block fetching.
PROMETHEUS METRICS
Validators and nodes expose Prometheus-compatible metrics on a configurable HTTP port. Metrics are returned in Prometheus text format on the root endpoint.
API REFERENCE
Complete JSON-RPC 2.0 developer reference. Query chain state, submit transactions, manage tokenized assets, interact with governance, staking, DEX, and more.
API OVERVIEW
The node exposes a JSON-RPC 2.0 API over HTTP POST. Every request follows the standard JSON-RPC envelope:
{
"jsonrpc": "2.0",
"id": 1,
"method": "vtt_methodName",
"params": [...]
}All method names are prefixed with vtt_. Parameters are passed as a JSON array in positional order. Responses return a result field on success or an error object on failure.
Amounts are represented as hex-encoded 128-bit unsigned integers with 18 decimal places. For example, 1.0 VTT = 1000000000000000000 (1e18) = 0xDE0B6B3A7640000 in hex.
Addresses are 20 bytes encoded as 0x + 40 hex characters, derived from the BLAKE3 hash of the Ed25519 public key (last 20 bytes of the 32-byte hash).
RATE LIMITING
The vtt_sendTransaction method is rate-limited to 10 calls per second globally. Exceeding the limit returns JSON-RPC error code -32005 ("Rate limit exceeded"). Other read-only RPC methods are not rate-limited.
CONNECTION
The VTT node listens for JSON-RPC requests on the configured HTTP port. The default endpoint for a local node is:
http://127.0.0.1:9944The VTT web application uses a BFF (Backend-for-Frontend) proxy at /api/rpc which forwards requests to the node. This allows the browser to communicate with the node without CORS issues and enables server-side middleware (rate limiting, caching, etc.).
When building external tools or scripts, point directly to the node endpoint. All requests must be HTTP POST with Content-Type: application/json.
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_chainStatus","params":[]}'CHAIN METHODS
vtt_chainStatusReturns the current status of the chain including height, head hash, validator count, and total stake.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| chain_id | number | Chain identifier |
| height | number | Current block height |
| head_hash | string | Hash of the latest block (0x-prefixed hex) |
| validator_count | number | Number of active validators |
| total_stake | string | Total staked VTT (hex-encoded u128, 18 decimals) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_chainStatus","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"chain_id": 1,
"height": 128456,
"head_hash": "0x9f3a...b2c1",
"validator_count": 21,
"total_stake": "0x56bc75e2d63100000"
}
}vtt_chainHeightReturns the current block height as a plain number. Lightweight alternative to vtt_chainStatus when you only need the height.
No parameters.
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_chainHeight","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": 128456
}BLOCK METHODS
vtt_getBlockRetrieves a block by its hash. Returns null if the block is not found.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| hash | string | Block hash (0x-prefixed, 64 hex chars) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| hash | string | Block hash |
| number | number | Block number (height) |
| parent_hash | string | Hash of the parent block |
| state_root | string | Merkle root of the state trie |
| transactions_root | string | Merkle root of the transactions trie |
| validator | string | Address of the block producer |
| epoch | number | Epoch number |
| slot | number | Slot number within the epoch |
| timestamp | number | Unix timestamp (seconds) |
| gas_limit | number | Maximum gas for the block |
| gas_used | number | Total gas consumed by transactions |
| tx_count | number | Number of transactions in the block |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getBlock","params":["0x9f3a...b2c1"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"hash": "0x9f3a...b2c1",
"number": 128456,
"parent_hash": "0x8e2b...a1d0",
"state_root": "0x1234...5678",
"transactions_root": "0xabcd...ef01",
"validator": "0x0101010101010101010101010101010101010101",
"epoch": 642,
"slot": 3,
"timestamp": 1711900800,
"gas_limit": 30000000,
"gas_used": 1250000,
"tx_count": 15
}
}vtt_getBlockByNumberRetrieves a block by its height number. Returns null if no block exists at that height.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| number | number | Block height (0-indexed) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| hash | string | Block hash |
| number | number | Block number (height) |
| parent_hash | string | Hash of the parent block |
| state_root | string | Merkle root of the state trie |
| transactions_root | string | Merkle root of the transactions trie |
| validator | string | Address of the block producer |
| epoch | number | Epoch number |
| slot | number | Slot number within the epoch |
| timestamp | number | Unix timestamp (seconds) |
| gas_limit | number | Maximum gas for the block |
| gas_used | number | Total gas consumed by transactions |
| tx_count | number | Number of transactions in the block |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getBlockByNumber","params":[128456]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"hash": "0x9f3a...b2c1",
"number": 128456,
"parent_hash": "0x8e2b...a1d0",
"state_root": "0x1234...5678",
"transactions_root": "0xabcd...ef01",
"validator": "0x0101010101010101010101010101010101010101",
"epoch": 642,
"slot": 3,
"timestamp": 1711900800,
"gas_limit": 30000000,
"gas_used": 1250000,
"tx_count": 15
}
}ACCOUNT METHODS
vtt_getBalanceReturns the native VTT balance for an address as a hex-encoded u128 string with 18 decimal places.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| address | string | Account address (0x + 40 hex chars) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getBalance","params":["0x0101010101010101010101010101010101010101"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": "0x56bc75e2d63100000"
}vtt_getAccountReturns full account information including balance, nonce, and whether the account is a smart contract.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| address | string | Account address (0x + 40 hex chars) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| address | string | The account address |
| balance | string | Native VTT balance (hex-encoded u128, 18 decimals) |
| nonce | number | Transaction count / sequence number for replay protection |
| is_contract | boolean | True if this address holds WASM contract code |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getAccount","params":["0x0101010101010101010101010101010101010101"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0x0101010101010101010101010101010101010101",
"balance": "0x56bc75e2d63100000",
"nonce": 42,
"is_contract": false
}
}vtt_getStakingInfoReturns staking information for a validator address, including self-stake, total stake, commission, active status, and list of delegations. Returns null if the address is not a validator.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| address | string | Validator address (0x + 40 hex chars) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| address | string | Validator address |
| self_stake | string | Validator's own stake (hex u128, 18 decimals) |
| total_stake | string | Total delegated + self stake (hex u128, 18 decimals) |
| commission_bps | number | Commission rate in basis points (100 = 1%) |
| active | boolean | Whether the validator is in the active set |
| delegations | DelegationInfo[] | List of delegations to this validator |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getStakingInfo","params":["0x0101010101010101010101010101010101010101"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0x0101010101010101010101010101010101010101",
"self_stake": "0x56bc75e2d63100000",
"total_stake": "0x1b1ae4d6e2ef500000",
"commission_bps": 500,
"active": true,
"delegations": [
{ "delegator": "0xabcd...1234", "amount": "0xDE0B6B3A7640000" },
{ "delegator": "0x5678...ef01", "amount": "0x1BC16D674EC80000" }
]
}
}TRANSACTION METHODS
vtt_sendTransactionSubmits a signed transaction to the mempool. The transaction must be fully serialized using Borsh encoding, signed with Ed25519, and hex-encoded. Returns the transaction hash on success.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| txHex | string | Hex-encoded signed transaction bytes (payload + 64-byte signature + 32-byte public key) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_sendTransaction","params":["0a0b0c...signed_tx_hex"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": "0x7f8a3c...transaction_hash"
}vtt_getTransactionRetrieves a confirmed transaction by its hash. Returns null if not found.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| hash | string | Transaction hash (0x-prefixed, 64 hex chars) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| hash | string | Transaction hash |
| block_number | number | Block that included this transaction |
| from | string | Sender address |
| to | string | null | Recipient address (null for contract deploys) |
| action_type | string | Action type (Transfer, Stake, DeployContract, etc.) |
| amount | string | VTT amount transferred (hex u128, 18 decimals) |
| gas_used | number | Gas consumed by execution |
| status | "success" | "fail" | Execution result |
| timestamp | number | Unix timestamp (seconds) |
| payload_hex | string | Raw Borsh-encoded payload as hex |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getTransaction","params":["0x7f8a3c..."]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"hash": "0x7f8a3c...abcd",
"block_number": 128456,
"from": "0x0101010101010101010101010101010101010101",
"to": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"action_type": "Transfer",
"amount": "0xDE0B6B3A7640000",
"gas_used": 21000,
"status": "success",
"timestamp": 1711900800,
"payload_hex": "01000000..."
}
}vtt_listTransactionsReturns a paginated list of recent transactions across the entire chain, ordered by most recent first.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| page | number | Page number (1-indexed) |
| limit | number | Items per page (max 100) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| items | TransactionInfo[] | Array of transactions for this page |
| total | number | Total number of transactions |
| page | number | Current page number |
| page_size | number | Items per page |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_listTransactions","params":[1, 20]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"items": [
{
"hash": "0x7f8a...",
"block_number": 128456,
"from": "0x0101...0101",
"to": "0xabcd...abcd",
"action_type": "Transfer",
"amount": "0xDE0B6B3A7640000",
"gas_used": 21000,
"status": "success",
"timestamp": 1711900800,
"payload_hex": "01000000..."
}
],
"total": 543210,
"page": 1,
"page_size": 20
}
}vtt_getTransactionsByAddressReturns paginated transactions for a specific address (both sent and received).
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| address | string | Account address (0x + 40 hex chars) |
| page | number | Page number (1-indexed) |
| limit | number | Items per page (max 100) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| items | TransactionInfo[] | Array of transactions for this page |
| total | number | Total transactions involving this address |
| page | number | Current page number |
| page_size | number | Items per page |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getTransactionsByAddress","params":["0x0101010101010101010101010101010101010101", 1, 20]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"items": [ ... ],
"total": 87,
"page": 1,
"page_size": 20
}
}vtt_txPoolSizeReturns the number of transactions currently waiting in the mempool (transaction pool).
No parameters.
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_txPoolSize","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": 42
}VALIDATOR METHODS
vtt_getValidatorsReturns the list of all active validators in the current epoch with their stake and commission info.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| address | string | Validator address |
| total_stake | string | Total stake including delegations (hex u128, 18 decimals) |
| self_stake | string | Validator's own stake (hex u128, 18 decimals) |
| commission_bps | number | Commission rate in basis points (500 = 5%) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getValidators","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"address": "0x0101010101010101010101010101010101010101",
"total_stake": "0x1b1ae4d6e2ef500000",
"self_stake": "0x56bc75e2d63100000",
"commission_bps": 500
},
{
"address": "0x0202020202020202020202020202020202020202",
"total_stake": "0xad78ebc5ac6200000",
"self_stake": "0x56bc75e2d63100000",
"commission_bps": 300
}
]
}ASSET METHODS
vtt_getAssetReturns information about a registered asset (tokenized RWA) by its ID. Returns null if not found.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| id | string | Asset ID (0x-prefixed, 64 hex chars / 32 bytes) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| id | string | Unique asset identifier |
| name | string | Human-readable asset name |
| symbol | string | Token ticker symbol |
| issuer | string | Address of the asset issuer |
| total_supply | string | Total supply (hex u128, using asset decimals) |
| status | string | Asset status (active, frozen, retired) |
| decimals | number | Decimal places for display |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getAsset","params":["0xabc123..."]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0xabc123...",
"name": "Milan Office Tower A",
"symbol": "MOTA",
"issuer": "0x0101010101010101010101010101010101010101",
"total_supply": "0x52b7d2dcc80cd2e4000000",
"status": "active",
"decimals": 18
}
}vtt_getAssetBalanceReturns the balance of a specific asset for a given address, including available and locked amounts.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| assetId | string | Asset ID (0x-prefixed, 64 hex chars) |
| address | string | Owner address (0x + 40 hex chars) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| asset_id | string | The asset ID |
| owner | string | The owner address |
| available | string | Available (unlocked) balance (hex u128) |
| locked | string | Locked balance (e.g. in escrow or vesting) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getAssetBalance","params":["0xabc123...", "0x0101...0101"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"asset_id": "0xabc123...",
"owner": "0x0101010101010101010101010101010101010101",
"available": "0xDE0B6B3A7640000",
"locked": "0x0"
}
}vtt_listAssetsReturns all registered assets on the chain.
No parameters.
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_listAssets","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"id": "0xabc123...",
"name": "Milan Office Tower A",
"symbol": "MOTA",
"issuer": "0x0101...0101",
"total_supply": "0x52b7d2dcc80cd2e4000000",
"status": "active",
"decimals": 18
}
]
}GOVERNANCE METHODS
vtt_listProposalsReturns all governance proposals on the chain, in any status (pending, active, passed, rejected, executed).
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| id | string | Proposal ID (0x-prefixed, 64 hex chars) |
| proposer | string | Address of the proposal creator |
| description | string | Human-readable proposal description |
| action_type | string | Type of governance action |
| status | string | Current status (pending, active, passed, rejected, executed) |
| votes_yes | string | Total yes vote weight (hex u128) |
| votes_no | string | Total no vote weight (hex u128) |
| votes_abstain | string | Total abstain vote weight (hex u128) |
| created_at | number | Creation timestamp (unix seconds) |
| voting_end | number | Voting deadline (unix seconds) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_listProposals","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"id": "0xdeadbeef...",
"proposer": "0x0101...0101",
"description": "Increase max validators to 50",
"action_type": "ParameterChange",
"status": "active",
"votes_yes": "0x56bc75e2d63100000",
"votes_no": "0x0",
"votes_abstain": "0xDE0B6B3A7640000",
"created_at": 1711800000,
"voting_end": 1712404800
}
]
}vtt_getProposalReturns a single governance proposal by its ID. Returns null if not found.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| id | string | Proposal ID (0x-prefixed, 64 hex chars) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getProposal","params":["0xdeadbeef..."]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0xdeadbeef...",
"proposer": "0x0101...0101",
"description": "Increase max validators to 50",
"action_type": "ParameterChange",
"status": "active",
"votes_yes": "0x56bc75e2d63100000",
"votes_no": "0x0",
"votes_abstain": "0xDE0B6B3A7640000",
"created_at": 1711800000,
"voting_end": 1712404800
}
}ASSET GOVERNANCE METHODS
vtt_getAssetProposalsReturns all governance proposals for a specific tokenized asset. Includes active, passed, rejected, and executed proposals.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| assetId | string | Asset ID (0x-prefixed, 64 hex chars / 32 bytes) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| id | string | Proposal ID (0x + 64 hex) |
| asset_id | string | Target asset ID |
| proposer | string | Address of the proposal creator |
| action_type | string | Proposal action (DistributeRevenue, ChangeIssuer, Signal) |
| description | string | Human-readable description |
| status | string | Current status (Active, Passed, Rejected, Executed) |
| votes_yes | string | Total yes vote weight (hex u128) |
| votes_no | string | Total no vote weight (hex u128) |
| votes_abstain | string | Total abstain vote weight (hex u128) |
| voting_end | number | Block number when voting ends |
| created_at | number | Block number when proposal was created |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getAssetProposals","params":["0xabc123..."]}'{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"id": "0xdeadbeef...",
"asset_id": "0xabc123...",
"proposer": "0x0101...0101",
"action_type": "DistributeRevenue",
"description": "Distribute Q1 2026 rental income",
"status": "Active",
"votes_yes": "0x56bc75e2d63100000",
"votes_no": "0x0",
"votes_abstain": "0xDE0B6B3A7640000",
"voting_end": 302400,
"created_at": 100800
}
]
}vtt_getAssetProposalReturns a single asset governance proposal by its proposal ID. Returns null if not found.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| proposalId | string | Proposal ID (0x-prefixed, 64 hex chars) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getAssetProposal","params":["0xdeadbeef..."]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0xdeadbeef...",
"asset_id": "0xabc123...",
"proposer": "0x0101...0101",
"action_type": "DistributeRevenue",
"description": "Distribute Q1 2026 rental income",
"status": "Active",
"votes_yes": "0x56bc75e2d63100000",
"votes_no": "0x0",
"votes_abstain": "0xDE0B6B3A7640000",
"voting_end": 302400,
"created_at": 100800
}
}BRIDGE METHODS
vtt_getBridgeWithdrawalsReturns all bridge withdrawal events. Used by the bridge relayer to monitor pending withdrawals that need to be relayed to external chains.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| tx_hash | string | Transaction hash of the BridgeWithdraw tx on VTT |
| block_number | number | Block that included the withdrawal |
| sender | string | Address that initiated the withdrawal on VTT |
| token | string | Token ID (0x000...0 = native VTT, or asset ID) |
| amount | string | Amount withdrawn/burned (hex u128) |
| destination_chain | number | EVM chain ID (1 = Ethereum, 8453 = Base) |
| destination_address | string | Recipient address on the external chain |
| timestamp | number | Unix timestamp (seconds) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getBridgeWithdrawals","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"tx_hash": "0x7f8a3c...abcd",
"block_number": 128456,
"sender": "0x0101010101010101010101010101010101010101",
"token": "0x0000000000000000000000000000000000000000000000000000000000000000",
"amount": "0xDE0B6B3A7640000",
"destination_chain": 1,
"destination_address": "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
"timestamp": 1711900800
}
]
}CONFIG METHODS
vtt_getConsensusParamsReturns the current consensus parameters of the chain, including epoch length, block time, validator limits, staking requirements, and slashing configuration.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| epoch_length | number | Blocks per epoch (e.g. 1200) |
| block_time_ms | number | Target block time in milliseconds (e.g. 3000) |
| active_validators | number | Maximum active validators per epoch (e.g. 21) |
| min_self_stake | string | Minimum self-stake to be a validator (hex u128, 18 decimals) |
| unbonding_period_secs | number | Unbonding period in seconds |
| slash_double_sign_bps | number | Double-sign slash penalty in basis points |
| slash_downtime_bps | number | Downtime slash penalty in basis points per epoch |
| downtime_threshold_pct | number | Missed slot percentage to trigger downtime slash |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getConsensusParams","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"epoch_length": 1200,
"block_time_ms": 3000,
"active_validators": 21,
"min_self_stake": "0x152D02C7E14AF6800000",
"unbonding_period_secs": 1814400,
"slash_double_sign_bps": 500,
"slash_downtime_bps": 10,
"downtime_threshold_pct": 50
}
}vtt_getGasConfigReturns the current gas configuration of the chain, including minimum gas price and base costs.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| min_gas_price | string | Minimum accepted gas price (hex u128, 18 decimals) |
| base_transfer_cost | number | Gas units for a simple VTT transfer (e.g. 21000) |
| cost_per_byte | number | Gas units per byte of calldata (e.g. 16) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getGasConfig","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"min_gas_price": "0x3B9ACA00",
"base_transfer_cost": 21000,
"cost_per_byte": 16
}
}DEX / SWAP METHODS
vtt_listPoolsReturns all AMM liquidity pools currently registered on-chain. Each pool holds two token reserves and tracks its LP token supply.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| pool_id | H256 | Deterministic pool identifier — BLAKE3(sort(token_a, token_b)) |
| token_a | H256 | First token ID (0x000...0 = native VTT) |
| token_b | H256 | Second token ID |
| reserve_a | string | Reserve of token A (hex u128) |
| reserve_b | string | Reserve of token B (hex u128) |
| lp_supply | string | Total LP tokens minted for this pool (hex u128) |
| fee_bps | number | Swap fee in basis points — always 30 (0.3%) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_listPools","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"pool_id": "0x3a5b8c2f1e9d4a7b6c0f2e1d3a8b9c4d5e6f7a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"token_a": "0x0000000000000000000000000000000000000000000000000000000000000000",
"token_b": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"reserve_a": "0x56bc75e2d63100000",
"reserve_b": "0x1b1ae4d6e2ef500000",
"lp_supply": "0x8ac7230489e80000",
"fee_bps": 30
}
]
}vtt_getPoolReturns details for a single AMM pool by its pool_id (H256). Returns null if the pool does not exist.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| pool_id | H256 | The pool identifier (32-byte hex string) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| pool_id | H256 | Deterministic pool identifier |
| token_a | H256 | First token ID (0x000...0 = native VTT) |
| token_b | H256 | Second token ID |
| reserve_a | string | Reserve of token A (hex u128) |
| reserve_b | string | Reserve of token B (hex u128) |
| lp_supply | string | Total LP tokens minted (hex u128) |
| fee_bps | number | Swap fee in basis points (30) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getPool","params":["0x3a5b8c2f1e9d4a7b6c0f2e1d3a8b9c4d5e6f7a0b1c2d3e4f5a6b7c8d9e0f1a2b"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"pool_id": "0x3a5b8c2f1e9d4a7b6c0f2e1d3a8b9c4d5e6f7a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"token_a": "0x0000000000000000000000000000000000000000000000000000000000000000",
"token_b": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"reserve_a": "0x56bc75e2d63100000",
"reserve_b": "0x1b1ae4d6e2ef500000",
"lp_supply": "0x8ac7230489e80000",
"fee_bps": 30
}
}vtt_getSwapQuoteReturns a price quote for swapping token_in through the given pool. Uses the constant-product formula (x·y=k) with the 0.3% fee applied before the invariant calculation.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| pool_id | H256 | Pool to route the swap through |
| token_in | H256 | Token being sold (0x000...0 = native VTT) |
| amount_in | string | Amount of token_in as a decimal string (raw units, no hex) |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| amount_in | string | Input amount echoed back (hex u128) |
| amount_out | string | Expected output after fees (hex u128) |
| price_impact_bps | number | Price impact in basis points |
| fee | string | Total fee charged (hex u128, 0.3% of amount_in) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getSwapQuote","params":["0x3a5b8c2f1e9d4a7b6c0f2e1d3a8b9c4d5e6f7a0b1c2d3e4f5a6b7c8d9e0f1a2b","0x0000000000000000000000000000000000000000000000000000000000000000","1000000000000000000"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"amount_in": "0xDE0B6B3A7640000",
"amount_out": "0x2B5E3AF16B1880000",
"price_impact_bps": 12,
"fee": "0x2386F26FC10000"
}
}LAUNCHPAD METHODS
vtt_getLaunchpadInfoReturns the current status of the VTT token sale / launchpad, including pricing, supply, and time window.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| active | boolean | Whether the sale is currently active |
| price_per_vtt | string | Price per VTT token in stablecoin (hex u128) |
| total_supply | string | Total tokens allocated for sale (hex u128) |
| sold | string | Tokens already sold (hex u128) |
| cap | string | Hard cap for the sale (hex u128) |
| start_time | number | Sale start timestamp (unix seconds) |
| end_time | number | Sale end timestamp (unix seconds) |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getLaunchpadInfo","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"active": true,
"price_per_vtt": "0x2386F26FC10000",
"total_supply": "0x52b7d2dcc80cd2e4000000",
"sold": "0x1b1ae4d6e2ef500000",
"cap": "0x295be96e64066972000000",
"start_time": 1711800000,
"end_time": 1714392000
}
}NETWORK METHODS
vtt_getNetworkStatsReturns historical network statistics (TPS, gas usage, total stake, block times) for a given time period. Used for charting and monitoring.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| period | string | Time period: "1h", "24h", "7d", or "30d" |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| tps | { timestamp: number; value: number }[] | Transactions per second over time |
| gas_usage | { timestamp: number; value: number }[] | Gas usage per block over time |
| total_stake | { timestamp: number; value: string }[] | Total staked VTT over time (hex u128) |
| block_times | { timestamp: number; value: number }[] | Block production time in milliseconds |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getNetworkStats","params":["24h"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tps": [
{ "timestamp": 1711900000, "value": 45 },
{ "timestamp": 1711900060, "value": 52 }
],
"gas_usage": [
{ "timestamp": 1711900000, "value": 12500000 }
],
"total_stake": [
{ "timestamp": 1711900000, "value": "0x56bc75e2d63100000" }
],
"block_times": [
{ "timestamp": 1711900000, "value": 2000 }
]
}
}vtt_getOracleReturns information about an oracle price feed by its feed ID, including the latest reported value and quorum requirements. Returns null if the feed does not exist.
| NAME | TYPE | DESCRIPTION |
|---|---|---|
| feedId | string | Oracle feed identifier (e.g. 'VTT/USD') |
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| feed_id | string | Feed identifier |
| name | string | Human-readable feed name |
| latest_value | string | null | Latest reported value (null if no data yet) |
| updated_at | number | Last update timestamp (unix seconds) |
| quorum | number | Minimum number of oracle sources required |
| sources | number | Current number of reporting sources |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getOracle","params":["VTT/USD"]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"feed_id": "VTT/USD",
"name": "VTT / US Dollar",
"latest_value": "0.85",
"updated_at": 1711900800,
"quorum": 3,
"sources": 5
}
}vtt_getNodeMetricsReturns current node metrics. Useful for monitoring dashboards and alerting systems.
No parameters.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| block_height | number | Current chain height |
| connected_peers | number | Number of connected P2P peers |
| txpool_size | number | Pending transactions in the mempool |
| blocks_imported | number | Total blocks imported since node start |
| transactions_executed | number | Total transactions executed since node start |
| current_epoch | number | Current consensus epoch number |
| active_validators | number | Number of active validators in the current set |
curl -X POST http://127.0.0.1:9944 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"vtt_getNodeMetrics","params":[]}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"block_height": 84210,
"connected_peers": 12,
"txpool_size": 3,
"blocks_imported": 84210,
"transactions_executed": 152847,
"current_epoch": 70,
"active_validators": 21
}
}TYPES REFERENCE
Complete TypeScript type definitions for all JSON-RPC response objects. These mirror the Rust types defined in vtt-rpc/src/types.rs.
BlockInfo
interface BlockInfo {
hash: string; // Block hash (0x + 64 hex)
number: number; // Block height
parent_hash: string; // Parent block hash
state_root: string; // State trie Merkle root
transactions_root: string; // Tx trie Merkle root
validator: string; // Block producer address (0x + 40 hex)
epoch: number; // Epoch number
slot: number; // Slot within the epoch
timestamp: number; // Unix timestamp (seconds)
gas_limit: number; // Max gas for block
gas_used: number; // Gas consumed
tx_count: number; // Transaction count
}ChainStatus
interface ChainStatus {
chain_id: number; // Chain identifier
height: number; // Current block height
head_hash: string; // Latest block hash
validator_count: number; // Active validator count
total_stake: string; // Total staked VTT (hex u128, 18 decimals)
total_burned: string; // Cumulative VTT burned from gas fees (hex u128)
total_minted: string; // Cumulative VTT minted as block rewards (hex u128)
}AccountInfo
interface AccountInfo {
address: string; // Account address (0x + 40 hex)
balance: string; // VTT balance (hex u128, 18 decimals)
nonce: number; // Transaction sequence number
is_contract: boolean; // True if WASM contract
}ValidatorInfo
interface ValidatorInfo {
address: string; // Validator address
total_stake: string; // Total stake (self + delegations)
self_stake: string; // Validator's own stake
commission_bps: number; // Commission in basis points (500 = 5%)
}StakingInfo
interface StakingInfo {
address: string; // Validator address
self_stake: string; // Own stake (hex u128)
total_stake: string; // Total stake including delegations
commission_bps: number; // Commission (basis points)
active: boolean; // In active validator set
delegations: DelegationInfo[];
}
interface DelegationInfo {
delegator: string; // Delegator address
amount: string; // Delegated amount (hex u128)
}AssetInfo
interface AssetInfo {
id: string; // Asset ID (0x + 64 hex / 32 bytes)
name: string; // Human-readable name
symbol: string; // Token ticker
issuer: string; // Issuer address
total_supply: string; // Total supply (hex u128)
status: string; // "active" | "frozen" | "retired"
decimals: number; // Decimal places
}AssetBalanceInfo
interface AssetBalanceInfo {
asset_id: string; // Asset ID
owner: string; // Owner address
available: string; // Available balance (hex u128)
locked: string; // Locked balance (hex u128)
}TransactionInfo
interface TransactionInfo {
hash: string; // Transaction hash
block_number: number; // Containing block height
from: string; // Sender address
to: string | null; // Recipient (null for deploys)
action_type: string; // "Transfer" | "Stake" | "Swap" | "BridgeWithdraw" | ...
amount: string; // VTT amount (hex u128, 18 decimals)
nonce: number; // Sender's sequence number
gas_price: string; // Gas price per unit (hex u128, 18 decimals)
gas_limit: number; // Maximum gas for this tx
timestamp: number; // Unix timestamp (seconds)
// Swap-specific fields (present only for Swap transactions)
swap_pool_id?: string; // Pool ID (hex, 0x + 64 chars)
swap_token_in?: string; // Token being sold (hex, 0x + 64 chars)
swap_min_out?: string; // Minimum output amount (hex u128)
}PaginatedResult<T>
interface PaginatedResult<T> {
items: T[]; // Items for current page
total: number; // Total item count
page: number; // Current page number
page_size: number; // Items per page
}ProposalInfo
interface ProposalInfo {
id: string; // Proposal ID (0x + 64 hex)
proposer: string; // Creator address
description: string; // Proposal description
action_type: string; // Governance action type
status: string; // "pending" | "active" | "passed" | "rejected" | "executed"
votes_yes: string; // Yes vote weight (hex u128)
votes_no: string; // No vote weight (hex u128)
votes_abstain: string; // Abstain vote weight (hex u128)
created_at: number; // Creation timestamp (unix seconds)
voting_end: number; // Voting deadline (unix seconds)
}SwapPool
interface SwapPool {
id: string; // Pool ID
token_a: string; // First token (asset ID or "VTT")
token_b: string; // Second token
reserve_a: string; // Reserve of token A (hex u128)
reserve_b: string; // Reserve of token B (hex u128)
total_liquidity: string; // Total LP tokens (hex u128)
fee_bps: number; // Swap fee (basis points, 30 = 0.3%)
}SwapQuote
interface SwapQuote {
amount_in: string; // Input amount (hex u128)
amount_out: string; // Expected output (hex u128)
price_impact_bps: number; // Price impact (basis points)
fee: string; // Swap fee (hex u128)
}LaunchpadInfo
interface LaunchpadInfo {
active: boolean; // Sale currently active
price_per_vtt: string; // Price per VTT (hex u128)
total_supply: string; // Tokens allocated for sale (hex u128)
sold: string; // Tokens sold so far (hex u128)
cap: string; // Hard cap (hex u128)
start_time: number; // Sale start (unix seconds)
end_time: number; // Sale end (unix seconds)
}NetworkStats
interface NetworkStats {
tps: { timestamp: number; value: number }[];
gas_usage: { timestamp: number; value: number }[];
total_stake: { timestamp: number; value: string }[];
block_times: { timestamp: number; value: number }[];
}OracleFeedInfo
interface OracleFeedInfo {
feed_id: string; // Feed identifier
name: string; // Human-readable name
latest_value: string | null; // Latest value (null if no data)
updated_at: number; // Last update (unix seconds)
quorum: number; // Min required sources
sources: number; // Current reporting sources
}AssetProposalInfo
interface AssetProposalInfo {
id: string; // Proposal ID (0x + 64 hex)
asset_id: string; // Target asset ID
proposer: string; // Creator address
action_type: string; // "DistributeRevenue" | "ChangeIssuer" | "Signal"
description: string; // Human-readable description
status: string; // "Active" | "Passed" | "Rejected" | "Executed"
votes_yes: string; // Yes vote weight (hex u128, token-weighted)
votes_no: string; // No vote weight (hex u128)
votes_abstain: string; // Abstain vote weight (hex u128)
voting_end: number; // Block number when voting ends
created_at: number; // Block number when created
}BridgeWithdrawalInfo
interface BridgeWithdrawalInfo {
tx_hash: string; // Transaction hash on VTT chain
block_number: number; // Block that included the withdrawal
sender: string; // Sender address on VTT
token: string; // Token ID (0x000...0 = VTT, or asset ID)
amount: string; // Amount burned (hex u128)
destination_chain: number; // EVM chain ID (1, 8453, etc.)
destination_address: string; // Recipient on external chain
timestamp: number; // Unix timestamp (seconds)
}ConsensusParamsRpc
interface ConsensusParamsRpc {
epoch_length: number; // Blocks per epoch
block_time_ms: number; // Target block time (ms)
active_validators: number; // Max active validators
min_self_stake: string; // Min self-stake (hex u128, 18 decimals)
unbonding_period_secs: number; // Unbonding period (seconds)
slash_double_sign_bps: number; // Double-sign slash (bps)
slash_downtime_bps: number; // Downtime slash per epoch (bps)
downtime_threshold_pct: number; // Missed slot % to trigger slash
}GasConfigRpc
interface GasConfigRpc {
min_gas_price: string; // Minimum gas price (hex u128)
base_transfer_cost: number; // Gas for simple transfer
cost_per_byte: number; // Gas per calldata byte
}TRANSACTION GUIDE
Transactions on VTT are serialized using Borsh (Binary Object Representation Serializer for Hashing) and signed with Ed25519. The full signed transaction is the concatenation of the serialized payload, the 64-byte signature, and the 32-byte public key.
PAYLOAD STRUCTURE
Every transaction payload contains these fields, serialized in order:
interface TransactionPayload {
chainId: number; // u32 — prevents replay across chains
nonce: bigint; // u64 — sender's sequence number
gasPrice: Amount; // u128 — price per unit of gas (18 decimals)
gasLimit: bigint; // u64 — maximum gas units
action: TransactionAction; // enum — what the tx does
}
// Amount is a u128 with 18 decimal places
interface Amount {
raw: bigint; // e.g. 1000000000000000000n = 1.0 VTT
}BORSH ENCODING RULES
ACTION TYPES
The action field is a Borsh enum. The first byte is the variant index, followed by the variant-specific fields. Here are all 21 action types:
| INDEX | ACTION | FIELDS |
|---|---|---|
| 0 | Transfer | to: Address (20 bytes), amount: Amount (u128) |
| 1 | DeployContract | code: Vec<u8> (WASM bytecode), initData: Vec<u8> |
| 2 | CallContract | contract: Address (20 bytes), method: String, args: Vec<u8>, value: Amount (u128) |
| 3 | Stake | validator: Address (20 bytes), amount: Amount (u128) |
| 4 | Unstake | validator: Address (20 bytes), amount: Amount (u128) |
| 5 | GovernanceVote | proposalId: H256 (32 bytes), vote: u8 (0=Yes, 1=No, 2=Abstain) |
| 6 | CreateAssetClass | name: String, symbol: String, metadataUri: String, totalSupply: Amount (u128) |
| 7 | AssetTransfer | assetId: H256 (32 bytes), to: Address (20 bytes), amount: Amount (u128) |
| 8 | CrossChainTransfer | destinationChain: u32, to: Address (20 bytes), payload: CrossChainPayload |
| 9 | CreatePool | token_a: H256, token_b: H256, amount_a: Amount, amount_b: Amount |
| 10 | AddLiquidity | pool_id: H256, amount_a: Amount, amount_b: Amount, min_lp: Amount |
| 11 | RemoveLiquidity | pool_id: H256, lp_amount: Amount, min_a: Amount, min_b: Amount |
| 12 | Swap | pool_id: H256, token_in: H256, amount_in: Amount, min_amount_out: Amount |
| 13 | ClaimRevenue | pool_id: H256 |
| 14 | ClaimMiningRewards | pool_id: H256 |
| 15 | DistributeRevenue | asset_id: H256, total_amount: Amount (u128) |
| 16 | ProposeAssetAction | asset_id: H256, action: AssetProposalAction, description: String |
| 17 | VoteAssetProposal | proposal_id: H256, vote: u8 (0=Yes, 1=No, 2=Abstain) |
| 18 | FinalizeAssetProposal | proposal_id: H256 |
| 19 | BridgeWithdraw | token: H256, amount: Amount, destination_chain: u32, destination_address: Address |
| 20 | GovernancePropose | description: String, action_type: String, param_key?: String, param_value?: String, recipient?: Address, amount?: Amount |
| 21 | FreezeAsset | asset_id: H256. Issuer-only. Blocks transfers until unfrozen. |
| 22 | UnfreezeAsset | asset_id: H256. Issuer-only. Restores transferability after FreezeAsset. |
| 23 | SubmitSlashingEvidence | evidence: Vec<u8> — Borsh-serialized DoubleSignEvidence. Any sender can relay; executor verifies and applies a 5% slash idempotently. |
| 24 | FundRedemptionPool | asset_id: H256, amount: Amount. Issuer deposits VTT into the pool for a RedemptionPending asset so holders can claim their pro-rata share. |
| 25 | ClaimRedemption | asset_id: H256. Holder burns their tokens and receives the pro-rata share of the redemption_pool. Last claim transitions asset to Redeemed. |
| 26 | SetKycApproval | address: Address, approved: bool. Treasury/admin-only. Toggles on-chain KYC flag required for transfers of regulated assets. |
| 27 | BridgeDeposit | source_tx_hash: H256, source_chain: u32, recipient: Address, token: H256, amount: Amount. Relayer-only. Credits an external-chain deposit onto VTT with replay protection. |
| 28 | SetAddressJurisdiction | address: Address, country: String (ISO 3166-1 alpha-2). Treasury/admin-only. Stamps the on-chain jurisdiction used by the governance whitelist/blacklist enforcement at AssetTransfer. Empty string clears the mapping. |
| 29 | CreateOracleFeed | feed_id: H256, name: String, feed_type: String ("price:<pair>", "rate:<name>", "asset:<hex>" or custom), authorized_sources: Vec<Address>, quorum: u8, max_staleness_ms: u64. Treasury/admin-only. Registers a new oracle feed. |
| 30 | SubmitOracleValue | feed_id: H256, value: Amount. Sender must be one of the feed's authorized sources. When the quorum is reached the feed's latest value is updated via median aggregation. |
CROSS-CHAIN PAYLOAD VARIANTS
The CrossChainTransfer action contains a nested enum for the cross-chain payload:
| INDEX | VARIANT | FIELDS |
|---|---|---|
| 0 | VttTransfer | amount: Amount (u128) |
| 1 | AssetTransfer | assetId: H256 (32 bytes), amount: Amount (u128) |
| 2 | ContractCall | contract: Address (20 bytes), method: String, args: Vec<u8>, value: Amount (u128) |
SIGNING & SUBMITTING
The signing process:
- Serialize the
TransactionPayloadto Borsh bytes - Sign the payload bytes with your Ed25519 private key (seed) to get a 64-byte signature
- Concatenate:
payload_bytes || signature (64 bytes) || public_key (32 bytes) - Hex-encode the full byte array
- Submit via
vtt_sendTransaction
import { serializeTransactionPayload } from "@/lib/crypto/borsh";
import { signTransaction } from "@/lib/wallet/transaction";
import { generateKeypair } from "@/lib/wallet/keypair";
// 1. Generate or import a keypair
const wallet = await generateKeypair();
console.log("Address:", wallet.addressHex);
// e.g. "0x7a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b"
// 2. Get account nonce
const account = await rpc.getAccount(wallet.addressHex);
const nonce = BigInt(account.nonce);
// 3. Build the payload
const payload = {
chainId: 1,
nonce: nonce,
gasPrice: { raw: 1000000000n }, // 1 gwei
gasLimit: 21000n,
action: {
type: "Transfer" as const,
to: hexToBytes("abcdefabcdefabcdefabcdefabcdefabcdefabcd"),
amount: { raw: 1000000000000000000n }, // 1.0 VTT
},
};
// 4. Sign and encode
const signed = await signTransaction(payload, wallet.seed, wallet.publicKey);
// 5. Submit
const txHash = await rpc.sendTransaction(signed.encodedHex);
console.log("Tx hash:", txHash);ACTION EXAMPLES
const payload = {
chainId: 1,
nonce: 5n,
gasPrice: { raw: 1000000000n },
gasLimit: 50000n,
action: {
type: "Stake" as const,
validator: hexToBytes("0101010101010101010101010101010101010101"),
amount: { raw: 100000000000000000000n }, // 100 VTT
},
};const payload = {
chainId: 1,
nonce: 6n,
gasPrice: { raw: 1000000000n },
gasLimit: 50000n,
action: {
type: "GovernanceVote" as const,
proposalId: hexToBytes("deadbeefdeadbeefdeadbeefdeadbeef" +
"deadbeefdeadbeefdeadbeefdeadbeef"),
vote: 0, // 0 = Yes, 1 = No, 2 = Abstain
},
};const payload = {
chainId: 1,
nonce: 7n,
gasPrice: { raw: 1000000000n },
gasLimit: 200000n,
action: {
type: "CreateAssetClass" as const,
name: "Milan Office Tower A",
symbol: "MOTA",
metadataUri: "ipfs://QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG",
totalSupply: { raw: 100000000000000000000000n }, // 100,000 tokens
},
};const payload = {
chainId: 1,
nonce: 8n,
gasPrice: { raw: 1000000000n },
gasLimit: 50000n,
action: {
type: "AssetTransfer" as const,
assetId: hexToBytes("abc123..." + "0".repeat(38)), // 32 bytes
to: hexToBytes("abcdefabcdefabcdefabcdefabcdefabcdefabcd"),
amount: { raw: 500000000000000000000n }, // 500 tokens
},
};const wasmBytes = new Uint8Array(fs.readFileSync("contract.wasm"));
const initArgs = new TextEncoder().encode('{"owner":"0x0101..."}');
const payload = {
chainId: 1,
nonce: 9n,
gasPrice: { raw: 1000000000n },
gasLimit: 5000000n,
action: {
type: "DeployContract" as const,
code: wasmBytes,
initData: initArgs,
},
};const args = new TextEncoder().encode('{"recipient":"0xabcd...","amount":"1000"}');
const payload = {
chainId: 1,
nonce: 10n,
gasPrice: { raw: 1000000000n },
gasLimit: 500000n,
action: {
type: "CallContract" as const,
contract: hexToBytes("abcdefabcdefabcdefabcdefabcdefabcdefabcd"),
method: "transfer",
args: args,
value: { raw: 0n }, // no VTT sent with call
},
};const payload = {
chainId: 1,
nonce: 11n,
gasPrice: { raw: 1000000000n },
gasLimit: 100000n,
action: {
type: "CrossChainTransfer" as const,
destinationChain: 2,
to: hexToBytes("abcdefabcdefabcdefabcdefabcdefabcdefabcd"),
payload: {
type: "VttTransfer" as const,
amount: { raw: 10000000000000000000n }, // 10 VTT
},
},
};const payload = {
chainId: 1,
nonce: 12n,
gasPrice: { raw: 1000000000n },
gasLimit: 500000n,
action: {
type: "DistributeRevenue" as const,
assetId: hexToBytes("abc123..." + "0".repeat(38)), // 32 bytes
totalAmount: { raw: 10000000000000000000000n }, // 10,000 VTT
},
};const payload = {
chainId: 1,
nonce: 13n,
gasPrice: { raw: 1000000000n },
gasLimit: 200000n,
action: {
type: "ProposeAssetAction" as const,
assetId: hexToBytes("abc123..." + "0".repeat(38)),
action: {
type: "DistributeRevenue" as const,
totalAmount: { raw: 5000000000000000000000n }, // 5,000 VTT
},
description: "Distribute Q1 2026 rental income to all holders",
},
};const payload = {
chainId: 1,
nonce: 14n,
gasPrice: { raw: 1000000000n },
gasLimit: 50000n,
action: {
type: "VoteAssetProposal" as const,
proposalId: hexToBytes("deadbeef..." + "0".repeat(48)), // 32 bytes
vote: 0, // 0 = Yes, 1 = No, 2 = Abstain
},
};const payload = {
chainId: 1,
nonce: 15n,
gasPrice: { raw: 1000000000n },
gasLimit: 100000n,
action: {
type: "BridgeWithdraw" as const,
token: new Uint8Array(32), // H256::ZERO = native VTT
amount: { raw: 5000000000000000000n }, // 5 VTT
destinationChain: 1, // Ethereum mainnet
destinationAddress: hexToBytes("abcdefabcdefabcdefabcdefabcdefabcdefabcd"),
},
};const payload = {
chainId: 1,
nonce: 16n,
gasPrice: { raw: 1000000000n },
gasLimit: 200000n,
action: {
type: "Swap" as const,
poolId: hexToBytes("3a5b8c2f..." + "0".repeat(48)), // 32 bytes
tokenIn: new Uint8Array(32), // H256::ZERO = native VTT
amountIn: { raw: 1000000000000000000n }, // 1 VTT
minAmountOut: { raw: 2800000000000000000n }, // min 2.8 tokens out
},
};WALLET GUIDE
VTT uses Ed25519 for cryptographic signing and BLAKE3 for address derivation. A wallet consists of a 32-byte seed (private key), from which the public key and address are derived.
KEYPAIR STRUCTURE
interface WalletKeypair {
seed: Uint8Array; // 32-byte Ed25519 seed (KEEP SECRET)
publicKey: Uint8Array; // 32-byte Ed25519 public key
address: Uint8Array; // 20-byte VTT address
publicKeyHex: string; // "0x" + 64 hex chars
addressHex: string; // "0x" + 40 hex chars
}ADDRESS DERIVATION
The address derivation process:
- Start with a 32-byte seed (random or imported)
- Derive the Ed25519 public key (32 bytes) from the seed
- Compute BLAKE3(publicKey) to get a 32-byte hash
- Take the last 20 bytes (bytes 12..32) as the address
- Encode as
0x+ 40 hex characters
// Address derivation (from blake3.ts)
import { blake3 } from "@noble/hashes/blake3";
function addressFromPublicKey(publicKey: Uint8Array): Uint8Array {
const hash = blake3(publicKey); // 32-byte BLAKE3 hash
return hash.slice(12, 32); // last 20 bytes
}
// Result: 0x + 40 hex chars = 20-byte addressGENERATE A NEW KEYPAIR
import { generateKeypair } from "@/lib/wallet/keypair";
const wallet = await generateKeypair();
console.log("Seed (KEEP SECRET):", bytesToHex(wallet.seed));
console.log("Public Key:", wallet.publicKeyHex);
console.log("Address:", wallet.addressHex);
// Example output:
// Seed: "a1b2c3d4...64 hex chars"
// Public Key: "0x9f8e7d6c5b4a3...64 hex chars"
// Address: "0x7a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b"IMPORT FROM SEED
import { importFromHexSeed } from "@/lib/wallet/keypair";
// Import from a 64-character hex seed (32 bytes)
const wallet = await importFromHexSeed(
"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
);
console.log("Address:", wallet.addressHex);
// Deterministic: same seed always produces the same addressSIGNING MESSAGES
import { sign, verify, getPublicKey } from "@/lib/crypto/ed25519";
// Sign arbitrary data
const message = new TextEncoder().encode("Hello VTT");
const signature = await sign(message, seed);
// Verify
const publicKey = await getPublicKey(seed);
const valid = await verify(signature, message, publicKey);
console.log("Valid:", valid); // trueCRYPTOGRAPHIC LIBRARIES
VTT uses audited, pure-JavaScript implementations from the @noble family:
ERROR CODES
When an RPC call fails, the response contains an error object with a numeric code and a human-readable message. Standard JSON-RPC 2.0 error codes apply, plus VTT-specific codes.
| CODE | MEANING | DESCRIPTION |
|---|---|---|
| -32700 | Parse error | Invalid JSON in request body |
| -32600 | Invalid request | JSON is valid but not a proper JSON-RPC 2.0 request |
| -32601 | Method not found | The method name does not exist |
| -32602 | Invalid params | Wrong number or type of parameters |
| -32603 | Internal error | Unexpected server-side error |
| -32000 | Transaction rejected | Transaction failed validation (bad signature, low balance, nonce mismatch) |
| -32001 | Nonce too low | Transaction nonce is less than account nonce (replay) |
| -32002 | Insufficient balance | Sender cannot cover amount + gas fees |
| -32003 | Gas limit exceeded | Gas limit exceeds block gas limit |
| -32004 | Invalid signature | Ed25519 signature verification failed |
| -32005 | Pool full | Transaction pool is at capacity, try again later |
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "Nonce too low: expected 43, got 42"
}
}import { VttRpcClient, RpcError } from "@/lib/rpc/client";
const client = new VttRpcClient("http://127.0.0.1:9944");
try {
const result = await client.call("vtt_getBalance", ["0x0101...0101"]);
console.log("Balance:", result);
} catch (err) {
if (err instanceof RpcError) {
console.error(`RPC error ${err.code}: ${err.message}`);
// Handle specific error codes
if (err.code === -32001) {
console.error("Nonce mismatch — re-fetch account nonce");
}
}
}SDK EXAMPLES
JAVASCRIPT / TYPESCRIPT
Full example: query chain status, fetch account info, list validators, get blocks.
// === VTT JSON-RPC Client — JavaScript ===
const RPC_URL = "http://127.0.0.1:9944";
async function rpcCall(method, params = []) {
const res = await fetch(RPC_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
});
const json = await res.json();
if (json.error) throw new Error(`RPC ${json.error.code}: ${json.error.message}`);
return json.result;
}
// Chain status
const status = await rpcCall("vtt_chainStatus");
console.log("Height:", status.height);
console.log("Validators:", status.validator_count);
// Account balance
const balance = await rpcCall("vtt_getBalance", [
"0x0101010101010101010101010101010101010101",
]);
const vttBalance = Number(BigInt(balance)) / 1e18;
console.log("Balance:", vttBalance, "VTT");
// Full account info
const account = await rpcCall("vtt_getAccount", [
"0x0101010101010101010101010101010101010101",
]);
console.log("Nonce:", account.nonce);
console.log("Is contract:", account.is_contract);
// Get block by number
const block = await rpcCall("vtt_getBlockByNumber", [100]);
if (block) {
console.log("Block hash:", block.hash);
console.log("Tx count:", block.tx_count);
console.log("Validator:", block.validator);
}
// List validators
const validators = await rpcCall("vtt_getValidators");
for (const v of validators) {
const stake = Number(BigInt(v.total_stake)) / 1e18;
console.log(`${v.address}: ${stake} VTT staked, ${v.commission_bps / 100}% commission`);
}
// Paginated transactions
const txPage = await rpcCall("vtt_listTransactions", [1, 10]);
console.log(`Showing ${txPage.items.length} of ${txPage.total} transactions`);
for (const tx of txPage.items) {
console.log(`${tx.hash} — ${tx.action_type} — ${tx.status}`);
}
// List assets (RWA tokens)
const assets = await rpcCall("vtt_listAssets");
for (const a of assets) {
console.log(`${a.symbol} (${a.name}) — status: ${a.status}`);
}
// Governance proposals
const proposals = await rpcCall("vtt_listProposals");
for (const p of proposals) {
console.log(`[#${p.id.slice(0, 10)}] ${p.description} — ${p.status}`);
}
// Swap quote
const quote = await rpcCall("vtt_getSwapQuote", [
"VTT",
"0xabc123...",
"0xDE0B6B3A7640000",
]);
console.log("Expected output:", quote.amount_out);
console.log("Price impact:", quote.price_impact_bps, "bps");PYTHON
Full example using the requests library.
# === VTT JSON-RPC Client — Python ===
import requests
import json
RPC_URL = "http://127.0.0.1:9944"
def rpc_call(method: str, params: list = None) -> dict:
"""Send a JSON-RPC 2.0 request to the VTT node."""
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params or [],
}
resp = requests.post(RPC_URL, json=payload)
resp.raise_for_status()
data = resp.json()
if "error" in data:
raise Exception(f"RPC error {data['error']['code']}: {data['error']['message']}")
return data["result"]
# Chain status
status = rpc_call("vtt_chainStatus")
print(f"Height: {status['height']}")
print(f"Validators: {status['validator_count']}")
print(f"Chain ID: {status['chain_id']}")
# Account balance (amounts are hex-encoded u128 with 18 decimals)
balance_hex = rpc_call("vtt_getBalance", [
"0x0101010101010101010101010101010101010101"
])
balance_vtt = int(balance_hex, 16) / 1e18
print(f"Balance: {balance_vtt:.4f} VTT")
# Full account info
account = rpc_call("vtt_getAccount", [
"0x0101010101010101010101010101010101010101"
])
print(f"Nonce: {account['nonce']}")
print(f"Is contract: {account['is_contract']}")
# Get block by number
block = rpc_call("vtt_getBlockByNumber", [100])
if block:
print(f"Block #{block['number']}: {block['hash']}")
print(f" Validator: {block['validator']}")
print(f" Transactions: {block['tx_count']}")
print(f" Gas: {block['gas_used']}/{block['gas_limit']}")
# List validators
validators = rpc_call("vtt_getValidators")
for v in validators:
stake = int(v["total_stake"], 16) / 1e18
commission = v["commission_bps"] / 100
print(f"{v['address']}: {stake:.2f} VTT staked, {commission}% commission")
# Paginated transaction list
tx_page = rpc_call("vtt_listTransactions", [1, 10])
print(f"\nShowing {len(tx_page['items'])} of {tx_page['total']} transactions")
for tx in tx_page["items"]:
amount = int(tx["amount"], 16) / 1e18
print(f" {tx['hash'][:16]}... {tx['action_type']} {amount:.4f} VTT [{tx['status']}]")
# Staking info for a validator
staking = rpc_call("vtt_getStakingInfo", [
"0x0101010101010101010101010101010101010101"
])
if staking:
print(f"\nValidator {staking['address']}")
print(f" Active: {staking['active']}")
print(f" Self stake: {int(staking['self_stake'], 16) / 1e18:.2f} VTT")
print(f" Total stake: {int(staking['total_stake'], 16) / 1e18:.2f} VTT")
print(f" Delegations: {len(staking['delegations'])}")
# List assets (tokenized RWAs)
assets = rpc_call("vtt_listAssets")
for a in assets:
supply = int(a["total_supply"], 16) / (10 ** a["decimals"])
print(f"{a['symbol']} ({a['name']}): {supply:.2f} total supply — {a['status']}")
# Governance proposals
proposals = rpc_call("vtt_listProposals")
for p in proposals:
print(f"Proposal {p['id'][:16]}... — {p['status']}")
print(f" {p['description']}")
yes = int(p["votes_yes"], 16) / 1e18
no = int(p["votes_no"], 16) / 1e18
print(f" Votes: {yes:.2f} yes / {no:.2f} no")
# DEX swap pools
pools = rpc_call("vtt_listPools")
for pool in pools:
ra = int(pool["reserve_a"], 16) / 1e18
rb = int(pool["reserve_b"], 16) / 1e18
print(f"Pool {pool['id'][:16]}... {pool['token_a']}/{pool['token_b']}")
print(f" Reserves: {ra:.2f} / {rb:.2f}, Fee: {pool['fee_bps']}bps")
# Swap quote
quote = rpc_call("vtt_getSwapQuote", [
"VTT", "0xabc123...", "0xDE0B6B3A7640000"
])
out_amount = int(quote["amount_out"], 16) / 1e18
print(f"Swap quote: {out_amount:.4f} out, impact: {quote['price_impact_bps']}bps")
# Launchpad info
launchpad = rpc_call("vtt_getLaunchpadInfo")
print(f"\nLaunchpad active: {launchpad['active']}")
sold = int(launchpad["sold"], 16) / 1e18
total = int(launchpad["total_supply"], 16) / 1e18
print(f" Sold: {sold:.2f} / {total:.2f} VTT")
# Network stats
stats = rpc_call("vtt_getNetworkStats", ["24h"])
if stats["tps"]:
latest_tps = stats["tps"][-1]["value"]
print(f"\nLatest TPS: {latest_tps}")
if stats["block_times"]:
latest_bt = stats["block_times"][-1]["value"]
print(f"Latest block time: {latest_bt}ms")
# Oracle feed
oracle = rpc_call("vtt_getOracle", ["VTT/USD"])
if oracle:
print(f"\n{oracle['name']}: {oracle['latest_value']}")
print(f" Sources: {oracle['sources']}/{oracle['quorum']} quorum")CLI TOOLS
The Vettor platform ships three command-line binaries: vtt-validator (block-producing node), vtt-node (non-producing full node), and vtt-cli (client tool for querying and transacting).
VTT-VALIDATOR
vtt-validator [FLAGS] --rpc-port <port> RPC server port (default: 9944) --port <port> P2P network port (default: 30333) --seed <hex> Validator private key (64 hex chars) --genesis <path> Path to genesis JSON file --data-dir <path> Path to RocksDB data directory --bootnodes <addrs> Comma-separated bootnode multiaddresses --metrics-port <port> Prometheus metrics port (default: 9615) --testnet Use built-in testnet genesis config --dev Use built-in dev genesis config Environment: VALIDATOR_SEED (fallback if --seed not provided)
VTT-NODE
Same flags as vtt-validator, but does not produce blocks. Used for RPC endpoints, indexers, and full-node infrastructure.
vtt-node [FLAGS] --rpc-port <port> RPC server port (default: 9944) --port <port> P2P network port (default: 30333) --genesis <path> Path to genesis JSON file --data-dir <path> Path to RocksDB data directory --bootnodes <addrs> Comma-separated bootnode multiaddresses --metrics-port <port> Prometheus metrics port (default: 9615) --testnet Use built-in testnet genesis config --dev Use built-in dev genesis config
VTT-CLI
vtt-cli <command> [OPTIONS] keygen [--seed <hex>] Generate or derive a keypair balance <address> Query account balance account <address> Query full account info block <hash|number> Query block by hash or number status Query chain status validators List active validators genesis [--testnet] Export genesis JSON stake --validator <addr> --amount <vtt> --seed <hex> Stake VTT unstake --validator <addr> --amount <vtt> --seed <hex> Unstake VTT Global: --rpc <url> (default: http://127.0.0.1:9944)
START BUILDING
Launch the explorer to browse live chain data, or open the dApp to interact with staking, governance, assets, and DEX.