# Building on Qwai

Everything needed to write a smart contract for the Qwai chain and to build
an app that uses it. This file is written to be read by people and by AI
coding assistants alike: give an assistant this file and the `sdk/` and
`examples/` folders and ask for what you want.

## What Qwai is

- A proof-of-stake layer 1. Blocks are final as soon as they are made.
- Coin: QWAI, six decimals. One QWAI is 1,000,000 smallest units. Amounts
  inside contracts are always whole smallest units in a `u64`.
- Addresses, transaction ids and hashes are 32 bytes, written as 64 hex
  characters. There are no `0x` prefixes.
- Accounts sign with post-quantum keys (ML-DSA-65, optionally also SLH-DSA).
  Apps never handle keys: the Qwail wallet signs.
- Public node: `https://rpc.qwai.io/api`. Explorer: `https://explorer.qwai.io`.

## Contracts

A contract is an ordinary Rust program (`#![no_std]`, `#![no_main]`) compiled
to RISC-V (`riscv32im-unknown-none-elf`). It is not Solidity and there is no
EVM. `counter/` is a complete small one; `examples/` has the real token, NFT
and exchange contracts running on the chain.

### Build

Install Rust from https://rustup.rs, then:

```
./build.sh          # output: build/<name>.elf
```

To start a new contract, copy the `counter` folder, rename it in its
`Cargo.toml`, and add it to `members` in the top `Cargo.toml`.

### How a contract runs

- It runs from the top each time it is called. There are no named functions
  on the chain: the convention is that the first input byte chooses the call
  and the rest are its arguments, fixed-width and little-endian.
- `chain::is_deploy()` is true for the one run when it is first put on the
  chain; do one-time setup there, with the deploy input as its settings.
- Memory: one megabyte, zeroed at the start of every call. No heap; use
  fixed-size arrays.
- Ending normally (or `chain::ret(bytes)`) succeeds. `chain::revert(message)`
  fails the call and undoes everything it did, including coins it was sent.
  A panic also reverts.
- Arithmetic is not checked for overflow in release builds. Use
  `checked_add` and friends, and `u128` for multiply-then-divide.

### What a contract can ask of the chain (`sdk/src/lib.rs`)

| Function | What it does |
|---|---|
| `input(&mut buf) -> usize` | Copies the call's input; returns its full length |
| `ret(&bytes)` / `revert(message)` | Ends the call, successfully or not |
| `caller() -> Address` | Who called: a wallet or another contract |
| `this() -> Address` | This contract's own address |
| `value() -> u64` | Coins sent with this call (already held by the contract) |
| `storage_get(&key, &mut out) -> Option<usize>` | Reads a stored value (keys are 32 bytes) |
| `storage_set(&key, &value)` | Stores up to 4,096 bytes; an empty value deletes |
| `transfer(&to, amount) -> bool` | Sends coins this contract holds. Runs no code at the receiver |
| `balance(&address) -> u64` | Coin balance of any address |
| `emit(&bytes)` | Records an event (up to 1,024 bytes, 64 per transaction) |
| `hash(&bytes) -> [u8; 32]` | BLAKE3. Use it to make storage keys: `hash(tag + owner)` |
| `block() -> (height, time_ms)` | The block this call is in |
| `call(&contract, value, &input, &mut out) -> Option<usize>` | Calls another contract (8 deep at most). `None` means it failed and its effects were undone |
| `code_id(&address) -> [u8; 32]` | Identity of the program at an address (zeros if none). Lets a contract accept only known programs |
| `is_deploy() -> bool` | True during the deploying run |

### Limits and cost

- Gas: 1 per instruction; storage write 20,000 + 50 per byte; storage read
  2,000; call to another contract 30,000; coin transfer 10,000.
- One transaction may use 5,000,000 gas; a block 50,000,000.
- 1,000 gas costs one smallest unit of QWAI. A typical token transfer costs
  about 0.0001 QWAI. Transactions also pay one smallest unit per byte.
- Output up to 4,096 bytes. Programs up to about 120 KB.

### Habits that keep contracts safe

- Check every input length exactly; revert on anything unexpected.
- Refuse coins on calls that do not use them (`if value() != 0 { revert }`),
  or they are stuck in the contract.
- Keep your own count of what the contract holds; never decide anything from
  `balance(this())`, which anyone can change by sending coins.
