Skip to content

Latest commit

 

History

History
200 lines (148 loc) · 7.61 KB

README.md

File metadata and controls

200 lines (148 loc) · 7.61 KB

bindings JS

JS library for interacting with Soroban smart contract bindings via Soroban RPC.

This library was automatically generated by Soroban CLI using a command similar to:

soroban contract bindings ts \
  --rpc-url INSERT_RPC_URL_HERE \
  --network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \
  --contract-id INSERT_CONTRACT_ID_HERE \
  --output-dir ./path/to/bindings

The network passphrase and contract ID are exported from index.ts in the networks constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update networks with other valid options. This will help your contract consumers use this library more easily.

To publish or not to publish

This library is suitable for publishing to NPM. You can publish it to NPM using the npm publish command.

But you don't need to publish this library to NPM to use it. You can add it to your project's package.json using a file path:

"dependencies": {
  "bindings": "./path/to/this/folder"
}

However, we've actually encountered frustration using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your node_modules folder automatically after each install by using a postinstall script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the install step, and then regenerate them when it gets to your postinstall step, which will keep the library up-to-date with your contract.

"scripts": {
  "postinstall": "soroban contract bindings ts --rpc-url INSERT_RPC_URL_HERE --network-passphrase \"INSERT_NETWORK_PASSPHRASE_HERE\" --id INSERT_CONTRACT_ID_HERE --name bindings"
}

Obviously you need to adjust the above command based on the actual command you used to generate the library.

Use it

Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods:

import { Contract, networks } from "bindings"

const contract = new Contract({
  ...networks.futurenet, // for example; check which networks this library exports
  rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers
})

contract.|

As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that | to get a list of all exports and inline-documentation for each. It exports a separate async function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code.

Decode contract errors

When a transaction fails, the contract returns a u32 error code. Use the helpers below to translate that code into a structured result or a user-facing string suitable for wallet UX.

import { decodeContractError, formatContractError } from "bindings"

// Structured result for programmatic handling
const decoded = decodeContractError(79)
if (decoded) {
  console.log(`Error ${decoded.code}: ${decoded.variant} — ${decoded.message}`)
  // => "Error 79: AccessDenied — AccessDenied"
}

// One-liner for display in a wallet or bot
console.log(formatContractError(9) ?? "Unknown error")
// => "InsufficientBalance (code 9)"

Use decodeContractError to surface contextual information in your wallet or bot, and fall back to "Unknown contract error (code N)" for codes that the current bindings do not yet recognise.

High-level helpers

The @xelma/bindings/helpers module provides typed convenience wrappers around the generated client for common wallet flows.

mintIfNeeded

Checks a user's vXLM balance and mints initial tokens if the balance is zero. One-time per user (enforced by the contract).

import { mintIfNeeded } from "@xelma/bindings/helpers"

const { minted, balance } = await mintIfNeeded(client, "GABCDEF123...")
if (minted) {
  console.log(`Minted initial tokens. Balance: ${balance}`)
}

placeBetChecked

Places a bet after running pre-flight validation:

  1. Contract is not paused
  2. An active round exists
  3. User has sufficient balance

Throws typed exceptions for known failure modes.

import { placeBetChecked } from "@xelma/bindings/helpers"

try {
  const tx = await placeBetChecked(client, {
    user: "GABCDEF123...",
    amount: 500_000_000n,   // 50 vXLM (7-digit precision)
    side: { tag: "Up", values: undefined },
  })
  console.log("Bet placed!")
} catch (err) {
  if (err instanceof ContractPausedError) {
    console.log("Betting is paused right now.")
  } else if (err instanceof InsufficientBalanceError) {
    console.log("Not enough vXLM — try minting first.")
  } else if (err instanceof NoActiveRoundError) {
    console.log("No round is currently active.")
  } else {
    console.error("Unexpected error:", err)
  }
}

claimIfPending

Checks for pending winnings and claims them if any exist.

import { claimIfPending } from "@xelma/bindings/helpers"

const { claimed, amount } = await claimIfPending(client, "GABCDEF123...")
if (claimed) {
  console.log(`Claimed ${amount} vXLM`)
}

simulateBet

Simulates a bet without broadcasting to estimate resource fees and decode errors.

import { simulateBet } from "@xelma/bindings/helpers"

const { simulated, minResourceFee, error } = await simulateBet(client, {
  user: "GABCDEF123...",
  amount: 500_000_000n,
  side: { tag: "Up", values: undefined },
})
if (simulated) {
  console.log(`Estimated min resource fee: ${minResourceFee}`)
} else {
  console.error("Simulation failed:", error)
}

Error reference

Exception Contract code Meaning
InsufficientBalanceError 9 Not enough vXLM to place bet
NoActiveRoundError 7 No round is currently active
ContractPausedError 22 Contract paused for maintenance
AlreadyBetError 10 User already bet in this round
StakeExceedsMaxError 28 Bet exceeds the max stake cap
ExposureCapExceededError 29 User exposure exceeds round cap
NoRoundTemplateError 65 No round template configured

All typed exceptions extend XelmaError (which extends Error), so you can catch specific types or the base class.

Error-code parity in CI

The error codes above are duplicated in three places:

  • Rust: the contract's #[contracterror] enum
  • TypeScript: bindings/src/index.ts (the generated ContractError enum)
  • Docs: the "Error reference" table in this README

These can drift apart silently. CI runs bindings/scripts/check-error-parity.mjs, which parses all three sources and fails the build if any code, variant name, or message disagrees.

Fixing a parity failure

  1. Read the CI log — it prints each mismatch as code <n>: <source> says ....
  2. Update the source that is wrong. Usually the Rust enum is authoritative; regenerate the TS bindings with the soroban contract bindings ts command shown at the top of this file.
  3. Update the "Error reference" table above so the code, exception name, and meaning match.
  4. Re-run node bindings/scripts/check-error-parity.mjs locally until it exits 0.