SDK

The TypeScript client for XE — hold your own keys, move value and rent compute without running a node. Pre-release, built from source.


@xeprotocol/sdk is the official client library for XE. It builds, signs and proof-of-works blocks in your own process and submits them to any node's HTTP API, so your keys never leave your machine and you never have to run a node. It runs in Node.js 20+ and in the browser on the same code path, and it is written in TypeScript with bigint throughout, so micro-unit amounts and nanosecond timestamps are never rounded.

The source of truth is the repository at github.com/xeprotocol/sdk; this page mirrors its README so the SDK is discoverable alongside the HTTP API and the CLI. When they disagree, the repository wins.

[!WARNING] Pre-release — not on npm

  • Not published to a registry yet. npm install @xeprotocol/sdk fails today; build it from the repository as described below.
  • XE is pre-1.0 and runs on a public testnet only. Protocol changes land by wiping the testnet; balances, accounts and history are discarded when that happens, without warning.
  • There is no backward compatibility, and there will be none before 1.0. The API in the SDK will change.
  • XE on the testnet has no monetary value.

Install from source

git clone https://github.com/xeprotocol/sdk.git
cd sdk
npm install        # runs the prepare script, which compiles src/ to dist/
npm test           # unit tests, no network needed

Then use it from your own project either by path or straight from git — both run the build for you:

npm install /path/to/sdk
# or
npm install github:xeprotocol/sdk

npm link works too during development. The package exposes two entry points: @xeprotocol/sdk (Node and browser) and @xeprotocol/sdk/jobs (Node only, for running code on leased machines).

Quick start

import { Wallet, Xe, fromMicro, toMicro } from '@xeprotocol/sdk'

const wallet = Wallet.create()          // or Wallet.fromSeedHex(process.env.SEED)
console.log(wallet.address)             // hand this out to receive funds

const xe = new Xe({ client: 'https://ldn.core.test.network', wallet })

// Anything sent to you arrives as PENDING and is yours only once you claim it.
await xe.receiveAll()

console.log(fromMicro(await xe.balance('XE')), 'XE')

await xe.send({
  to: someAddress,
  amount: toMicro('1.5'),
  memo: 'thanks',
})

Point client at any node. https://ldn.core.test.network is the public London testnet node; the other bootstrap nodes are listed in Getting Started. Fund a fresh wallet with xe faucet from the CLI or the faucet tutorial below — the faucet sends 1,000 XE per account per day as a pending transfer, which xe.receiveAll() claims.

Things the SDK gets right for you

Address is not public key

An address is derived from a public key, and the two are different values:

address = sha256("xe/account/v1" || pubkey)

The address is identity — it goes in a block's account and destination, and it is what you paste to receive funds. The public key is a credential. Keeping them apart is what lets a key be rotated without the account changing, so the SDK types them distinctly (Address, PublicKey) and will not let you pass one where the other belongs.

Amounts are integers

Both assets carry six decimal places and every amount on the wire is an integer count of micro-units; timestamps are unix nanoseconds. Both exceed what a JavaScript number can hold exactly, so the SDK uses bigint throughout and parses responses losslessly — a rounded timestamp produces a block the network rejects, and a rounded balance is simply wrong.

toMicro('1.5')        // 1500000n
fromMicro(1500000n)   // '1.500000'

Errors tell you whether to retry

The node distinguishes a failure worth retrying (a block that arrived before its dependency) from a terminal one (a bad signature). The SDK carries that distinction through, so you never guess from a message:

import { isRetryable } from '@xeprotocol/sdk'

try {
  await xe.send({ to, amount })
} catch (err) {
  if (isRetryable(err)) { /* back off and try again */ }
}

A non-2xx response always throws. An empty list from the SDK means the account genuinely has nothing — never that the request failed.

Renting a machine

A lease escrows XUSD with a provider for a fixed term. To pay only for the time you use, keep the term short and renew it a minute at a time: nothing beyond the current minute is ever escrowed, so if you stop — or your process dies — the machine is released within a minute.

import { Wallet, Xe, holdLease } from '@xeprotocol/sdk'

