Skip to main content

Sign Raw Bytes

POST /v1/wallets/:walletId/sign-raw Headers:
  • X-API-Key: sk_...
  • X-Request-Id (optional)
Body:
{
  "data": "0xdeadbeef"
}
Response:
{
  "signature": "0x..."
}
Example (cURL):
curl -X POST "https://api.sandbox.getpara.com/v1/wallets/$WALLET_ID/sign-raw" \
  -H "X-API-Key: sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "data": "0xdeadbeef" }'
Notes:
  • data must be hex-encoded bytes (include the 0x prefix and ensure even length).
  • Sign only after the wallet is ready (key generation complete).
  • The response returns the 0x-prefixed signature produced by the enclave. For secp256k1 wallets, the signature already encodes any required r/s/v values.
  • See the Endpoints reference for error codes and status codes.

Sign and Send a Sepolia Transaction

Prereqs

  • Wallet id + address from the wallet guide, funded with Sepolia ETH.
  • Sepolia RPC endpoint and a recipient address.
  1. Read nonce and fee data from Sepolia.
  2. Build a type-2 transaction, hash it, and call sign-raw.
  3. Serialize with the returned signature and broadcast.
TypeScript example (Node 18+, ethers v6):
import {
  JsonRpcProvider,
  Signature,
  Transaction,
  keccak256,
  parseEther,
  parseUnits,
  serializeTransaction
} from "ethers";

const PARA_BASE_URL = "https://api.sandbox.getpara.com";
const PARA_API_KEY = process.env.PARA_API_KEY!;
const PARA_WALLET_ID = process.env.PARA_WALLET_ID!;
const PARA_WALLET_ADDRESS = process.env.PARA_WALLET_ADDRESS!;
const RECIPIENT = process.env.SEPOLIA_RECIPIENT!;
const provider = new JsonRpcProvider(process.env.SEPOLIA_RPC_URL!);

async function main() {
  const nonce = await provider.getTransactionCount(PARA_WALLET_ADDRESS, "latest");
  const feeData = await provider.getFeeData();

  const unsignedTx = {
    type: 2,
    chainId: 11155111,
    to: RECIPIENT,
    nonce,
    value: parseEther("0.001"),
    gasLimit: 21_000,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? parseUnits("1.5", "gwei"),
    maxFeePerGas: feeData.maxFeePerGas ?? parseUnits("40", "gwei"),
    data: "0x"
  };

  const unsignedSerialized = Transaction.from(unsignedTx).unsignedSerialized;
  const digest = keccak256(unsignedSerialized);

  const res = await fetch(`${PARA_BASE_URL}/v1/wallets/${PARA_WALLET_ID}/sign-raw`, { method: "POST", headers: { "X-API-Key": PARA_API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ data: digest }) });

  if (!res.ok) {
    throw new Error(`sign-raw failed: ${res.status} ${await res.text()}`);
  }

  const { signature } = await res.json();
  const rawTx = serializeTransaction(unsignedTx, Signature.from(signature));
  const broadcast = await provider.broadcastTransaction(rawTx);

  console.log(`Sepolia tx hash: ${broadcast.hash}`);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
Signature.from(signature) expands to r / s / v so broadcastTransaction can send it. Swap the RPC URL and chainId for other networks.