Skip to content

Devpack Overview

The devpack provides Solidity-facing libraries, standard contracts, and compiler intrinsics for Neo N3 development. It bridges the gap between Solidity syntax and Neo blockchain capabilities, giving developers access to native contracts, syscalls, storage, runtime services, and NEP token standards through familiar Solidity interfaces.

Compiler Intrinsics

These libraries are primarily compiler intrinsics surfaces — the neo-solc compiler recognizes supported members and lowers them directly to NeoVM opcodes and syscalls. Unsupported members produce a compile-time diagnostic listing available intrinsics. You write Solidity; the compiler emits NeoVM bytecode.

Directory Layout

devpack/
├── contracts/               # Syscall/native-call wrappers and framework contracts
│   ├── FrameworkBase.sol    # Base framework with ownership, storage, gas management
│   ├── Framework.sol        # Extended framework (strict-manifest mode)
│   ├── Syscalls.sol         # Complete Neo N3 syscall surface (1,323 lines)
│   ├── NativeCalls.sol      # Native contract wrappers (1,068 lines)
│   ├── OracleService.sol    # Oracle convenience wrapper
│   └── NEP17Rescue.sol      # Emergency native-token recovery for NEP-17 contracts
├── libraries/               # High-level helper libraries
│   ├── Neo.sol              # Blockchain integration (542 lines)
│   ├── Storage.sol          # Advanced storage operations (827 lines)
│   └── Runtime.sol          # Runtime services and utilities (688 lines)
├── standards/               # NEP token standard implementations
│   ├── NEP17.sol            # Fungible token (NEP-17)
│   ├── NEP11.sol            # Non-fungible token (NEP-11, divisible + indivisible)
│   └── NEP24.sol            # Royalty standard (NEP-24)
├── examples/                # Production-oriented usage examples
│   ├── CompleteNEP17Token.sol
│   ├── CompleteNEP11NFT.sol
│   └── VaultPattern.sol
├── DEVPACK_GUIDE.md
├── README.md
└── standards/STANDARDS_MAPPING.md

Usage

Compiling with the devpack

Pass the devpack root as an include path:

bash
neo-solc MyContract.sol -I devpack -O2 -o build/MyContract

Importing in Solidity

solidity
import "contracts/NativeCalls.sol";
import "libraries/Storage.sol";
import "standards/NEP17.sol";

All imports resolve relative to the -I include path. No package manager or remappings required.

Core Contracts

Syscalls.sol

Complete mapping of all Neo N3 syscalls to Solidity functions. This is the lowest-level devpack surface — every other contract and library ultimately delegates to Syscalls.

CategorySyscallsExamples
StorageGetContext, Get, Put, Delete, Find, AsReadOnly, Local.*storageGet(), storagePut(), storageFind()
RuntimeCheckWitness, GetTime, GasLeft, Notify, Log, BurnGascheckWitness(), gasLeft(), notify()
ContractCall, GetCallFlags, CreateStandardAccount, CreateMultisigAccountcontractCall(), getCallFlags()
CryptoCheckSig, CheckMultisig (via System.Crypto.*)checkSig(), checkMultisig()
CryptoLibSHA256, RIPEMD160, Keccak256, VerifyWithECDsa, Ed25519, BLS12-381, Murmur32sha256(), verifyWithECDsa(), bls12381Pairing()
StdLibserialize, deserialize, JSON, base64/58, hex, string ops, itoa/atoijsonSerialize(), base64Encode(), stringSplit()
IteratorNext, ValueiteratorNext(), iteratorValue()
AdvancedGetRandom, GetNetwork, GetAddressVersion, GetInvocationCountergetCurrentRandom(), getNetwork()

Syscalls.sol also defines the core data structures used throughout the devpack:

