Skip to content
Global documentation search

USC SDK

The @gluwa/usc-sdk is a TypeScript/JavaScript SDK for verifying cross-chain transactions on the Creditcoin network. It lets you generate inclusion proofs for transactions on supported source chains (e.g. Ethereum Sepolia) and verify them on-chain via Creditcoin’s precompile contracts.

Terminal window
npm install @gluwa/usc-sdk
# or
yarn add @gluwa/usc-sdk

The SDK requires ethers.js v6 as a peer dependency.


A transaction inclusion proof answers the question: “Did this transaction really happen on chain X?” It is made of two parts:

Part What it proves
Merkle proof The transaction is included in a specific block’s transaction tree
Continuity proof That block is part of a sequence of blocks anchored to an attestation point on Creditcoin

The SDK provides three main components you will work with:

  • ProverAPIProofGenerator — fetches pre-computed proofs from a hosted API (recommended starting point)
  • PrecompileChainInfoProvider — queries attestation state from Creditcoin
  • PrecompileBlockProver — submits proofs to Creditcoin’s on-chain verifier

You need two JSON-RPC providers: one for the source chain (where the transaction happened) and one for Creditcoin (where proofs are verified).

import { JsonRpcProvider } from 'ethers';
import { chainInfo, blockProver, proofGenerator } from '@gluwa/usc-sdk';
// Source chain (e.g. Ethereum Sepolia)
const sourceProvider = new JsonRpcProvider('https://sepolia.infura.io/v3/<api_key>'); //or other providers
// Creditcoin USC v2 Testnet, or CC3 Testnet
const creditcoinProvider = new JsonRpcProvider('https://rpc.usc-testnet.creditcoin.network'); //or CC3 Tesnet RPC, https://rpc.cc3-testnet.creditcoin.network

Use PrecompileChainInfoProvider to see which source chains are currently supported and find the chainKey for the chain you want to prove transactions from.

const chainInfoProvider = new chainInfo.PrecompileChainInfoProvider(creditcoinProvider);
const supportedChains = await chainInfoProvider.getSupportedChains();
console.log(supportedChains);
// [{ chainKey: 1, chainId: 11155111, chainName: 'Ethereum Sepolia', chainEncoding: 1 }, ...]

The chainKey is a Creditcoin-internal identifier for a source chain — it is not the same as the chain’s EVM chainId. You will need it in every subsequent call.


Before a proof can be generated, the block containing your transaction must be attested on Creditcoin. Attestation happens periodically and automatically; you just need to wait for it.

const txHash = '0x6fe777442b70a5511f3c443176ae860e50445bd93b663711717996a70c5022ab';
const chainKey = 1; // from Step 1
// Find which block the transaction is in
const tx = await sourceProvider.getTransaction(txHash);
const blockNumber = tx!.blockNumber!;
// We create a connection to the proof generation server. We listen
// for new attestations to be cached here rather than listening for
// them directly on-chain. This prevents request timing issues.
const proofGenApi = new proofGenerator.api.ProverAPIProofGenerator(
chainKey,
'https://prover.usc-testnet.creditcoin.network',
5000, // request timeout in ms (optional, default: 5000)
);
// Wait until Creditcoin has attested that block
await proofGenApi.waitUntilHeightAttested(chainKey, blockNumber);
console.log(`Block ${blockNumber} is attested — ready to generate proof`);

waitUntilHeightAttested polls the proofGenerationApi server at a configurable interval (default: 15 s) and resolves once the necessary attestation is present in the prover cache. It will throw after a configurable timeout (default: 15 m).


ProverAPIProofGenerator is the simplest way to get a proof. It calls a hosted API that computes and caches proofs on your behalf — no RPC-heavy local computation required.

const result = await proofGenApi.generateProof(txHash);
if (!result.success) {
throw new Error(`Proof generation failed: ${result.error}`);
}
const proofData = result.data!;
console.log('Block number:', proofData.headerNumber);
console.log('Transaction bytes:', proofData.txBytes);

The returned proofData object contains everything needed for on-chain verification:

Field Type Description
chainKey number Source chain identifier
headerNumber number Block number the transaction was in
txHash string Transaction hash
txBytes string ABI-encoded transaction
merkleProof TransactionMerkleProof Siblings in the block’s transaction Merkle tree
continuityProof ContinuityProof Chain of Merkle roots linking the block to an attestation
cached boolean Whether the proof was served from cache

