Quorlin
Kortana's native contract language: looks like Java, reads like English, publishes an Ethereum ABI.
The shape of a contract
One contract per file, in a `.ql` source file. A contract holds fields, events, an optional constructor and functions. Every function begins with `reads` or `writes`, so you can tell which functions can move money by reading down the left edge of the file.
contract Counter {
number count;
address owner;
event Incremented(address indexed by, number newValue);
constructor {
owner = caller;
count = 0;
}
reads number value() {
return count;
}
writes number increment() {
count = count + 1;
emit Incremented(caller, count);
return count;
}
}Types
Four scalars, and nothing converts between them by itself.
| Quorlin | Holds | ABI type |
|---|---|---|
| number | A whole number, 0 to 2²⁵⁶−1 | uint256 |
| truth | yes or no | bool |
| address | An account | address |
| text | Up to 128 bytes | string |
| list<T> | Up to 64 scalars, never storage | T[] |
| bytes | Up to 256 opaque bytes | bytes |
| bytes4 | A four-byte tag | bytes4 |
reads and writes
`reads` may look at state and may not change it. That is proved when the contract is compiled, and it compiles to a static call which the VM refuses to let write. A `reads` function calling a `writes` one is a compile error — `reads` is a promise about what calling a function does, not about its own statements.
`writes` may change state, emit events and call other `writes` functions.
What surprises people coming from Solidity
There is no `msg.sender` — use `caller`. There is no hexadecimal literal and no address literal at all: an address comes from a parameter, a field, `caller`, or `nobody`. Booleans are `yes` and `no`. Logic is `and`, `or`, `not` rather than `&&`, `||`, `!`.
There is no `else if` — nest instead. Braces are mandatory on both arms of an `if`, so the dangling-else ambiguity does not exist. `require` is a keyword, not a function, so it takes no wrapping parentheses and cannot be shadowed.
Arithmetic is checked by default: `+ - * / %` stop the call on overflow, underflow or divide by zero. Wrapping is available as `+% -% *%` and has to be written deliberately.
The constructor takes no arguments. To configure at deploy time, use a one-shot initialiser guarded by a flag.
Execution context
These are values, not calls — write `caller`, not `caller()`.
| Built-in | Type | Meaning |
|---|---|---|
| caller | address | Who called this function — may be another contract |
| sender | address | The account that signed the transaction |
| sentAmount | number | DNR sent with this call |
| thisContract | address | This contract's own address |
| blockNumber | number | Current block height |
| blockTime | number | Current block's timestamp |
| clock | number | The dPoH sequence — no EVM equivalent |
| nobody | address | The zero address |
| hashPair(a, b) | number | keccak256(a ‖ b) over two 256-bit words |
| hasCode(a) | truth | Whether address a has contract code |
Access control uses caller, not sender
`caller` is whoever made this call, which may be another contract. `sender` is the account that signed the transaction. A check against `sender` can be passed by tricking a user into calling through a contract you control, so access control should almost always use `caller`.
writes setCap(number newCap) {
require caller == owner, "only the owner";
cap = newCap;
}How Studio compiles Quorlin
Quorlin is compiled by `quorlinc`, the native compiler in the Kortana node repository. Studio invokes that binary directly through its own compiler API, so the diagnostics, the ABI and the KVM bytecode you see are what the compiler produced — not a reimplementation.
Studio finds the binary by looking at `QUORLIN_COMPILER_PATH`, then at `KORTANA_BLOCKCHAIN_PATH` and the conventional build outputs of the node repository. If none of those resolve, Quorlin building reports itself unavailable with the reason, and editing, the outline, hover documentation, go-to-definition, completion and structural diagnostics all keep working.
Each compile runs in its own temporary directory with a time limit and a trimmed environment, and the directory is removed afterwards.
Deploying a Quorlin contract
A Quorlin deployment is an ordinary contract-creation transaction carrying the KVM bytecode, so it goes through `eth_sendRawTransaction` exactly as Solidity does. There is no separate KVM transaction surface, and no `kvm_*` RPC family is required.
The constructor takes no arguments, so deployment carries the bytecode alone. Configure at deploy time with a one-shot initialiser guarded by a flag.