StructDescription
BlockTrimmed block: hash, version, previousHash, merkleRoot, timestamp, nonce, index, primaryIndex, nextConsensus, txCount
TransactionBase transaction: hash, version, nonce, sender, systemFee, networkFee, validUntilBlock, script
WitnessinvocationScript + verificationScript
Signeraccount, scopes, allowedContracts, allowedGroups, rules
WitnessRuleaction (deny/allow) + condition bytes
StorageContextContract storage context (id + isReadOnly)
IteratorNeoVM iterator handle (id, hasNext, currentKey, currentValue)
NotificationscriptHash, eventName, state array
ContractStateNativeid, updateCounter, hash, nef, manifest

TIP

Prefer NativeCalls wrappers over raw Syscalls.contractCall() — they generate specific manifest permissions instead of wildcards.

NativeCalls.sol

Direct wrappers for all Neo N3 native contracts. Each function calls through Syscalls.contractCall() with a deterministic contract hash constant, enabling the compiler to infer exact manifest permissions.

Native ContractHash ConstantKey Functions
NEO TokenNEO_CONTRACTneoBalanceOf(), neoTransfer(), vote(), getCandidates(), getAccountState(), unclaimedGas()
GAS TokenGAS_CONTRACTgasBalanceOf(), gasTransfer(), gasTotalSupply()
ContractManagementCONTRACT_MANAGEMENTdeployContract(), updateContract(), destroyContract(), getContract(), hasMethod()
PolicyPOLICY_CONTRACTgetFeePerByte(), getExecFeeFactor(), getStoragePrice(), blockAccount(), isBlocked()
OracleORACLE_CONTRACTrequestOracleData(), getOraclePrice(), setOraclePrice()
RoleManagementROLE_MANAGEMENTdesignateAsRole(), getDesignatedByRole()
NotaryNOTARY_CONTRACTnotaryVerify(), notaryBalanceOf(), notaryLockDepositUntil(), notaryWithdraw()
TreasuryTREASURY_CONTRACTtreasuryVerify(), treasuryOnNEP17Payment(), treasuryOnNEP11Payment()
LedgerLEDGER_CONTRACTcurrentIndex(), currentHash(), getBlock(), getTransaction()

Reference constants for CryptoLib (CRYPTO_LIB) and StdLib (STD_LIB) are also defined, but their methods are exposed through Syscalls.sol directly (e.g., Syscalls.sha256(), Syscalls.base64Encode()).

solidity
// Transfer GAS — generates permission: {"contract":"0xd2a4...","methods":["transfer"]}
bool ok = NativeCalls.gasTransfer(from, to, amount, "");

// Query NEO governance
NativeCalls.NeoCandidate[] memory candidates = NativeCalls.getCandidates();
uint256 unclaimed = NativeCalls.unclaimedGas(account, NativeCalls.currentIndex());

FrameworkBase.sol

Base contract providing common infrastructure for Neo N3 Solidity contracts:

  • OwnershiptransferOwnership(), renounceOwnership(), onlyOwner modifier
  • InitializationwhenInitialized modifier, version tracking
  • Witness-based access controlwithWitness modifier (calls Runtime.checkWitness)
  • Gas limit guardswithGasLimit(minGas) modifier (calls Runtime.gasLeft)
  • Contract lifecycleupgradeContract() with NEF + manifest update
  • EventsFrameworkInitialized, OwnershipTransferred, ContractUpgraded, EmergencyStop
solidity
contract MyToken is FrameworkBase {
    function adminAction() public onlyOwner withWitness withGasLimit(20000000) {
        // Only owner, witness-verified, with at least 0.2 GAS remaining
    }
}

Framework.sol

Extended framework that inherits FrameworkBase. Previously exposed a fully dynamic callContract(address, string, bytes) surface, but this forced wildcard permissions in the manifest ({"contract":"*","methods":"*"}).

Strict-Manifest Mode

Framework.callContract() is intentionally disabled — it reverts at runtime. Use explicit NativeCalls.* or Syscalls.contractCall(KNOWN_HASH, ...) wrappers instead. If truly dynamic dispatch is unavoidable, compile with --manifest-permissions to supply explicit overrides.

OracleService.sol

