lua_evalsaved

Execute a previously saved Lua script by its SHA256 hash.

Method

lua_evalsaved

Parameters

  • 0 (string): Script SHA256 hash (from lua_savescript)
  • 1+ (any): Optional arguments (accessible via args table)

Request

{
  "jsonrpc": "2.0",
  "method": "lua_evalsaved",
  "params": [
    "a1b2c3d4e5f6789012345678901234567890123456789012345678901234abcd",
    "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
  ],
  "id": 1
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "calls": 1,
    "returns": 5,
    "error": null,
    "runtime": 32
  },
  "id": 1
}

Response Fields

  • calls (number): Number of RPC calls made during execution
  • returns (any): Return value from the Lua script
  • error (object/null): Error information if execution failed
  • runtime (number): Execution time in milliseconds

Error: Script Not Found

If the script hash is not in storage:

{
  "jsonrpc": "2.0",
  "result": {
    "calls": 0,
    "returns": null,
    "error": {
      "code": -32602,
      "message": "Script not found for hash: a1b2c3d4..."
    },
    "runtime": 0
  },
  "id": 1
}

Complete Workflow Example

Step 1: Save the Script

{
  "jsonrpc": "2.0",
  "method": "lua_savescript",
  "params": [
    "local address = args[1]\nlocal ord_outputs = _RPC.ord_outputs(address)\nlocal inscriptions = {}\nfor _, output in ipairs(ord_outputs) do\n  if output.inscriptions then\n    for _, insc in ipairs(output.inscriptions) do\n      table.insert(inscriptions, insc)\n    end\n  end\nend\nreturn { count = #inscriptions, inscriptions = inscriptions }"
  ],
  "id": 1
}

Response:

{
  "jsonrpc": "2.0",
  "result": {
    "hash": "7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730"
  },
  "id": 1
}

Step 2: Execute Multiple Times

First execution:

{
  "jsonrpc": "2.0",
  "method": "lua_evalsaved",
  "params": [
    "7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730",
    "bc1pxyz..."
  ],
  "id": 2
}

Second execution with different address:

{
  "jsonrpc": "2.0",
  "method": "lua_evalsaved",
  "params": [
    "7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730",
    "bc1pabc..."
  ],
  "id": 3
}

JavaScript Example with Fallback

async function executeScript(scriptCode, ...args) {
  const crypto = require('crypto');
  const hash = crypto.createHash('sha256').update(scriptCode).digest('hex');
  
  // Try evalsaved first
  try {
    const response = await rpc('lua_evalsaved', [hash, ...args]);
    if (!response.error || !response.error.message.includes('not found')) {
      return response;
    }
  } catch (e) {
    // Script not saved, continue to save it
  }
  
  // Save the script
  await rpc('lua_savescript', [scriptCode]);
  
  // Now execute with evalsaved
  return await rpc('lua_evalsaved', [hash, ...args]);
}

// Usage
const script = `
  local address = args[1]
  local utxos = _RPC.esplora_addressutxo(address)
  return #utxos
`;

const result = await executeScript(script, 'bc1q...');
console.log('UTXO count:', result.returns);

Advantages Over lua_evalscript

  • Request size: lua_evalscript sends full script, lua_evalsaved sends 64-char hash
  • Parse time: lua_evalscript parses every request, lua_evalsaved parses once on save
  • Network usage: lua_evalsaved is lower bandwidth
  • Best for: lua_evalscript for development, lua_evalsaved for production

Caching Strategy

For production applications:

class ScriptCache {
  constructor() {
    this.scripts = new Map();
    this.hashes = new Map();
  }

  register(name, code) {
    const hash = this.computeHash(code);
    this.scripts.set(name, { code, hash, saved: false });
    this.hashes.set(hash, name);
  }

  computeHash(code) {
    return require('crypto').createHash('sha256').update(code).digest('hex');
  }

  async ensureSaved(rpcClient, name) {
    const script = this.scripts.get(name);
    if (!script) throw new Error(`Unknown script: ${name}`);
    
    if (!script.saved) {
      await rpcClient.call('lua_savescript', [script.code]);
      script.saved = true;
    }
    
    return script.hash;
  }

  async run(rpcClient, name, ...args) {
    const hash = await this.ensureSaved(rpcClient, name);
    return await rpcClient.call('lua_evalsaved', [hash, ...args]);
  }
}

// Initialize on startup
const cache = new ScriptCache();
cache.register('getBalance', `
  local addr = args[1]
  local utxos = _RPC.esplora_addressutxo(addr)
  local sum = 0
  for _, u in ipairs(utxos) do sum = sum + u.value end
  return sum
`);

// Use throughout application
const balance = await cache.run(rpc, 'getBalance', 'bc1q...');