Source

lib/bindings/wasm_fetcher.js

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.WasmFetchError = void 0;
exports.fetchFromContractId = fetchFromContractId;
exports.fetchFromWasmHash = fetchFromWasmHash;
var _stellarBase = require("@stellar/stellar-base");
/**
 * Types of contract data that can be fetched
 */

/**
 * Errors that can occur during WASM fetching
 */
class WasmFetchError extends Error {
  constructor(message, cause) {
    super(message);
    this.cause = cause;
    this.name = "WasmFetchError";
  }
}

/**
 * Get WASM bytes from a WASM hash on the network
 */
exports.WasmFetchError = WasmFetchError;
async function getRemoteWasmFromHash(server, hashBuffer) {
  try {
    // Create the ledger key for the contract code
    const contractCodeKey = _stellarBase.xdr.LedgerKey.contractCode(new _stellarBase.xdr.LedgerKeyContractCode({
      hash: _stellarBase.xdr.Hash.fromXDR(hashBuffer, "raw")
    }));

    // Get the ledger entries
    const response = await server.getLedgerEntries(contractCodeKey);
    if (!response.entries || response.entries.length === 0) {
      throw new WasmFetchError("WASM not found for the given hash");
    }
    const entry = response.entries[0];
    if (entry.key.switch() !== _stellarBase.xdr.LedgerEntryType.contractCode()) {
      throw new WasmFetchError("Invalid ledger entry type returned");
    }
    const contractCode = entry.val.contractCode();
    return Buffer.from(contractCode.code());
  } catch (error) {
    if (error instanceof WasmFetchError) {
      throw error;
    }
    throw new WasmFetchError("Failed to fetch WASM from hash", error);
  }
}

/**
 * Check if a contract is a Stellar Asset Contract
 */
function isStellarAssetContract(instance) {
  // Check if it's a Stellar Asset Contract (has no WASM hash)
  return instance.executable().switch() === _stellarBase.xdr.ContractExecutableType.contractExecutableStellarAsset();
}

/**
 * Fetch WASM bytes from a deployed contract
 */
async function fetchWasmFromContract(server, contractAddress) {
  try {
    const contract = new _stellarBase.Contract(contractAddress.toString());
    const response = await server.getLedgerEntries(contract.getFootprint());
    if (!response.entries || response.entries.length === 0) {
      throw new WasmFetchError("Contract instance not found");
    }
    const entry = response.entries[0];
    if (entry.key.switch() !== _stellarBase.xdr.LedgerEntryType.contractData()) {
      throw new WasmFetchError("Invalid ledger entry type returned");
    }
    const contractData = entry.val.contractData();
    const instance = contractData.val().instance();
    if (isStellarAssetContract(instance)) {
      return {
        type: "stellar-asset-contract"
      };
    }
    const wasmHash = instance.executable().wasmHash();
    let wasmBytes = await getRemoteWasmFromHash(server, wasmHash);
    return {
      type: "wasm",
      wasmBytes
    };
  } catch (error) {
    if (error instanceof WasmFetchError) {
      throw error;
    }
    throw new WasmFetchError("Failed to fetch WASM from contract", error);
  }
}

/**
 * Fetch WASM from network using WASM hash
 */
async function fetchFromWasmHash(wasmHash, rpcServer) {
  try {
    // Validate and decode the hash
    const hashBuffer = Buffer.from(wasmHash, "hex");
    if (hashBuffer.length !== 32) {
      throw new WasmFetchError(`Invalid WASM hash length: expected 32 bytes, got ${hashBuffer.length}`);
    }

    // Get WASM from hash
    const wasmBytes = await getRemoteWasmFromHash(rpcServer, hashBuffer);
    return {
      type: "wasm",
      wasmBytes
    };
  } catch (error) {
    throw new WasmFetchError(`Failed to fetch WASM from hash ${wasmHash}`, error);
  }
}

/**
 * Fetch WASM from network using contract ID
 */
async function fetchFromContractId(contractId, rpcServer) {
  try {
    if (!_stellarBase.StrKey.isValidContract(contractId)) {
      throw new WasmFetchError(`Invalid contract ID: ${contractId}`);
    }
    // Parse contract address
    const contractAddress = _stellarBase.Address.fromString(contractId);

    // Try to get WASM from contract
    return await fetchWasmFromContract(rpcServer, contractAddress);
  } catch (error) {
    throw new WasmFetchError(`Failed to fetch WASM from contract ${contractId}`, error);
  }
}