If you need proofs for multiple transactions at once, use generateBatchProof. All transactions in a batch share a single continuity proof, which makes on-chain batch verification more efficient. The current MAX_BATCH_SIZE is 10 proofs, and these must be within a MAX_BATCH_RANGE of 1000 blocks.

const batchResult = await proofGenApi.generateBatchProof([txHash1, txHash2]);
if (!batchResult.success) {
throw new Error(`Batch proof generation failed: ${batchResult.error}`);
}
const batchData = batchResult.data!;

PrecompileBlockProver submits proofs to Creditcoin’s verifier precompile.

const prover = new blockProver.PrecompileBlockProver(creditcoinProvider);
const verified = await prover.verifySingle(
proofData.chainKey,
proofData.headerNumber,
proofData.txBytes,
proofData.merkleProof,
proofData.continuityProof,
);
console.log('Verification result:', verified ? 'SUCCESS' : 'FAILED');

When using batch proofs, you need to flatten the proof data into parallel arrays:

const headers: number[] = [];
const txBytesArr: string[] = [];
const merkleProofs = [];
for (const [headerNumber, proofsMap] of batchData.merkleProofs.entries()) {
for (const [, proofEntry] of proofsMap.entries()) {
headers.push(headerNumber);
txBytesArr.push(proofEntry.txBytes);
merkleProofs.push(proofEntry.merkleProof);
}
}
const batchVerified = await prover.verifyBatch(
batchData.chainKey,
headers,
txBytesArr,
merkleProofs,
batchData.continuityProof,
);
console.log('Batch verification result:', batchVerified ? 'SUCCESS' : 'FAILED');

import { JsonRpcProvider } from 'ethers';
import { chainInfo, blockProver, proofGenerator } from '@gluwa/usc-sdk';

async function proveTransaction(txHash: string) {
  // Resolve chain key
  const chainKey = 1; // Ethereum Sepolia on USC Testnet2 and CC3 Testnet
  
  // Providers
  const sourceProvider = new JsonRpcProvider('https://sepolia.infura.io/v3/<api_key>'); //or other providers
  const creditcoinProvider = new JsonRpcProvider('https://rpc.usc-testnet2.creditcoin.network');  //or CC3 Tesnet RPC, https://rpc.cc3-testnet.creditcoin.network

  const chainInfoProvider = new chainInfo.PrecompileChainInfoProvider(creditcoinProvider);
  const prover = new blockProver.PrecompileBlockProver(creditcoinProvider);
  const proofGenApi = new proofGenerator.api.ProverAPIProofGenerator(
    chainKey,
    'https://prover.usc-testnet.creditcoin.network',
  );

  // Find block and wait for attestation
  const tx = await sourceProvider.getTransaction(txHash);
  await proofGenApi.waitUntilHeightAttested(chainKey, tx!.blockNumber!);

  // Generate proof via API
  const result = await proofGenApi.generateProof(txHash);

  if (!result.success || !result.data) {
    throw new Error(`Proof generation failed: ${result.error}`);
  }

  const { chainKey: ck, headerNumber, txBytes, merkleProof, continuityProof } = result.data;

  // Verify on-chain
  const verified = await prover.verifySingle(ck, headerNumber, txBytes, merkleProof, continuityProof);
  console.log('Proof verification:', verified ? 'SUCCESS' : 'FAILED');

  return verified;
}

For advanced use cases where you need full control (e.g. running your own indexer, offline proof computation, or custom block providers), the SDK also ships a RawProofGenerator that computes proofs locally by fetching data directly from source chain RPCs.

import { EncodingVersion } from '@gluwa/usc-sdk/encoding';
const blockProvider = new proofGenerator.raw.blockProvider.SimpleBlockProvider(sourceProvider);
const rawGenerator = new proofGenerator.raw.RawProofGenerator(
chainKey,
blockProvider,
chainInfoProvider,
EncodingVersion.V1,
);
const result = await rawGenerator.generateProof(txHash);

Both RawProofGenerator and ProverAPIProofGenerator implement the same ProofGenerator interface and produce identical output, so you can swap between them without changing any downstream code.