{"_id":"@clove-labs/txmill-sdk","name":"@clove-labs/txmill-sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@clove-labs/txmill-sdk","version":"0.1.0","description":"TypeScript client for the txmill EVM transaction relayer (caller-side).","license":"MIT","type":"module","engines":{"node":">=20"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"devDependencies":{"@types/node":"^22.0.0","tsup":"^8.0.0","typescript":"^5.4.0","vitest":"^2.0.0"},"keywords":["txmill","evm","sonic","relayer","ethereum"],"_id":"@clove-labs/txmill-sdk@0.1.0","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-5Hshh7gN+s6f13m+t/vMAiqNNpdsGTCs5w/wOT52Fg3/dpoKRegp75sRjQNd2FgsDNw7VR57oNGOuAuMpcYgAw==","shasum":"ff3d116f6e1a5e4673c34cfceca97358bf2fc53e","tarball":"https://registry.npmjs.org/@clove-labs/txmill-sdk/-/txmill-sdk-0.1.0.tgz","fileCount":9,"unpackedSize":80929,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFog1RA7sJa/bA1RiDGKbPkiXCux19NzsGHMKGATuoUXAiEA/xGKUs1Uspo8lK+h4WVSE5udZaS4xkWSkE5FGT2QZ2w="}]},"_npmUser":{"name":"clovelabs","email":"tech@clove.trade"},"directories":{},"maintainers":[{"name":"clovelabs","email":"tech@clove.trade"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/txmill-sdk_0.1.0_1777979384078_0.7217050227265767"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-05T11:09:44.001Z","0.1.0":"2026-05-05T11:09:44.234Z","modified":"2026-05-05T11:09:44.694Z"},"maintainers":[{"name":"clovelabs","email":"tech@clove.trade"}],"description":"TypeScript client for the txmill EVM transaction relayer (caller-side).","keywords":["txmill","evm","sonic","relayer","ethereum"],"license":"MIT","readme":"# @clove-labs/txmill-sdk\n\nTypeScript client for the [txmill](https://github.com/Clove-Labs/txmill) EVM transaction relayer.\n\nThis SDK is for **caller-side integration**. It assumes you have already been issued a `bearerToken` (and optionally a `defaultCallbackSecret`) by the txmill operator. App creation is admin-side; this SDK does not expose it.\n\n## Install\n\n```bash\nnpm i @clove-labs/txmill-sdk\n```\n\nRequires **Node.js 20+** (uses native `fetch` and `node:crypto`).\n\n## Quick start\n\n```ts\nimport { Txmill } from \"@clove-labs/txmill-sdk\";\n\nconst txmill = new Txmill({\n  baseUrl: \"https://txmill-production.up.railway.app\",\n  bearerToken: process.env.TXMILL_TOKEN!,\n});\n\nconst submit = await txmill.submit({\n  chainId: 146,\n  to: \"0xYourContract\",\n  data: \"0xa9059cbb...\",\n  value: 0n,                       // bigint, defaults to 0n\n  callbackMetadata: \"order=42\",    // echoed back in status\n});\nconsole.log(submit.requestId, submit.txHash, submit.signer);\n\n// Poll until terminal (or set up a webhook — see below)\nconst final = await txmill.waitForTerminalStatus(submit.requestId, {\n  intervalMs: 1000,\n  timeoutMs: 60_000,\n});\nconsole.log(final.status, final.blockNumber, final.gasUsed);\n```\n\n## What this SDK does\n\n- `submit(input)` — send a relay request.\n- `getStatus(requestId)` — fetch the current lifecycle state.\n- `waitForTerminalStatus(requestId, opts)` — poll `getStatus` until `confirmed | reverted | rejected | failed`, with configurable interval and timeout.\n- `getBalances(appId)` — live on-chain balance of the treasury + every signer in your pool.\n- `listSigners(appId)` — recover the signer list (useful if the original create response was lost).\n- `Txmill.verifyWebhook(rawBody, signatureHeader, secret)` — static helper to verify HMAC-signed webhook deliveries.\n\nAll `uint256` values cross the API boundary as `bigint` (sent as decimal strings on the wire). Field naming is camelCase in TS land; the SDK translates to/from snake_case JSON on every request.\n\n## What this SDK does NOT do\n\n- **No app creation.** `POST /v1/apps` is operator-side. The operator gives you a token; this SDK starts from there.\n- **No automatic retry on `502` / `5xx`.** When `submit()` returns a `TxmillUpstreamError`, the relay request **may have already been persisted server-side** even though no `requestId` came back to you. Auto-retrying risks duplicate requests with no idempotency key. Surface the error to the caller and decide deliberately.\n- **No browser support.** This package uses `node:crypto` for `verifyWebhook` and assumes Node 20+ globals. A browser-friendly subpackage is possible later if needed.\n\n## Webhook verification\n\ntxmill `POST`s status changes to your callback URL with these headers:\n\n```\nContent-Type:        application/json\nX-Txmill-Signature:  sha256=<hex>\nX-Txmill-Delivery:   <delivery-uuid>\nX-Txmill-Attempt:    <0-indexed>\n```\n\nVerify with the **raw request body bytes** — never a re-serialized object. If your framework's body-parser middleware mutates the body, capture the raw bytes BEFORE parsing.\n\n```ts\nimport express from \"express\";\nimport { Txmill } from \"@clove-labs/txmill-sdk\";\n\nconst app = express();\nconst SECRET = process.env.TXMILL_WEBHOOK_SECRET!;\n\n// IMPORTANT: use raw to keep the exact bytes for HMAC verification.\napp.post(\"/txmill-cb\", express.raw({ type: \"application/json\" }), (req, res) => {\n  const sig = req.header(\"X-Txmill-Signature\");\n  if (!Txmill.verifyWebhook(req.body as Buffer, sig, SECRET)) {\n    return res.status(401).send(\"bad signature\");\n  }\n  const payload = JSON.parse((req.body as Buffer).toString(\"utf8\"));\n  // ...handle payload (deduplicate on (request_id, status); honor updated_at as freshness)\n  res.sendStatus(200);\n});\n```\n\n## Error handling\n\nEvery non-2xx is mapped to a typed error subclass. Each carries `status`, `message` (parsed from the server's `{message: ...}` body when present), and `body` (raw response text).\n\n```ts\nimport {\n  Txmill,\n  TxmillValidationError,\n  TxmillAuthError,\n  TxmillNotFoundError,\n  TxmillUpstreamError,\n  TxmillTimeoutError,\n} from \"@clove-labs/txmill-sdk\";\n\ntry {\n  await txmill.submit({ chainId: 1, to: \"0x0\", data: \"0x\" });\n} catch (err) {\n  if (err instanceof TxmillValidationError) {\n    // 400 — bad input. Don't retry.\n  } else if (err instanceof TxmillAuthError) {\n    // 401/403 — token is missing, invalid, or app disabled.\n  } else if (err instanceof TxmillUpstreamError) {\n    // 502/503 — RPC failure. NOTE: the request may have been persisted server-side.\n  } else if (err instanceof TxmillTimeoutError) {\n    // waitForTerminalStatus exceeded its timeout.\n  } else {\n    // Network error or unexpected status.\n  }\n}\n```\n\n## Notes on the wire format\n\n- `value`, `effectiveGasPrice`, `balanceWei` are `bigint` in the SDK and **decimal strings** on the wire (JSON numbers can't safely hold uint256).\n- `blockNumber`, `gasUsed`, `gasLimit`, `deadline` are `number` (fit `int64`).\n- `Address` is `0x${string}`. Inputs accept any case; responses are EIP-55 checksummed.\n- `data` accepts `0x`-prefixed or bare hex; `\"\"` and `\"0x\"` both mean empty calldata.\n\n## Reference\n\nFor the underlying API contract — every endpoint, every field, the status state machine, the operational footguns — see the txmill integration guide:\n\n[`docs/integration.md`](https://github.com/Clove-Labs/txmill/blob/main/docs/integration.md) in the txmill repo.\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-93e2b26be28d4e65733374415314bda8"}