QAN TestNet Docs

Writing a smart contract

Overview of smart contract samples.

Prerequisites

Note:

Smart contract samples

Programming language  
Solidity Note: Since QANplatform is Ethereum and EVM-compatible, you can write smart contracts in the same way as you do on Ethereum or other EVM-compatible blockchains.
TypeScript (TS) Writing a smart contract in TypeScript (TS)
C++ Writing a smart contract in C++
C Writing a smart contract in C
Golang (Go) Writing a smart contract in Golang (Go)
Python Writing a smart contract in Python

Why storage slots must be declared upfront

QVM contracts run inside an isolated sandbox environment. Unlike Solidity contracts, which execute natively on the node and can access storage slots dynamically at runtime, QVM contracts cannot reach outside the sandbox to fetch storage values on demand. All storage slots a contract intends to read must be declared before the transaction is submitted, so the execution layer can fetch and inject them as environment variables prior to execution.

How storage slots map to environment variables

Each storage slot name is prefixed with DB_ when exposed inside the contract.
For example:

Storage Slot Name Environment Variable inside Contract
QVM_INIT_MAXUSER DB_QVM_INIT_MAXUSER
TOTALUSERS DB_TOTALUSERS
USER_CURRENT DB_USER_CURRENT

What happens if slots are not declared

If the -r flag is omitted, qvmctl does not create an AccessList for those slots. The execution layer has no knowledge of which slots to fetch, so all storage reads inside the contract return empty values. This is a silent failure - the contract executes without error but operates on empty state.

How to declare read slots using qvmctl

Use the -r flag followed by a comma-separated list of slot names. Example for a contract method diag that reads three slots:

docker run --rm -v "${PWD}:/ws" qanplatform/qvmctl tx -loglevel debug \
  -rpc "https://rpc-testnet.qanplatform.com" \
  -privkey /ws/privkey.hex \
  -r QVM_INIT_MAXUSER,TOTALUSERS,USER_CURRENT \
  <address> diag

The -r flag instructs qvmctl to: 1. Build an AccessList transaction that includes the specified storage slots 2. Have the execution layer fetch the current values of those slots from on-chain storage 3. Inject them as environment variables (DB_<SLOT_NAME>) before the contract function runs

Best practice

Review every storage read in your contract source code and ensure all slot names are listed in the -r flag when calling qvmctl tx. Any slot not listed will appear as an empty string inside the contract.

Previous

Installing qvmctl

Next

Writing a smart contract in JavaScript (JS)