Convenience wrapper around the Neo N3 Oracle native contract:

  • request(url, filter, userData, gasForResponse) — issue an oracle data request, returns a requestId
  • oracleCallback(url, userData, code, result) — receives the native Oracle callback (restricted to onlyOracleNative)
  • Forwards responses to the original requester via IOracleServiceReceiver.onOracleResponse()
  • Uses a fixed callback method name to avoid wildcard manifest permissions
  • Tracks request metadata (requester, URL, filter, timestamps, completion status)
solidity
contract MyOracle is IOracleServiceReceiver {
    OracleService private _oracle;

    function fetchPrice(string calldata url) external {
        _oracle.request(url, "$.price", "", 100000000);
    }

    function onOracleResponse(
        uint256 requestId, uint256 code, bytes calldata result, bytes calldata userData
    ) external override {
        // Handle oracle response
    }
}

NEP17Rescue.sol

Optional extension for NEP-17 tokens that enables emergency recovery of accidentally sent native tokens (NEO and GAS only). In strict-manifest mode, rescuing arbitrary NEP-17 tokens is not supported because it would require wildcard contract permissions.

solidity
contract MyToken is NEP17Rescue {
    // Inherits emergencyTokenRecovery(token, to, amount, data)
    // Restricted to NEO_CONTRACT and GAS_CONTRACT targets
}

Libraries

Neo.sol (542 lines)

High-level blockchain integration library. Wraps Syscalls and NativeCalls into a convenient API:

CategoryFunctions
Block infogetCurrentBlock(), getBlockByIndex(), getBlockHeight(), getBlockTime()
TransactionsgetTransaction(), getTransactionHeight()
Account/balanceNEO and GAS balance queries via NativeCalls
CryptographicSignature verification, hash functions (delegates to Syscalls CryptoLib)
Contract mgmtCall, deploy, query contracts
NetworkGovernance, committee, validators
GasFee calculations, gas management utilities
solidity
using Neo for *;

(uint256 index, bytes32 hash, uint256 timestamp, bytes32 merkle) = Neo.getCurrentBlock();
uint256 height = Neo.getBlockHeight();

Storage.sol (827 lines)

Advanced storage operations built on top of Syscalls storage syscalls:

CategoryFunctions
ContextgetContext(), getReadOnlyContext(), asReadOnly()
Basic CRUDput(), get(), remove(), exists()
Local storageputLocal(), getLocal(), removeLocal()
Iteratorsfind(), findLocal(), findValues(), findKeys(), findLocalValues(), findLocalKeys()
Countingcount() — count entries matching a prefix
Batch opsBatch put/remove (max 100 per batch)
Key derivationMapping keys, array keys, nested keys
Typed accessorsuint256, address, string, bool typed get/put
Secure storageChecksum validation
ExpirationTTL-based storage entries
solidity
using Storage for *;

// Basic operations
Storage.put("owner", abi.encode(msg.sender));
bytes memory data = Storage.get("owner");
bool hasKey = Storage.exists("owner");

// Iterator-based prefix scan
bytes[] memory allValues = Storage.findValues("token:");
bytes[] memory allKeys = Storage.findKeys("token:");

// Local storage (contract-private, not visible to other contracts)
Storage.putLocal("internal_state", abi.encode(42));

Local vs Global Storage

put()/get() use the contract's global storage context. putLocal()/getLocal() use a local context that is private to the contract and cannot be read by other contracts via System.Storage.GetReadOnlyContext.

Runtime.sol (688 lines)

Runtime services and utilities:

CategoryFunctions
Eventsnotify(), notifyIndexed(), notifyTransfer(), notifyApproval(), notifyNFTTransfer()
NotificationsgetNotifications(), getContractNotifications()
WitnesscheckWitness(), requireWitness(), checkAnyWitness(), checkAllWitnesses(), checkMultiSigWitness()
Execution contextgetExecutionContext(), getCallFlags(), getScriptContainer(), loadScript()
TriggergetTriggerType(), isApplicationTrigger()
GasgasLeft(), burnGas(), requireGas()
Time/blockBlock time, platform info
Logginglog() for debug output
solidity
using Runtime for *;

