# Nearcast: full language reference > Nearcast is a scripting language for NEAR. A script says in a few readable lines what should happen onchain. Nearcast simulates every step against live chain state, shows exactly what will be signed, then runs it from the user's NEAR wallet and resumes from a checkpoint if anything fails. Through NEAR Chain Signatures and NEAR Intents, one NEAR account's script also acts on EVM chains, Bitcoin and Solana. App: https://nearcast-phi.vercel.app (write, simulate and run scripts in the browser with a NEAR wallet). Source: https://github.com/chiranjeev13/nearcast ## Writing scripts: the essentials - One statement per line. `#` starts a comment. - `load ` before using a module's commands (`token:transfer`) or helpers (`@token:balance`). Built-ins (`transfer`, `call`, `batch`, `@balance` …) need no load. - `set $name ` stores a variable; `$name` reads it. - `@helper(args)` reads live state (balances, view calls, derived addresses). Helpers never sign anything. - `assert "message"` stops the plan before anything is signed if the check fails. Operators: > >= < <= == != - Blocks use parentheses: `batch ( … )` makes one atomic NEAR transaction; `chainsig:on "" ( … )` holds EVM calls signed by the NEAR account. - Each command is one step. Steps run in order; steps on different chains are not atomic with each other. - NEAR amounts without a unit are whole NEAR (`transfer bob.near 0.1` sends 0.1 NEAR). Token amounts in `token:transfer` and `intents:swap` are whole tokens. Amounts inside `exec` and in balance comparisons are raw units, so write `1e6` for 1 USDC (6 decimals). - Strings use double quotes. Chain Signatures paths ("payroll", "vault") name independent addresses per chain, all controlled by the same NEAR account. ## Units - `near`: 1near = 10^24 yoctoNEAR - `tgas`: 1tgas = 10^12 gas - `yocto`: smallest NEAR unit - `e`: exponent: 1e6 = 1000000, 0.5e18 ## Language keywords ### `load` ``` load ``` Make a module's commands and helpers available. Example: ``` load token ``` ### `set` ``` set $name ``` Store a value in a variable. Example: ``` set $vault @chainsig:address(base "vault") ``` ### `print` ``` print ... ``` Show values in the plan output. Example: ``` print "vault:" $vault ``` ### `assert` ``` assert ["message"] ``` Check a condition. Before any transaction: blocks the plan. After one: stops the run there. Operators: > >= < <= == != Example: ``` assert @balance > 1near "need 1 NEAR" ``` ## Module `std` (built in) Built-ins: contract calls, NEAR transfers, atomic batches, signing as another account. Commands: ### `call` ``` call [{json args}] [--deposit ] [--gas ] ``` Call a NEAR contract method. Deposit and gas are base units: use 1yocto, 0.1near, 50tgas. Default gas 30 Tgas. Options: - `--deposit `: NEAR attached (1yocto, 0.5near) - `--gas `: gas attached (default 30tgas) Chains: near Example: ``` call counter.testnet increment {"by": 2} --gas 50tgas ``` ### `transfer` ``` transfer ``` Send NEAR. Amount in NEAR (1, 0.25) or with a unit (1near, 500yocto). Sending to a 64-hex or 0x implicit account creates it. Chains: near Example: ``` transfer bob.testnet 0.5 ``` ### `batch` ``` batch [] ( ... ) ``` One atomic transaction: every action inside goes to one receiver; all succeed or none do. Without , the first action decides it. Several intents:swap deposits from the same NEAR token fit in one batch, and so do chainsig commands (their signature requests become one transaction): one approval. Chains: near Example: ``` batch counter.testnet ( call counter.testnet increment call counter.testnet increment ) ``` ### `seq` ``` seq ( ... ) ``` Steps in order, explicitly not atomic. ### `as` ``` as ( ... ) ``` Sign the block's NEAR transactions as another account you hold a local key for. Keys come from ~/.near-credentials//.json, e.g. one saved by account:create. Chains: near Example: ``` as app.me.testnet ( account:deploy ./app.wasm ) ``` Helpers: ### `@me` ``` @me ``` The account running the script. ### `@view` ``` @view( [{json args}]) ``` Read-only contract call. Chains: near Example: ``` @view(counter.testnet get_num {}) ``` ### `@token` ``` @token() ``` Token contract for a symbol: NEP-141 on NEAR, ERC-20 inside chainsig:on. Example: ``` @token(USDC) ``` ### `@balance` ``` @balance([account]) ``` NEAR balance in yoctoNEAR (default: @me). Example: ``` assert @balance > 1near ``` ### `@exists` ``` @exists() ``` true if the NEAR account exists. Example: ``` assert @exists(bob.testnet) == true ``` ## Module `token` (`load token`) Fungible tokens: NEP-141 on NEAR, ERC-20 inside chainsig:on. Commands: ### `token:transfer` ``` token:transfer [--memo "text"] ``` Send a NEP-141 token. Amount in whole tokens; registers the recipient if needed, atomically. Options: - `--memo "text"`: memo stored with the transfer Chains: near Example: ``` token:transfer USDC alice.testnet 1.5 ``` ### `token:transfer-call` ``` token:transfer-call "" ``` Send tokens to a contract and call ft_on_transfer on it (deposits into DEXes, etc.). Options: - `--gas `: default 100tgas Chains: near ### `token:register` ``` token:register [account] ``` Pay the token's storage deposit for an account. Chains: near ### `token:wrap` ``` token:wrap ``` Wrap NEAR into wNEAR. Chains: near Example: ``` token:wrap 1 ``` ### `token:unwrap` ``` token:unwrap ``` Unwrap wNEAR back to NEAR. Chains: near Helpers: ### `@token:registered` ``` @token:registered( [account]) ``` true if the account is registered with the token. ### `@token:amount` ``` @token:amount( ) ``` Whole tokens to base units, using the token's decimals. Example: ``` @token:amount(USDC 1.5) ``` ### `@token:balance` ``` @token:balance( [account]) ``` Balance in base units (inside chainsig:on: the ERC-20 balance). ### `@token:decimals` ``` @token:decimals() ``` Token decimals. ## Module `nft` (`load nft`) Non-fungible tokens (NEP-171): transfer, approve, inspect. Commands: ### `nft:transfer` ``` nft:transfer [--memo "text"] ``` Transfer an NFT (attaches the required 1 yoctoNEAR). Options: - `--memo "text"`: memo stored with the transfer Chains: near Example: ``` nft:transfer nft.examples.testnet "42" bob.testnet ``` ### `nft:transfer-call` ``` nft:transfer-call "" ``` Transfer an NFT to a contract and call nft_on_transfer on it. Options: - `--gas `: default 100tgas Chains: near ### `nft:approve` ``` nft:approve [--msg "text"] [--deposit ] ``` Let another account transfer one of your NFTs. Options: - `--msg "text"`: calls nft_on_approve on the account - `--deposit `: storage deposit (default 0.001 NEAR) Chains: near ### `nft:revoke` ``` nft:revoke [account] ``` Revoke one approval, or all without an account. Chains: near Helpers: ### `@nft:owner` ``` @nft:owner( ) ``` Owner of a token, or null. ### `@nft:tokens` ``` @nft:tokens( [account]) ``` Token ids owned (first 100). ### `@nft:count` ``` @nft:count( [account]) ``` Total supply, or how many an account owns. ### `@nft:metadata` ``` @nft:metadata( [token id]) ``` Contract metadata, or one token's metadata. ## Module `account` (`load account`) NEAR accounts: create, delete, access keys, contract deployment. Commands: ### `account:create` ``` account:create [--deposit ] [--key ] ``` Create a sub-account (x.you.testnet) or a top-level account (x.testnet). Default deposit 0.1 NEAR. --key new (default) generates a key and saves it to ~/.near-credentials, so `as ( ... )` can use it. Options: - `--deposit `: initial balance in NEAR (default 0.1) - `--key `: full-access key (default: new, saved locally) Chains: near Example: ``` account:create app.me.testnet --deposit 2 ``` ### `account:delete` ``` account:delete --beneficiary ``` Delete the signing account and send its balance to the beneficiary. Options: - `--beneficiary `: receives the remaining NEAR Chains: near Example: ``` as old.me.testnet ( account:delete --beneficiary me.testnet ) ``` ### `account:add-key` ``` account:add-key [--contract --methods "a,b" --allowance ] ``` Add a full-access key, or a function-call key with --contract. Options: - `--contract `: makes it a function-call key for this contract - `--methods "a,b"`: allowed methods (default: any) - `--allowance `: NEAR it may spend on gas (default 0.25) Chains: near Example: ``` account:add-key ed25519:7xT… --contract game.testnet --methods "play,claim" --allowance 0.25 ``` ### `account:delete-key` ``` account:delete-key ``` Remove an access key. Chains: near Example: ``` account:delete-key ed25519:7xT… ``` ### `account:deploy` ``` account:deploy [--init --args {…} --deposit --gas ] ``` Deploy a contract to the signing account, optionally initializing it atomically. Paths are relative to the script. Storage locks about 1 NEAR per 100 KB. Options: - `--init `: call this method in the same transaction - `--args {…}`: init arguments - `--deposit `: init deposit (1yocto, 1near) - `--gas `: init gas (default 30tgas) Chains: near Example: ``` account:deploy ./counter.wasm --init new --args {"owner": "me.testnet"} ``` ### `account:deploy-global` ``` account:deploy-global [--by hash|account] ``` Publish code once as a global contract that other accounts can use. Options: - `--by hash|account`: how others reference it (default hash) Chains: near ### `account:use-global` ``` account:use-global ``` Make the signing account run a published global contract. Chains: near Helpers: ### `@account:exists` ``` @account:exists() ``` true if the account exists. ### `@account:balance` ``` @account:balance([account]) ``` NEAR balance in yoctoNEAR. ### `@account:keys` ``` @account:keys([account]) ``` List of public keys on the account. ### `@account:code-hash` ``` @account:code-hash([account]) ``` Contract code hash (all 1s = no contract). ## Module `stake` (`load stake`) Staking: delegate to staking pools, or stake natively as a validator. Commands: ### `stake:deposit` ``` stake:deposit ``` Deposit and stake NEAR with a staking pool. Chains: near Example: ``` stake:deposit legends.pool.f863973.m0 1 ``` ### `stake:unstake` ``` stake:unstake [ | all] ``` Unstake from a pool; withdrawable after ~4 epochs. Chains: near Example: ``` stake:unstake legends.pool.f863973.m0 all ``` ### `stake:withdraw` ``` stake:withdraw [ | all] ``` Withdraw unstaked NEAR from a pool. Chains: near ### `stake:native` ``` stake:native ``` Stake action: lock NEAR as a validator with this key (0 stops). Chains: near Helpers: ### `@stake:staked` ``` @stake:staked( [account]) ``` Staked balance in yoctoNEAR. ### `@stake:unstaked` ``` @stake:unstaked( [account]) ``` Unstaked (not yet withdrawn) balance in yoctoNEAR. ### `@stake:withdrawable` ``` @stake:withdrawable( [account]) ``` true when unstaked NEAR can be withdrawn now. ## Module `meta` (`load meta`) Meta transactions (NEP-366): one account signs, another pays the gas. Commands: ### `meta:relay` ``` meta:relay --relayer [--ttl ] ( ... ) ``` The signer signs the block's actions; the relayer sends them and pays gas and deposits. One receiver per block, like batch. Both accounts need local keys. Options: - `--relayer `: pays and sends - `--ttl `: validity after signing (default 600) Chains: near Example: ``` meta:relay --relayer sponsor.testnet ( call counter.testnet increment ) ``` ### `meta:sign` ``` meta:sign [--ttl ] ( ... ) ``` Sign a meta transaction without sending it; the receipt shows it (base64) for any relayer. Options: - `--ttl `: validity after signing (default 600) Chains: near ### `meta:submit` ``` meta:submit ``` Relay someone else's signed meta transaction, paying its gas. Chains: near ## Module `chainsig` (`load chainsig`) Chain Signatures: this NEAR account controls addresses on EVM chains, Bitcoin and Solana. Commands: ### `chainsig:on` ``` chainsig:on "" ( exec ... ) ``` Run EVM calls from the address this account controls for , signed by NEAR's MPC network. Chains: evm Example: ``` chainsig:on base "treasury" ( exec @token(USDC) "transfer(address,uint256)" 0x…dEaD 1e6 ) ``` ### `exec` ``` exec "" [args...] [--value ] [--gas ] ``` One EVM contract call, inside chainsig:on. Options: - `--value `: native coin sent with the call - `--gas `: gas limit (default: estimate x1.3) Chains: evm Example: ``` exec 0xC02a…6Cc2 "deposit()" --value 1e15 ``` ### `chainsig:transfer` ``` chainsig:transfer "" ``` Send the native coin (ETH, POL, BNB, BTC, SOL) from a derived address. Amount in whole coins. Bitcoin: P2WPKH, inputs and fee pinned at plan time. Solana: built right before signing; re-signed if its blockhash expires. Chains: evm, bitcoin, solana Example: ``` chainsig:transfer bitcoin "vault" tb1q… 0.0001 ``` Helpers: ### `@chainsig:address` ``` @chainsig:address( "") ``` Address this account controls on for . ### `@chainsig:balance` ``` @chainsig:balance( "" [native | token]) ``` Balance of the derived address in base units (wei, sats, lamports). Example: ``` assert @chainsig:balance(base "treasury" USDC) >= 1e6 ``` ## Module `intents` (`load intents`) NEAR Intents: bridge or swap between NEAR, EVM chains, Bitcoin and Solana (mainnet). Commands: ### `intents:swap` ``` intents:swap from to [--from-wallet] [--to ] [--path "p"] [--slippage ] [--deadline ] ``` Bridge or swap through NEAR Intents. Your NEAR account signs the deposit; solvers deliver on the other chain. Mainnet only. Several swaps from the same NEAR token can share one transaction: wrap them in batch ( ... ). `from`/`to` are optional. Same token on two chains = bridge (base:USDC → arbitrum:USDC). Recipient and refund default to addresses this NEAR account controls. The quote is re-checked before depositing; unfilled swaps are refunded. Options: - `--from-wallet`: deposit from the EVM wallet connected in the UI (MetaMask); refunds go back to it - `--to `: receiver on the destination chain (default: yours) - `--path "p"`: Chain Signatures path for your addresses (default "intents") - `--slippage `: max price slippage, default 0.5 - `--deadline `: quote lifetime, default 60 Chains: near, evm, bitcoin, solana Example: ``` intents:swap 100 from base:USDC to arbitrum:USDC --from-wallet ``` Helpers: ### `@intents:seconds-left` ``` @intents:seconds-left("") ``` Seconds until a time (used to re-check quote deadlines). ## Chains ### testnet - `ethereum`: Ethereum Sepolia (evm, native ETH, 18 decimals, tokens: USDC) - `base`: Base Sepolia (evm, native ETH, 18 decimals, tokens: USDC) - `arbitrum`: Arbitrum Sepolia (evm, native ETH, 18 decimals, tokens: USDC) - `optimism`: OP Sepolia (evm, native ETH, 18 decimals, tokens: USDC) - `polygon`: Polygon Amoy (evm, native POL, 18 decimals, tokens: USDC) - `bsc`: BNB Testnet (evm, native BNB, 18 decimals) - `bitcoin`: Bitcoin testnet (btc, native BTC, 8 decimals) - `solana`: Solana devnet (sol, native SOL, 9 decimals) ### mainnet - `ethereum`: Ethereum (evm, native ETH, 18 decimals, tokens: USDC, USDT) - `base`: Base (evm, native ETH, 18 decimals, tokens: USDC) - `arbitrum`: Arbitrum One (evm, native ETH, 18 decimals, tokens: USDC, USDT) - `optimism`: OP Mainnet (evm, native ETH, 18 decimals, tokens: USDC) - `polygon`: Polygon (evm, native POL, 18 decimals, tokens: USDC, USDT) - `bsc`: BNB Smart Chain (evm, native BNB, 18 decimals, tokens: USDC, USDT) - `bitcoin`: Bitcoin (btc, native BTC, 8 decimals) - `solana`: Solana (sol, native SOL, 9 decimals) ## NEAR tokens by symbol - testnet: USDC, USDT, wNEAR - mainnet: USDC, USDT, wNEAR ## Example scripts ### Payroll on three chains from one NEAR account (testnet) ``` load chainsig assert @balance > 0.5near "need 0.5 NEAR for signing fees" assert @chainsig:balance(base "payroll" USDC) >= 1e6 "Base payroll wallet needs 1 USDC" assert @chainsig:balance(solana "payroll") > 0.02e9 "Solana payroll wallet needs 0.02 SOL" assert @chainsig:balance(bitcoin "payroll") > 5000 "Bitcoin payroll wallet needs 5000 sats" chainsig:on base "payroll" ( exec @token(USDC) "transfer(address,uint256)" 0xcC50098040cdD7F65f198f3EDF5baB02bC3dfa10 1e6 ) chainsig:transfer solana "payroll" 8xXETPXGab2fEyqhaeGeUkxxFXhGkC5wo8ndNzTikcZj 0.01 chainsig:transfer bitcoin "payroll" tb1qsstasqk8t7qu0d9ywr7khf3nug2jyaszpkrl4d 0.00001 ``` ### Settle USDC from NEAR onto Base and Solana in one transaction (mainnet) ``` load intents load token assert @token:balance(USDC) >= 4e6 "need 4 USDC on NEAR" batch ( intents:swap 2 from near:USDC to base:USDC --to 0xYourBaseAddress intents:swap 2 from near:USDC to solana:USDC --to YourSolanaAddress ) ``` ### examples/bridge-usdc.cast ``` # Bridge USDC from Base to Arbitrum with NEAR Intents (mainnet). # Your NEAR account signs the Base deposit through Chain Signatures; solvers deliver on Arbitrum # to the address the same NEAR account controls there. Unfilled swaps are refunded on Base. load intents load chainsig print "your Base / Arbitrum address:" @chainsig:address(base "intents") assert @chainsig:balance(base "intents" USDC) >= 10e6 "the Base address needs 10 USDC" intents:swap 10 from base:USDC to arbitrum:USDC --slippage 0.5 ``` ### examples/demo.cast ``` # Cross-chain payroll: one NEAR account pays people on NEAR and moves funds on Base, # in one reviewable run. Simulate first (nothing is signed), then Review & execute. load token load chainsig # Two Base addresses this NEAR account controls through Chain Signatures. set $treasury @chainsig:address(base "treasury") set $savings @chainsig:address(base "savings") print "treasury on Base:" $treasury print "savings on Base: " $savings # Preflight: nothing is signed unless every check passes. assert @balance > 1near "need 1 NEAR for fees" assert @token:balance(USDC) >= 1e6 "need 1 USDC on NEAR" assert @chainsig:balance(base "treasury" ETH) > 0.0001e18 "treasury needs ETH on Base for gas" assert @chainsig:balance(base "treasury" USDC) >= 1e6 "treasury needs 1 USDC on Base" # NEAR: pay alice in USDC (her token registration is added in the same transaction) and bob in NEAR. token:transfer USDC alice.testnet 1 --memo "payroll" transfer bob.testnet 0.1 # Base: move 1 USDC from treasury to savings, signed by NEAR's MPC network. chainsig:on base "treasury" ( exec @token(USDC) "transfer(address,uint256)" $savings 1e6 ) ``` ### examples/multichain.cast ``` # One NEAR account, native coins on three other chains through Chain Signatures. load chainsig print "base: " @chainsig:address(base "vault") print "bitcoin:" @chainsig:address(bitcoin "vault") print "solana: " @chainsig:address(solana "vault") assert @chainsig:balance(bitcoin "vault") > 0 "fund the Bitcoin address first" assert @chainsig:balance(solana "vault") > 0 "fund the Solana address first" chainsig:transfer bitcoin "vault" tb1qnk7n0hf7ykmg5zx8fs3jkxgzr4ph7tz0xxml2z 0.00001 chainsig:transfer solana "vault" 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM 0.001 chainsig:transfer base "vault" 0x000000000000000000000000000000000000dEaD 0.00001 ``` ## How a run works 1. Simulate: reads live balances, estimates gas, pins nonces, picks Bitcoin inputs, fetches Intents quotes. Signs nothing. Each step is tagged [READ-ONLY], [STATE-CHANGING], [IRREVERSIBLE] or [HIGH-VALUE]. 2. Execute: the NEAR wallet (Meteor, HOT, MyNearWallet, Intear, or MetaMask via NEAR's Ethereum wallet support) approves each transaction. Other chains are signed by NEAR's MPC network, and every signature is checked against the expected address before broadcast. 3. Resume: every step is checkpointed; a run never sends a second transaction for a step that may already have landed. CLI: `nearcast plan script.cast` (simulate), `nearcast run script.cast`, `nearcast resume script.cast`, `nearcast docs [filter]`, `nearcast ui`. Flags: `--network testnet|mainnet`, `--account `. ## Limits - NEAR Intents runs on mainnet only. - Chain Signatures requests are one per transaction: `chainsig:on` and `chainsig:transfer` go outside `batch`. - Bitcoin: one payment per address per script. Solana: native SOL only (no SPL tokens yet). - Wallets can't sign meta transactions or global-contract actions; use a local key with the CLI. - MetaMask (as a NEAR wallet) splits multi-action transactions, so it can't approve a `batch`.