const xe = new Xe({
  client: 'https://ldn.core.test.network',
  wallet: Wallet.fromSeedHex(process.env.SEED),
  // Renewals are timed by the network's timekeepers, so the SDK needs to
  // reach a threshold of them.
  timekeepers: [
    'https://ldn.core.test.network',
    'https://ffm.core.test.network',
    'https://nyc.core.test.network',
  ],
})

const sshKey = Wallet.create() // the key the machine will accept
const lease = await xe.openLease({
  provider,                      // an address from xe.client.providers()
  vcpus: 1, memoryMb: 1024, diskGb: 10,
  durationSecs: 60,
  accessPubKey: sshKey.publicKey,
})

// Renew a minute at a time until the term reaches ten minutes.
await holdLease(xe, lease.hash, { renewSecs: 60, totalSecs: 600 })

openLease prices the lease from the provider's current performance certificate, exactly as the ledger will. renewLease gathers timekeeper attestations, checks them locally and locks the current oracle epoch's emission parameters. holdLease renews ahead of each expiry and confirms every extension on the ledger before scheduling the next. cancelLease withdraws a lease the provider has not accepted; forceSettleLease reclaims the escrow of one the provider never settled.

Prices. You sign the exact amount of every lease and renewal, so nobody can charge you more than you signed for. On top of that the SDK refuses to sign a price you did not agree to: a price ceiling (maxPricePerMinute), renewals held at the price the lease opened at, and a total budget passed to holdLease. When a limit is hit the SDK stops renewing; the machine runs to the end of the minute already paid for and is then released. The full model is in docs/budgets.md.

[!NOTE] Paying for a lease needs XUSD Leases are priced in XUSD, which only the operators' sys.minter accounts issue and which the faucet does not hand out, so testers can read the compute market (xe.client.providers(), GET /leases) and run a provider, but cannot fund a lease themselves yet. See Assets.

Running a job

@xeprotocol/sdk/jobs (Node only) runs your code on a rented machine and brings the results home, holding the lease a minute at a time while it runs:

import { runJob } from '@xeprotocol/sdk/jobs'

const job = await runJob(xe, {
  machine: { vcpus: 1, memoryMb: 1024, diskGb: 1 },
  files: { 'main.py': 'print("hello")' },
  run: 'python3 main.py',
  budget: toMicro('0.01'),
})
console.log(job.status, job.stdout, fromMicro(job.paid))

Five complete programs, including every way a job can fail, are in examples/jobs/.

Tutorials

Step-by-step, in TypeScript and JavaScript, each ending in a complete program you can run against the testnet: examples/.

#TutorialYou will
1Your first walletinstall the SDK, create and keep a wallet, connect, read a balance
2Getting testnet fundsuse the faucet, claim pending transfers, wait for finality
3Sending XEsend with a memo, claim it on the other side, look up a block
4Errors and retriestell temporary failures from final ones and retry safely
5Renting a machinefind a provider, price a machine, rent it
6Holding a lease openkeep a machine as long as you need it, paying a minute at a time

Tutorials 1–4 need nothing but Node.js; 5–6 need XUSD.

What works today

AreaStatus
Wallets, addresses, signing✅
Canonical block encoding, hashing, proof of work✅ verified byte-for-byte against the node
Send, receive, burn✅
Balances, pending, chains, blocks, supply✅
Providers, leases, state chain (read)✅
Leasing a machine: open, renew, cancel, force-settle, hold a lease open✅
Price ceiling, renewal price lock, budgets, quotes✅
Jobs: run code on a leased machine and collect results (Node)✅ through the testnet SSH gateway
Published package🚧 not yet
Messaging and the account directory🚧 not yet — use the HTTP API
Other languages🚧 later — TypeScript first

Lower-level exports

Everything the high-level Xe class uses is exported, so you can build blocks yourself when you need to: XeClient (typed HTTP calls), Wallet, deriveAddress, verifySignature, hashBlock, signBlock, marshalCanonical, solvePow / validatePow, toMicro / fromMicro, toAddress / toPublicKey / toHash, and lossless JSON helpers (parseLossless, stringifyWithBigInts). The wire format they implement is documented under Encoding and Cryptography.

Development

npm install
npm run typecheck
npm run lint
npm test                    # unit tests, no network needed
npm run build
npm run test:integration    # against a public node; skips with a reason when none is reachable