Skip to main content

Minimal React Hook (EVM)

const bridge = useCallback(async ({ amount, destinationDomain, isNative }) => {
  assertWalletConnected();

  const quote = await fetchEcdsaQuote({
    localDomain,
    destinationDomain,
    isV2: true,
    isNative,
  });

  const params = buildTransferV2Params({
    amount,
    destinationDomain,
    quote,
    isNative,
    recipient: toBytes32(destinationAddress),
  });

  await ensureAllowance(params, isNative);
  const overrides = isNative ? { value: params.fee + params.gasDropAmount } : {};
  return cashmere.transferV2(params, overrides);
}, [provider, cashmere, usdc]);
Wrap the helper utilities (fetchEcdsaQuote, buildTransferV2Params, ensureAllowance) around your existing network stack so that decimals and deadlines are handled consistently.

Node.js Treasury Distributor

async function distribute({ amount, fromDomain, routes }) {
  const signer = buildSigner(fromDomain);
  const cashmere = loadCashmereContract(fromDomain, signer);
  const usdc = loadUsdcContract(fromDomain, signer);

  const allowance = computeAllowance({ amount, routes });
  await usdc.approve(cashmere.target, allowance);

  for (const route of routes) {
    const quote = await fetchEcdsaQuote({
      localDomain: fromDomain,
      destinationDomain: route.domain,
      isV2: true,
      isNative: route.isNativeFee,
    });

    const params = buildTransferV2Params({ amount, quote, recipient: route.recipient });
    const overrides = route.isNativeFee ? { value: params.fee + params.gasDropAmount } : {};
    await cashmere.transferV2(params, overrides);
  }
}
Fetch Circle’s minimumFee guidance per domain pair through https://iris-api.circle.com/v2/burn/USDC/fees/{sourceDomainId}/{destDomainId} and map the result to your maxFee / minFinalityThreshold selection.

Status Dashboard Snippet

async function fetchRecentTransfers(address) {
  const url = new URL('https://kapi.cashmere.exchange/transactionsmainnet');
  url.searchParams.set('senders', address);
  url.searchParams.set('limit', '25');

  const res = await fetch(url.toString());
  if (!res.ok) throw new Error('Failed to fetch transactions');
  const body = await res.json();

  return body.transactions.map((tx) => ({
    hash: tx.source_tx_hash,
    route: `${tx.source_domain}${tx.destination_domain}`,
    amount: Number(tx.deposit_amount) / 1e6, // display helper (USDC = 6 decimals)
    relayerFee: Number(tx.relayer_fee) / 1e6,
    status: tx.confirmed ? 'confirmed' : 'pending',
    completed: tx.confirmed,
  }));
}

Solana Transfer (Anchor)

const quote = await fetchEd25519Quote({ localDomain: 5, destinationDomain, version: 2 });
const config = await loadCashmereConfig(connection);

const signatureIx = buildEd25519Ix(quote);
const transferIx = await cashmereProgram.methods.transferV2(
  new BN(amount.toString()),
  destinationDomain,
  toBytes(recipientAta),
  toBytes(destinationOwner),
  new BN(quote.fee),
  new BN(quote.deadline),
  new BN(gasDropLamports.toString()),
  isNativeFee,
  new BN(maxFee.toString()),
  new BN(finalityThreshold),
).accounts(buildAccountMap({ owner, config, destinationDomain })).instruction();

const tx = assembleAndSign({
  payer: owner,
  ixs: [setComputeUnits(), signatureIx, transferIx],
  lookupTables: [cashmereLookupTable],
  additionalSigners: [cashmereMessageSigner],
});

await connection.sendAndConfirmTransaction(tx);

Sui Transfer (Transactions API)

const quote = await fetchEd25519Quote({ localDomain: 8, destinationDomain, isNative });

const tx = new Transaction();
const modules = loadSuiConstants(); // resolve module IDs & on-chain object IDs from configuration
const usdcCoin = coinWithBalance({ type: modules.usdcType, balance: burnAmountPlusGasDrop });
const nativeFeeCoin = buildNativeFeeCoin(tx, { quote, gasDropNative, isNative });

const [ticket, depositInfo] = tx.moveCall({
  target: `${modules.cashmereTransfer}::prepare_deposit_for_burn_ticket`,
  typeArguments: [modules.usdcType],
  arguments: buildPrepareArgs({ quote, nativeFeeCoin, recipientHex, solanaOwnerHex, modules }),
});

const [burnMessage, circleMessage] = tx.moveCall({
  target: `${modules.tokenMessenger}::deposit_for_burn_with_package_auth`,
  typeArguments: [modules.usdcType, `${modules.cashmereTransfer}::Auth`],
  arguments: buildDepositArgs({ ticket, modules }),
});

tx.moveCall({
  target: `${modules.cashmereTransfer}::post_deposit_for_burn`,
  arguments: [burnMessage, circleMessage, depositInfo, tx.object(modules.configId)],
});

await wallet.signAndExecuteTransaction({ transaction: tx });
  • recipientHex / solanaOwnerHex are 32-byte hex strings. Use left-padded bytes for EVM/Sui/Aptos destinations and the wallet public key when bridging into Solana.
  • When fee_is_native=true, convert SUI to 9-decimal “mist” and pass fee + gas_drop_amount inside nativeFeeCoin.

Aptos Transfer (ts-sdk)

const quote = await fetchEd25519Quote({ localDomain: 9, destinationDomain, isNative });
const payload = buildTransferOuterPayload({
  amount,
  destinationDomain,
  recipientHex,
  solanaOwnerHex,
  quote,
  isNative,
  gasDropMicro,
});
// buildTransferOuterPayload should reference process.env.APTOS_CASHMERE_MODULE and USDC type constants rather than embedding literal addresses.

const tx = await aptos.transaction.build.simple({ sender, data: payload });
const signer = selectWalletSigner();
const submission = await signer.signAndSubmit(tx);
await aptos.waitForTransaction({ transactionHash: submission.hash });
  • recipientHex / solanaOwnerHex are 32-byte hex strings. Left-pad EVM/Aptos/Sui addresses and use the Solana wallet address only when bridging into Solana.
  • Pontem expects the Ed25519 signature as a byte array; Array.from(Buffer.from(...)) satisfies that requirement.
Always validate the deadline returned by the Gas API. If the quote is within ~30 seconds of expiring, request a fresh signature before prompting the user to sign.