VS Code extension providing language support for Qubic Smart Contracts written with the Qubic Public Interface (QPI).
Compatibility: Tested against qubic/core v1.286.0 (Epoch 208, released 2026-04-08). The QPI API coverage in this extension reflects
src/contract_core/qpi.hat that release. If Qubic core has been updated since then, check GitHub Issue #5 for the latest sync status.
- QPI control macros highlighted as keywords (
PUBLIC_PROCEDURE,BEGIN_EPOCH, etc.) - QPI built-in types styled as storage types (
id,sint64,uint64,Array, etc.) - QPI API calls styled as support functions (
qpi.transfer,qpi.K12, etc.) - Raw
#includedirectives flagged as invalid (except#include "qpi.h"/<qpi.h>for local IntelliSense) - Raw
/and%operators flagged as illegal (usediv/mod)
| Prefix | Description |
|---|---|
qpi-contract |
Full contract skeleton with state, I/O structs, epoch/tick hooks, and registration |
qpi-contract-doc |
JSDoc metadata block (@contract, @description, @procedure, etc.) for AI agents |
qpi-procedure |
PUBLIC_PROCEDURE block |
qpi-function |
PUBLIC_FUNCTION block |
qpi-procedure-locals |
PUBLIC_PROCEDURE_WITH_LOCALS block |
qpi-function-locals |
PUBLIC_FUNCTION_WITH_LOCALS block |
qpi-epoch |
BEGIN_EPOCH / END_EPOCH block |
qpi-tick |
BEGIN_TICK / END_TICK block |
Snippets are available in both qpi and cpp language modes.
The extension analyses .h files that inherit from ContractBase and applies the QPI C++ restrictions (no stack locals except via _WITH_LOCALS macros, no raw pointers/[], no # except optional qpi.h include for IDE use, no float/double, use div/mod instead of //%, no string/char literals, no ..., no __, limited ::, no union, controlled typedef/using, QPI integer types only, etc.):
| Code | Severity | Rule |
|---|---|---|
QPI001 |
Warning / Error | Warning: #include "qpi.h" / <qpi.h> only (IDE helper — remove before deploy). Error: any other # line (other includes, #define, etc.) |
QPI002 |
Error | / operator prohibited — use div(a, b) |
QPI003 |
Error | % operator prohibited — use mod(a, b) |
QPI004 |
Error | String literals (double quotes) prohibited |
QPI005 |
Error | Character literals (single quotes) prohibited |
QPI006 |
Error | [ and ] prohibited |
QPI007 |
Error | ... (variadic / parameter packs) prohibited |
QPI008 |
Warning | :: only for types/namespaces in this contract or QPI from qpi.h |
QPI009 |
Error | * except for multiplication (no pointers) |
QPI010 |
Error | BEGIN_EPOCH without matching END_EPOCH |
QPI011 |
Error | BEGIN_TICK without matching END_TICK |
QPI012 |
Warning | PUBLIC_PROCEDURE / PUBLIC_FUNCTION not registered |
QPI013 |
Error | __ (double underscore) prohibited |
QPI014 |
Error | float, double, union, const_cast, QpiContext prohibited |
QPI015 |
Error | Native C/C++ int / char / short / long / bool / signed / unsigned — use QPI types |
QPI016 |
Error | typedef / using only in local scope; using namespace QPI allowed at file scope |
QPI017 |
Warning | @procedure / @function declared in doc-comment but not found in contract body |
The linter and validator run on file open, save, and every keystroke.
Command palette: Qubic: New Smart Contract
Prompts for a contract name, generates a .h file with a complete QPI skeleton, and opens it in the editor.
Search for "Qubic QPI Language Support" in the VS Code Extensions panel (Ctrl+Shift+X) or install directly from the Marketplace.
Open the Command Palette (Ctrl+Shift+P) and run:
Qubic: New Smart Contract
Enter a contract name (letters, digits, underscores). The extension creates a ready-to-use .h file with a complete QPI skeleton and opens it in the editor.
While editing any .h file that contains QPI keywords, type qpi. to get an autocomplete list of all available API methods. Each entry shows the full signature, return type, and a description. Tab stops let you fill in arguments quickly.
Hover over any qpi.* call or QPI keyword (PUBLIC_PROCEDURE, BEGIN_EPOCH, etc.) to see inline documentation — signature, return type, and a usage description — without leaving the editor.
Type one of the snippet prefixes and press Tab:
| Prefix | What it inserts |
|---|---|
qpi-contract |
Complete contract skeleton |
qpi-contract-doc |
JSDoc metadata block for AI coding agents |
qpi-procedure |
PUBLIC_PROCEDURE block |
qpi-function |
PUBLIC_FUNCTION block |
qpi-procedure-locals |
PUBLIC_PROCEDURE_WITH_LOCALS block |
qpi-function-locals |
PUBLIC_FUNCTION_WITH_LOCALS block |
qpi-epoch |
BEGIN_EPOCH / END_EPOCH block |
qpi-tick |
BEGIN_TICK / END_TICK block |
Snippets work in both qpi and cpp language modes.
The linter activates automatically for .h files that contain QPI keywords. Problems appear in the Problems panel (Ctrl+Shift+M) and as coloured underlines in the editor:
| Code | Colour | What to do |
|---|---|---|
QPI001 |
Yellow (Warning) for #include qpi.h only; red (Error) for any other # |
Remove all # before deploy; qpi.h is warning-level as a dev-only include |
QPI002 |
Red (Error) | Replace / with div(a, b) |
QPI003 |
Red (Error) | Replace % with mod(a, b) |
QPI004–QPI009, QPI013–QPI016 |
Red (Error) | Match the restriction named in the Problems panel message |
QPI008 |
Yellow (Warning) | Use :: only for contract types or QPI::… from qpi.h |
QPI010 |
Red (Error) | Add missing END_EPOCH after BEGIN_EPOCH |
QPI011 |
Red (Error) | Add missing END_TICK after BEGIN_TICK |
QPI012 |
Yellow (Warning) | Register the procedure/function (see Section 7) |
The Contract Validator checks the overall structure of a QPI contract and catches mistakes that the line-level linter cannot see.
QPI010 — Missing END_EPOCH
Every BEGIN_EPOCH block must be closed with END_EPOCH. If the closing macro is absent the contract will not compile inside the Qubic node.
// Wrong — triggers QPI010
BEGIN_EPOCH
{
}
// END_EPOCH ← missing
// Correct
BEGIN_EPOCH
{
}
END_EPOCHQPI011 — Missing END_TICK
Same rule as QPI010, but for tick hooks.
// Correct
BEGIN_TICK
{
}
END_TICKQPI012 — Unregistered procedure or function
Every PUBLIC_PROCEDURE and PUBLIC_FUNCTION you declare must be listed inside the REGISTER_USER_FUNCTIONS_AND_PROCEDURES block. Omitting the registration means the Qubic network will never call your entry point.
// Declaration (triggers QPI012 if not registered below)
PUBLIC_PROCEDURE(Transfer)
{
// ...
}
// Registration block — Transfer must appear here
REGISTER_USER_FUNCTIONS_AND_PROCEDURES
{
REGISTER_USER_PROCEDURE(Transfer, 1); // ← required
}The index (1, 2, …) is the call index used by clients to invoke the entry point. Each entry point needs a unique index.
Add a JSDoc-style block directly above your contract struct to document it for AI coding agents (Copilot, Cursor, Claude, etc.) and developers:
/**
* @contract TransferContract
* @description Handles peer-to-peer QU transfers with optional escrow
* @author AndyQus
* @version 1
* @procedure Transfer — Transfers QU from caller to recipient
* @procedure Escrow — Holds QU in escrow until release condition is met
* @function GetBalance — Returns current contract balance
* @state transferCount — Number of successful transfers
*/
struct TransferContract : public ContractBase {Use the qpi-contract-doc snippet to insert a pre-filled template.
Hover documentation: Hovering over the contract struct name shows the full metadata inline.
QPI017 validation: A Warning is emitted if a @procedure or @function is declared in the doc-comment but the corresponding PUBLIC_PROCEDURE(X) / PUBLIC_FUNCTION(X) is not found in the contract body.
All preprocessor directives are prohibited in deployed contracts. For local development you may add #include "qpi.h" or #include <qpi.h> so IntelliSense understands QPI types; remove every # line before the contract is deployed. Use QPI built-in types and the qpi API object exclusively.
The / and % operators are prohibited in QPI contracts (e.g. division by zero can yield inconsistent state). Always use:
div(dividend, divisor)instead ofa / b(returns zero if the divisor is zero)mod(dividend, divisor)instead ofa % b(returns zero if the divisor is zero)
The linter flags raw / and % but does not flag div() or mod() — they are the required QPI idioms.
Identity & Context
| Method | Description |
|---|---|
invocator() |
Returns the id of the direct caller of this contract invocation. |
originator() |
Returns the id of the originator of the transaction (the entity that signed it). |
arbitrator() |
Returns the id of the current arbitrator. |
computor(index: uint16) |
Returns the id of the computor at the given index (0–675). |
isContractId(id: id) |
Returns 1 if the given id belongs to a smart contract. |
Time
| Method | Description |
|---|---|
tick() |
Returns the current tick number of the Qubic network. |
epoch() |
Returns the current epoch number of the Qubic network. |
year() |
Returns the current UTC year offset (e.g. 25 for 2025). |
month() |
Returns the current UTC month (1–12). |
day() |
Returns the current UTC day of month (1–31). |
hour() |
Returns the current UTC hour (0–23). |
minute() |
Returns the current UTC minute (0–59). |
second() |
Returns the current UTC second (0–59). |
millisecond() |
Returns the current UTC millisecond (0–999). |
dayOfWeek(year: uint8, month: uint8, day: uint8) |
Returns the day of week for the given date (0 = Wednesday, ..., 6 = Tuesday). |
now() |
Returns the current date and time as a DateAndTime struct. |
Balance & Transfer
| Method | Description |
|---|---|
transfer(dest: id, amount: sint64) |
Transfers amount QU from this contract to dest. Returns false if the balance is insufficient. |
burn(amount: sint64, contractIndexBurnedFor: uint32 = 0) |
Burns amount QU permanently — removes them from circulation. Optional contractIndexBurnedFor defaults to 0 (this contract). Returns the amount burned or a negative value on error. |
invocationReward() |
Returns the amount of QU sent with the current invocation. |
queryFeeReserve(contractIndex: uint32) |
Returns the fee reserve of the contract at contractIndex (0 = this contract). |
distributeDividends(amountPerShare: sint64) |
Distributes amountPerShare QU to every shareholder of this contract. Returns 1 on success. |
Hashing & Crypto
| Method | Description |
|---|---|
K12(data: T) |
Computes the Qubic K12 hash of data and returns the result as an id. |
signatureValidity(entity: id, digest: id, signature: Array<sint8, 64>) |
Returns 1 if the signature is valid for the given entity and digest. |
getPrevSpectrumDigest() |
Returns the spectrum digest of the previous tick. |
getPrevUniverseDigest() |
Returns the universe digest of the previous tick. |
getPrevComputerDigest() |
Returns the computer digest of the previous tick. |
Spectrum / Entities
| Method | Description |
|---|---|
getEntity(id: id, entity: Entity) |
Fills entity with the spectrum entry for the given id. Returns 1 if found, 0 otherwise. |
nextId(currentId: id) |
Returns the next id in the spectrum after currentId. |
prevId(currentId: id) |
Returns the previous id in the spectrum before currentId. |
numberOfTickTransactions() |
Returns the number of transactions in the current tick. |
Assets
| Method | Description |
|---|---|
issueAsset(name: uint64, numberOfDecimalPlaces: sint8, numberOfShares: sint64, unitOfMeasurement: uint64) |
Issues a new asset on the Qubic network. Returns the number of issued shares, or a negative value on failure. |
transferShareOwnershipAndPossession(assetName: uint64, issuer: id, owner: id, possessor: id, numberOfShares: sint64, newOwnerAndPossessor: id) |
Transfers ownership and possession of numberOfShares asset shares from owner/possessor to newOwnerAndPossessor. Returns the number of transferred shares. |
numberOfPossessedShares(assetName: uint64, issuer: id, owner: id, possessor: id, ownershipManagingContractIndex: uint32, possessionManagingContractIndex: uint32) |
Returns the number of possessed shares matching all specified criteria. |
numberOfShares(assetName: uint64, issuer: id) |
Returns the total number of issued shares for the given asset. |
isAssetIssued(issuer: id, assetName: uint64) |
Returns 1 if the asset has been issued by the given issuer, 0 otherwise. |
acquireShares(assetName: uint64, issuer: id, owner: id, possessor: id, numberOfShares: sint64, acquirerContractIndex: uint32) |
Acquires shares into the contract. Returns the number of shares acquired, or a negative value on error. |
releaseShares(assetName: uint64, issuer: id, owner: id, possessor: id, numberOfShares: sint64, releaserContractIndex: uint32) |
Releases shares from the contract. Returns the number of shares released, or a negative value on error. |
bidInIPO(ipoContractIndex: uint32, price: sint64, quantity: uint32) |
Places an IPO bid. Returns the bid index or a negative value on error. |
ipoBidId(ipoContractIndex: uint32, ipoBidIndex: uint32) |
Returns the id of the bidder at the given IPO bid index. |
ipoBidPrice(ipoContractIndex: uint32, ipoBidIndex: uint32) |
Returns the bid price at the given IPO bid index. |
Common types used in QPI contracts:
| Type | Description |
|---|---|
Array<T, L> |
Fixed-size array — use instead of C++ arrays |
HashMap<K, V, L> |
Hash map with key K, value V, capacity L |
HashSet<K, L> |
Hash set with key K, capacity L |
Collection<T, L> |
Ordered collection with capacity L |
ContractState<T, N> |
Persistent contract state storage |
Entity |
Spectrum entry (public key + balance info) |
Asset |
Asset descriptor (issuer id + asset name) |
DateAndTime |
Date/time struct returned by qpi.now() |
NoData |
Empty struct for procedures/functions with no I/O |
id |
256-bit identity / address |
uint8 / uint16 / uint32 / uint64 / uint128 |
Unsigned integer types |
sint8 / sint16 / sint32 / sint64 |
Signed integer types |
bit |
Single-bit boolean (0 or 1) |
m256i |
256-bit value for digests and hashes |
Array typedefs |
bit_2…bit_4096, sint8_2…sint64_8, uint8_2…uint64_8, id_2/id_4/id_8 |
Useful constants for QPI development:
| Constant | Value | Description |
|---|---|---|
NULL_ID |
id::zero() |
The zero/null identity |
NULL_INDEX |
-1 |
Invalid / not-found index sentinel |
NUMBER_OF_COMPUTORS |
676 |
Total computors in the network |
QUORUM |
451 |
Minimum computors required for consensus |
INVALID_AMOUNT |
— | Sentinel for invalid QU amounts |
_A – _Z |
letter values | Used with ID() macro to build identities from letters |
JANUARY – DECEMBER |
1–12 |
Month constants |
MONDAY – SUNDAY |
— | Day-of-week constants |
- VS Code
^1.85.0 - No runtime dependencies
npm install
npm run compile
npm run package # produces .vsixInstall the .vsix via Extensions: Install from VSIX in VS Code.
- Syntax Highlighting (QPI keywords, macros, types)
- Code Snippets (PUBLIC_PROCEDURE, PUBLIC_FUNCTION, contract skeleton)
- Linter: QPI language restrictions (
#,/,%, pointers, native types, etc.) - "New Qubic SC" template command
- IntelliSense for all
qpi.*functions - Hover documentation
- Error squiggles (red underline for harder violations)
- Full QPI type and constant completions (Array, HashMap, Entity, NULL_ID, etc.)
- JSDoc-style contract metadata (
@contract,@description,@procedure,@function,@state) - Hover documentation from doc-comment metadata
- QPI017 — warning when
@procedure/@functiondeclared but missing from code -
qpi-contract-docsnippet
-
Dev Kit integration (deploy to testnet)
Note for contributors: This feature requires a stable Qubic CLI or REST API that allows contract developers to deploy
.hfiles to the Qubic testnet directly from VS Code. As of 2026-03, no such public API exists for contract developers — qubic-cli is targeted at node operators, not smart contract authors. If you know of an official deploy API or Dev Kit, please open an issue or contact the publisher. -
Contract validator
Qubic QPI Language Support – VS Code Marketplace
Thanks to everyone who has contributed code, fixes, and improvements to this extension.
- @double-k-3033 — Hardened smart contract detection
(
QPI_CONTRACT_DECLARATION_REGEX), split QPI001 into Warning/Error severity, and improved test harness accuracy. Thank you for the thorough and well-structured contributions!
