NexScript Documentation

website·Indexed 31 days ago

https://nexscript.org

A high-level, statically typed programming language for developing stateless, UTXO-based smart contracts on Nexa. The documentation covers the NexScript compiler, SDK, language grammar, artifact specifications, and advanced features such as MAST Contract Paths (MCP), multiplex contracts, and Merkle tree interaction tools.

Tokens
17.5K
Snippets
78
Records
103
Agent score
56%

What's inside NexScript

  1. Overview of NexScript for Nexa Smart Contracts

    NexScript is a high-level programming language designed for creating smart contracts on the Nexa blockchain. It provides a strong abstraction layer over Nexa's native virtual machine, Nexa Script. While its syntax is based on Ethereum's Solidity, its underlying functionality is distinct because Nexa's smart contract model differs significantly from Ethereum's. NexScript is a fork of CashScript (originally for Bitcoin Cash).
  2. Understand Nexa Script Templates and P2PKT

    In Nexa, coins are held in Script Templates, which are a generalization of Bitcoin's Pay-To-Script-Hash (P2SH). A Script Template hashes the script code to keep it concise. 'Well-Known' templates are standardized and identified by a script number rather than the full template. The first well-known template (script number 1) is Pay-To-Publickey-Template (P2PKT), where only the private key corresponding to the public key can create a valid signature to spend the coins.
  3. Access transaction state and identity

    The following global variables provide information about the transaction's identity and total native cryptocurrency flow:
    bytes32 txId = tx.id; // Transaction ID in native endianness
    bytes32 txIdem = tx.idem; // Transaction idem in native endianness
    int totalIn = tx.amountIn; // Total incoming satoshis
    int totalOut = tx.amountOut; // Total outgoing satoshis
  4. Understand Nexa's stateless UTXO smart contract model

    Nexa smart contracts are stateless and based on the UTXO (Unspent Transaction Output) model. Unlike account-based models, this architecture ensures that transactions are verified independently and efficiently. Because there is no global state that can influence execution, contract results are deterministic and predictable.
  5. Implement DeFi applications on Nexa

    While Nexa differs from EVM-based chains due to its UTXO architecture, DeFi applications can be built using a combination of Script Templates, transaction introspection, and local state management.
  6. Instantiate a NexScript Contract

    The Contract class represents a NexScript contract in JavaScript. It requires an Artifact (the result of compiling a NexScript contract), a list of constructor arguments, and an optional NetworkProvider to manage network operations. If no provider is specified, a mainnet ElectrumNetworkProvider is used by default.
    import { Contract, ElectrumNetworkProvider } from '@nexscript/nexscript';
    import { compileFile } from 'nexc';
    
    // Option 1: Import the artifact JSON
    import P2PKH_JSON from './p2pkh.json';
    
    // Option 2: Compile a contract file
    const P2PKH_COMPILED = compileFile(new URL('p2pkh.nex', import.meta.url));
    
    const provider = new ElectrumNetworkProvider('testnet4');
    const contract = new Contract(P2PKH_JSON, [alicePkh], { provider });
  7. Implement absolute time locks with tx.time

    tx.time is used for absolute time locks. It represents either the block number or the timestamp of the spending transaction.

    • Values below 500,000,000 are treated as block numbers.
    • Values 500,000,000 and above are treated as timestamps.

    Due to Script limitations, it must be used as: require(tx.time >= <expression>);.

    SDK Integration: A corresponding transaction-level time lock is required. The NexScript SDK sets this to the most recent block number by default. To use a specific block number or timestamp, use the withTime() function in the SDK.

    require(tx.time >= 600000);
  8. Declare visible unused constraints in MCP contracts

    Due to the pre-formed stack layout in MCP contracts, unused arguments must be dropped first. This requires a different declaration order for 'visible unused' constraints compared to normal contracts.

    For an MCP contract, declare visible unused constraints at the beginning of the argument list.

    // Correct for MCP contracts
    contract A(int visible unused a, int b)…
    
    // Incorrect for MCP contracts (standard ordering)
    contract A(int a, int visible unused b)…
  9. Develop Nexa smart contracts using NexScript

    Nexa transactions are executed on a virtual machine called 'raw Nexa Script'. Because writing raw bytecode for this VM is complex and error-prone, developers should use NexScript, which provides a strong abstraction layer to improve developer experience and contract reliability.
  10. Implement relative time locks with tx.age

    tx.age is used for relative time locks. It represents either a number of blocks or a number of 'chunks' (512 seconds).

    Due to Script limitations, it must be used as: require(tx.age >= <expression>);.

    SDK Integration: A corresponding transaction-level time lock is required. This is configured in the NexScript SDK using the withAge() function. Note that withAge() currently only supports block counts; using tx.age as a number of second chunks is not currently supported via the SDK.

    require(tx.age >= 100);
  11. Implement Control Flow and Requirements in NexScript

    NexScript supports conditional logic via if statements and validation via require statements. Time-locked requirements can be enforced using require(tx.age >= expression) or require(tx.time >= expression).
    // Basic requirement
    require(amount > 100);
    
    // Time-based requirement
    require(tx.age >= 1000);
    
    // Conditional logic
    if (isAuthorized) {
        // logic
    } else {
        // alternative logic
    }
  12. Manage smart contract local state using GroupTokens

    In Nexa smart contracts, local state can be persisted across multiple transactions by storing data in the NFT data of a GroupToken. This data is located in the bytes following the 32-byte group ID of the smart contract UTXO. This approach allows the base contract address to remain constant, avoiding the need for 'simulated state' where the P2ST locking bytecode must be updated (which would change the address) for every state iteration.