- Change your stored state before calling another contract, and guard
  against being called back while you are mid-call (see `guarded` in
  `examples/exchange.rs`).
- Round in the contract's favour.
- A contract cannot be changed after it is deployed. Test first.

## Standards

**Tokens** (`examples/token.rs`): `01` transfer(to 32, amount 8), `02`
balance(of 32) -> 8, `03` supply -> 8, `04` name, `05` symbol, `06` decimals
-> 1 byte. Tokens launched on Qwaix all run this exact program.

**NFTs** (`examples/nft.rs`): one collection per contract. `01` transfer(to
32, item 8), `02` owner(item 8) -> 32, `03` supply, `04` name, `05` symbol,
`09` item(item 8) -> owner + description, `0a` mint(to 32, description),
`0b` burn, `0c` balance(owner) -> 8, `0d` owned(owner 32, first 8, count 1)
-> item numbers, `0e` approve(item 8, account 32), `10` take(item 8, to 32).
An item's description is JSON: `{"name","image","about","traits"}`, where
`image` is a `data:image/...;base64,` picture or an `https://` address.
Program id: `6aa367b3c8e72a23f5c006c501ea1ab403753325e34106530150c59edb7c5201`.
Deploying `build`-identical bytes of this program makes a collection that
wallets and explorers recognise automatically.

## Apps

### Reading (no wallet needed)

All `GET` unless noted; replies are JSON. Amounts are text in whole coins.

| Path under `https://rpc.qwai.io/api` | Gives |
|---|---|
| `/status` | Height, supply, validators |
| `/account/<address>` | Balance and next transaction number |
| `/tx/<id>`, `/receipt/<id>` | A transaction and what it did (`ok`, `output`, `events`, `created`) |
| `/address/<address>/transactions?limit=25` | History of an address |
| `/contract/<address>` | Whether a contract is there, its program id |
| `/contracts?program=<program id>` | Every contract running one program |
| `/events?contract=<address>&about=<hex>&limit=100&before=<seq>` | A contract's events, newest first; `about` keeps those whose data, after its first byte, starts with those bytes |
| `POST /query` `{"contract","input"}` | Runs a contract read-only. Free. `input` and `output` are hex |
| `POST /queries` `[{"contract","input"}, ...]` | Up to 25 of those at once |
| `/dex/tokens`, `/dex/token?id=`, `/dex/holders?id=` | Qwaix tokens, one token, its holders |

There are no push updates: ask `/status` every couple of seconds and fetch
what is new. Any website may call the API.

### Writing (through the Qwail wallet)

```js
// Present when the user has Qwail installed.
if (!window.qwail) { /* send them to https://qwail.org */ }

const { address } = await window.qwail.connect();

// Call a contract. `input` is hex: selector byte, then arguments.
// `value` is QWAI to send along, as text in whole coins.
const result = await window.qwail.signAndSend({
  kind: 'call', contract: '<64 hex>', input: '01' + to + u64(amount), value: '0',
});
// Resolves once it is in a block: { id, ok, output, created }.
// Rejects if the user declines or the contract reverts.

// Put a contract on the chain. `code` is the .elf file as hex.
const made = await window.qwail.signAndSend({ kind: 'deploy', code, input });
// made.created is the new contract's address.

// Plain payment.
await window.qwail.signAndSend({ kind: 'transfer', to: '<64 hex>', amount: '2.5' });
```

Encoding helpers every app needs:

```js
// A number as 8 little-endian bytes of hex.
const u64 = (n) => { let v = BigInt(n), s = ''; for (let i = 0; i < 8; i++) { s += (v & 255n).toString(16).padStart(2, '0'); v >>= 8n; } return s; };
// The reverse, from a hex reply.
const readU64 = (hex) => (hex.match(/../g) || []).reduceRight((n, h) => (n << 8n) | BigInt(parseInt(h, 16)), 0n);
```

### A good prompt for an AI assistant

> Using QWAI-DEV-GUIDE.md, the SDK in `sdk/` and the contracts in
> `examples/` as the reference for style and safety, write a Qwai contract
> that [what it should do]. Use the first input byte as the call selector,
> fixed-width little-endian arguments, checked arithmetic, and revert on any
> unexpected input. Then write a single-page site that reads it through
> `POST /api/queries` and sends transactions through `window.qwail`.