// Witness verification
Runtime.requireWitness(msg.sender);

// Multi-sig threshold check
address[] memory signers = new address[](3);
signers[0] = addr1; signers[1] = addr2; signers[2] = addr3;
bool approved = Runtime.checkMultiSigWitness(signers, 2); // 2-of-3

// Gas management
uint256 remaining = Runtime.gasLeft();
Runtime.burnGas(1000000); // burn 0.01 GAS

Callback Limitations

Some Runtime.sol methods that accept callback functions (e.g., optimizeGasUsage, executeIfGasAvailable, tryWithFallback) revert at runtime because NeoVM does not support first-class function callbacks. Use inline logic instead.

Token Standards

NEP17.sol — Fungible Tokens

Complete NEP-17 implementation with ERC-20 compatibility:

solidity
interface INEP17 {
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address from, address to, uint256 amount, Any calldata data) external returns (bool);
}

Key features:

  • onNEP17Payment callback support via INEP17Receiver
  • Any type for the data parameter (maps to NeoVM's unconstrained StackItem)
  • Mint, burn, pause, and allowance management
  • Inherits FrameworkBase for ownership and witness control

NEP11.sol — Non-Fungible Tokens

Complete NEP-11 implementation supporting both indivisible and divisible NFTs:

solidity
interface INEP11 {
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
    function totalSupply() external view returns (uint256);
    function balanceOf(address owner) external view returns (uint256);
    function tokensOf(address owner) external view returns (bytes32[] memory);
    function ownerOf(bytes32 tokenId) external view returns (address);
    function transfer(address to, bytes32 tokenId, bytes calldata data) external returns (bool);
    function properties(bytes32 tokenId) external view returns (bytes memory);
}

interface INEP11Divisible is INEP11 {
    function balanceOf(address owner, bytes32 tokenId) external view returns (uint256);
    function transfer(address from, address to, uint256 amount, bytes32 tokenId, bytes calldata data) external returns (bool);
    function ownersOf(bytes32 tokenId) external view returns (address[] memory);
}

NEP24.sol — Royalty Standard

Minimal NEP-24 royalty mixin for NEP-11 NFTs:

solidity
interface INEP24Royalty {
    struct RoyaltyInfo {
        address royaltyRecipient;
        uint256 royaltyAmount;
    }
    function royaltyInfo(bytes32 tokenId, address royaltyToken, uint256 salePrice)
        external view returns (RoyaltyInfo[] memory);
}
  • Per-token and default royalty rules (basis points: 10000 = 100%)
  • _setDefaultRoyalty(), _setTokenRoyalty(), _clearTokenRoyalty()
  • Returns empty array when no royalty is configured

Compiler Intrinsics

The following devpack surfaces are recognized as compiler intrinsics by neo-solc:

Intrinsic SurfaceLowered To
Runtime.checkWitness()System.Runtime.CheckWitness
Runtime.gasLeft()System.Runtime.GasLeft
Runtime.notify()System.Runtime.Notify
Storage.put() / get() / remove()System.Storage.Put / Get / Delete
Storage.find()System.Storage.Find
Syscalls.sha256()CryptoLib.sha256 (native call)
Syscalls.verifyWithECDsa()CryptoLib.verifyWithECDsa (native call)
Syscalls.neoKeccak256()CryptoLib.keccak256 (native call)
Syscalls.contractCall()System.Contract.Call
NativeCalls.gasTransfer()System.Contract.Call → GAS transfer
abi.encode() / abi.decode()NeoVM stack manipulation
Solidity keccak256()CryptoLib.keccak256 (native call)

When you call a member that the compiler does not recognize as an intrinsic, compilation fails with a diagnostic:

error: unsupported intrinsic 'Runtime.unsupportedMethod'
  --> MyContract.sol:12:5
   |
12 |     Runtime.unsupportedMethod();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: available Runtime intrinsics: checkWitness, gasLeft, notify, log, ...

Permission-Conscious Development

Neo N3 manifests declare which contracts and methods your contract is allowed to call. The compiler infers these permissions from your code. Using fixed-target wrappers produces minimal permissions:

NativeCalls.gasTransfer(...)
  → manifest permission: {"contract":"0xd2a4cff3...","methods":["transfer"]}

Syscalls.contractCall(dynamicTarget, dynamicMethod, ...)
  → manifest permission: {"contract":"*","methods":"*"}

Wildcard Permissions

Wildcard permissions ("*") are a security anti-pattern. They allow your contract to call any contract and any method, which increases the attack surface. Always prefer fixed-target wrappers.

Compile with strict flags to reject wildcard permissions at build time:

bash
neo-solc MyContract.sol -I devpack \
  --deny-wildcard-contracts \
  --deny-wildcard-methods \
  -o build/MyContract

If truly dynamic dispatch is unavoidable, supply explicit permission overrides:

bash
neo-solc MyContract.sol -I devpack \
  --manifest-permissions '{"contract":"0xabcd...","methods":["specificMethod"]}' \
  -o build/MyContract

Building Custom Contracts

Minimal contract using devpack features:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "contracts/NativeCalls.sol";
import "libraries/Storage.sol";
import "libraries/Runtime.sol";
import "contracts/FrameworkBase.sol";

contract Vault is FrameworkBase {
    event Deposit(address indexed from, uint256 amount);
    event Withdraw(address indexed to, uint256 amount);

    function deposit() external withWitness {
        uint256 balance = NativeCalls.gasBalanceOf(address(this));
        Storage.put("balance", abi.encode(balance));
        emit Deposit(msg.sender, balance);
    }

    function withdraw(address to, uint256 amount) external onlyOwner withWitness {
        Runtime.requireWitness(to);
        bool ok = NativeCalls.gasTransfer(address(this), to, amount, "");
        require(ok, "Vault: transfer failed");

        uint256 remaining = NativeCalls.gasBalanceOf(address(this));
        Storage.put("balance", abi.encode(remaining));
        emit Withdraw(to, amount);
    }

    function getBalance() external view returns (uint256) {
        bytes memory data = Storage.get("balance");
        if (data.length == 0) return 0;
        return abi.decode(data, (uint256));
    }
}

Compile and deploy:

bash
# Compile with strict permissions
neo-solc Vault.sol -I devpack -O2 \
  --deny-wildcard-contracts \
  --deny-wildcard-methods \
  -o build/Vault

# Inspect generated manifest permissions
cat build/Vault/Vault.manifest.json | jq '.permissions'

# Deploy to testnet
neo-solc deploy build/Vault --network testnet

Native Contract Hash Reference

All native contract hashes are deterministic and identical across all Neo N3 networks:

ContractHashDevpack Constant
NeoToken0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5NativeCalls.NEO_CONTRACT
GasToken0xd2a4cff31913016155e38e474a2c06d08be276cfNativeCalls.GAS_CONTRACT
ContractManagement0xfffdc93764dbaddd97c48f252a53ea4643faa3fdNativeCalls.CONTRACT_MANAGEMENT
PolicyContract0xcc5e4edd9f5f8dba8bb65734541df7a1c081c67bNativeCalls.POLICY_CONTRACT
OracleContract0xfe924b7cfe89ddd271abaf7210a80a7e11178758NativeCalls.ORACLE_CONTRACT
RoleManagement0x49cf4e5378ffcd4dec034fd98a174c5491e395e2NativeCalls.ROLE_MANAGEMENT
Notary0xc1e14f19c3e60d0b9244d06dd7ba9b113135ec3bNativeCalls.NOTARY_CONTRACT
Treasury0x156326f25b1b5d839a4d326aeaa75383c9563ac1NativeCalls.TREASURY_CONTRACT
LedgerContract0xda65b600f7124ce6c79950c1772a36403104f2beNativeCalls.LEDGER_CONTRACT
CryptoLib0x726cb6e0cd8628a1350a611384688911ab75f51bNativeCalls.CRYPTO_LIB
StdLib0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0NativeCalls.STD_LIB

See Also

MIT Licensed