{"_id":"@aori/usdm-bridge-sdk","name":"@aori/usdm-bridge-sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aori/usdm-bridge-sdk","version":"0.1.0","description":"Headless TypeScript SDK for the LayerZero VT API powering Aori USDM bridging — quote, swap, and status tracking without UI.","license":"UNLICENSED","type":"module","private":false,"publishConfig":{"access":"public"},"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"},"./ethers":{"types":"./dist/ethers.d.ts","import":"./dist/ethers.js","require":"./dist/ethers.cjs"}},"scripts":{"build":"tsup","dev":"tsup --watch","type-check":"tsc --noEmit","prepublishOnly":"npm run build"},"peerDependencies":{"viem":"^2","ethers":"^6"},"peerDependenciesMeta":{"ethers":{"optional":true}},"devDependencies":{"@types/node":"^22","ethers":"^6","tsup":"^8.4.0","typescript":"^5","viem":"^2"},"_id":"@aori/usdm-bridge-sdk@0.1.0","gitHead":"cee7b526885de5184b7f2fea05f0f37cbf79602f","_nodeVersion":"24.2.0","_npmVersion":"11.3.0","dist":{"integrity":"sha512-0IlRZBQ2+3aMkU9U7pbP6+x9lCPQQtCPvIm0gllofGfUcBKmujUFbYdnrbDJCgmuUuSvA6Ip94Ulof9d/IzhqQ==","shasum":"5e016777966409443eac1eec68d17b358c65e14c","tarball":"https://registry.npmjs.org/@aori/usdm-bridge-sdk/-/usdm-bridge-sdk-0.1.0.tgz","fileCount":12,"unpackedSize":147587,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIG4gShkgrbQK8FjDJK2z8KhEpebJddijVbBBVjulb9iDAiEA7r6P5//RwJyB52JTPK/KOuNxmiWzcAwVJOoV29FCBv4="}]},"_npmUser":{"name":"flooreyes","email":"wyattcravatas@gmail.com"},"directories":{},"maintainers":[{"name":"flooreyes","email":"wyattcravatas@gmail.com"},{"name":"hmsimha","email":"hartsimha@gmail.com"},{"name":"adammac7","email":"adam@aori.io"},{"name":"aduliba","email":"andrew@aori.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/usdm-bridge-sdk_0.1.0_1777499744922_0.9917064561260744"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-29T21:55:44.689Z","0.1.0":"2026-04-29T21:55:45.071Z","modified":"2026-04-29T21:55:45.452Z"},"maintainers":[{"name":"flooreyes","email":"wyattcravatas@gmail.com"},{"name":"hmsimha","email":"hartsimha@gmail.com"},{"name":"adammac7","email":"adam@aori.io"},{"name":"aduliba","email":"andrew@aori.io"}],"description":"Headless TypeScript SDK for the LayerZero VT API powering Aori USDM bridging — quote, swap, and status tracking without UI.","license":"UNLICENSED","readme":"# @aori/usdm-bridge-sdk\n\nHeadless TypeScript SDK for the LayerZero VT (Value Transfer) API that powers the [`@aori/mega-swap-widget`](https://www.npmjs.com/package/@aori/mega-swap-widget). Same quote, sign, submit, and status-tracking flow as the widget — without React, wagmi, or any UI.\n\nUse this when you want to bridge to/from USDM on MegaETH (or any other VT-supported pair) from a backend job, a CLI, a custom UI, or a non-React framework.\n\n## Install\n\n```bash\nnpm install @aori/usdm-bridge-sdk viem\n# or\nbun add @aori/usdm-bridge-sdk viem\n```\n\n`viem` is a required peer **whether or not you use the ethers adapter** — see [Using ethers v6](#using-ethers-v6-as-an-integrator-side-adapter). `ethers` is an optional peer; install it only if you want your application code to construct the wallet client through the ethers adapter:\n\n```bash\nnpm install @aori/usdm-bridge-sdk viem ethers\n```\n\n## Configure\n\n`UsdmBridgeConfig` mirrors the widget's `aori.config.ts` shape, minus the theme/appearance/wallet-modal fields. The example below binds the **output** side of every pair to USDM on MegaETH (chain `4326`), which is the canonical Aori \"USDM bridge\" setup.\n\n```ts\nimport type { UsdmBridgeConfig } from '@aori/usdm-bridge-sdk';\n\nexport const usdmBridgeConfig: UsdmBridgeConfig = {\n  vtApiBaseUrl: '/api/vt',\n  rpcOverrides: {\n    1: '/api/rpc/1',\n    10: '/api/rpc/10',\n    56: '/api/rpc/56',\n    143: '/api/rpc/143',\n    4326: '/api/rpc/4326',\n    8453: '/api/rpc/8453',\n    42161: '/api/rpc/42161',\n  },\n  tokens: {\n    defaultBase: { chainId: 1, address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' }, // USDC Ethereum\n    defaultQuote: { chainId: 4326, address: '0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7' }, // USDM MegaETH\n    supportedOutputTokens: [\n      { chainId: 4326, address: '0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7' },\n    ],\n    supportedOutputChains: [4326],\n  },\n  walletScreening: {\n    enabled: true,\n    useChainalysisOracle: true,\n    screeningUrl: '/api/screening',\n  },\n  settings: {\n    defaultSlippage: 0.01,\n    pollingIntervalMs: 4000,\n    statusTimeoutMs: 300_000,\n  },\n};\n```\n\n`getQuote` rejects any pair whose input or output isn't in the configured `supported*` lists with `UnsupportedPairError` — that's how you keep one side of every quote bound to USDM.\n\n## One-shot bridge (recommended)\n\n`sdk.bridge(...)` runs the entire flow — `executeSwap`, the deposit-chain settle delay, and `pollStatus` — and resolves only after the order reaches a terminal state. Use the `onSuccess` / `onFailure` / `onSettled` hooks to trigger code in your application when the swap completes. All three are awaited before the returned promise resolves, so you can `await` side effects (analytics, crediting an account, sending a push notification, …) inline:\n\n```ts\nimport { UsdmBridgeSdk } from '@aori/usdm-bridge-sdk';\n\nconst sdk = new UsdmBridgeSdk(usdmBridgeConfig);\n\nconst quote = await sdk.getQuote({ /* … */ });\n\nconst result = await sdk.bridge({\n  quote,\n  walletClient,\n  onStep:         (step)   => console.log('step:', step.kind),\n  onStatusChange: (status) => console.log('status:', status.status),\n\n  onSuccess: async (r) => {\n    await fetch('/api/credit-user', {\n      method: 'POST',\n      body: JSON.stringify({ userId, dstTxHash: r.dstTxHash, amount: r.quote.dstAmount }),\n    });\n  },\n  onFailure: (r) => sentry.captureMessage('bridge failed', { extra: r }),\n  onSettled: (r) => analytics.track('bridge_settled', { outcome: r.outcome, quoteId: r.quoteId }),\n});\n\nif (result.outcome === 'success') {\n  console.log('Settled:', result.dstTxHash, result.explorerUrl);\n} else {\n  console.warn('Did not settle:', result.status.status);\n}\n```\n\n`bridge()` resolves regardless of outcome — `result.outcome` is `'success'` for `SUCCEEDED`/`COMPLETED` and `'failure'` for `FAILED`/`CANCELLED`. The promise only **rejects** for actual errors: network failures, user-rejected signing, abort, or anything thrown from your hooks. If a hook throws, `bridge()` propagates the error so you can fail-fast on, say, a downstream API rejecting the credit.\n\nCancel a bridge in flight with an `AbortSignal`:\n\n```ts\nconst ac = new AbortController();\nconst promise = sdk.bridge({ quote, walletClient, abortSignal: ac.signal, onSuccess });\n// later…\nac.abort();\n```\n\nIf you'd rather drive `executeSwap` and `pollStatus` separately (e.g. to render distinct \"submitting\" vs \"settling\" UI states), the lower-level flow below still works.\n\n## Quote → Swap → Status (low-level)\n\n```ts\nimport { UsdmBridgeSdk } from '@aori/usdm-bridge-sdk';\nimport { createWalletClient, custom } from 'viem';\nimport { mainnet } from 'viem/chains';\nimport { usdmBridgeConfig } from './usdm-bridge.config';\n\nconst sdk = new UsdmBridgeSdk(usdmBridgeConfig);\n\nconst walletClient = createWalletClient({\n  account: '0xYourAddress',\n  chain: mainnet,\n  transport: custom(window.ethereum!),\n});\n\n// 1. Fetch a quote\nconst quote = await sdk.getQuote({\n  srcChainId: 1,\n  dstChainId: 4326,\n  srcTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC Ethereum\n  dstTokenAddress: '0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7', // USDM MegaETH\n  amount: '100',           // decimal human amount (100 USDC)\n  srcTokenDecimals: 6,     // required for string/number `amount`\n  // or: amount: 100_000_000n   // bigint = raw on-chain units (no decimals needed)\n  srcWalletAddress: '0xYourAddress',\n});\n\nconsole.log(`Quote ${quote.id}: ${quote.srcAmount} -> ${quote.dstAmount}`);\n\n// 2. Execute (chain-switch + approval + deposit + sign + submit)\nconst result = await sdk.executeSwap({\n  quote,\n  walletClient,\n  onStep: (step) => console.log('step:', step),\n  onTxHash: (hash, kind) => console.log(`${kind} tx: ${hash}`),\n});\n\nconsole.log(`Submitted ${result.quoteId}, tx hashes:`, result.txHashes);\n\n// 3. Track status to terminal state\nconst finalStatus = await sdk.pollStatus(result.quoteId, {\n  txHash: result.txHashes[result.txHashes.length - 1],\n  onStatusChange: (s) => console.log('status:', s.status),\n});\n\nconsole.log('done:', finalStatus.status, finalStatus.dstTxHash);\n```\n\n### What `executeSwap` does\n\nFor each step in `quote.userSteps`:\n\n| Step type   | Action                                                                                                                                                                                    |\n| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| TRANSACTION | Chain-switches the wallet, decodes calldata. If `approve(...)` it does the allowance/USDT-style reset/`maxUint256` dance and waits for receipt. Otherwise it sends as the native deposit. |\n| SIGNATURE   | Chain-switches if needed, signs EIP-712 typed data with the wallet, then `POST /submit-signature` to the relayer.                                                                         |\n\nAfter all steps complete you get back `{ quoteId, signature?, txHashes, isNativeDeposit, depositChainBlockTimeMs }`. If `isNativeDeposit`, wait `2 * depositChainBlockTimeMs` before the first `pollStatus` call (the widget does the same).\n\n## Using ethers v6 as an integrator-side adapter\n\nIf your codebase is already on ethers and you don't want to import viem in your wallet-construction code, the SDK ships a small adapter at `@aori/usdm-bridge-sdk/ethers`:\n\n```ts\nimport { UsdmBridgeSdk } from '@aori/usdm-bridge-sdk';\nimport { ethersSignerToWalletClient } from '@aori/usdm-bridge-sdk/ethers';\nimport { BrowserProvider } from 'ethers';\n\nconst sdk = new UsdmBridgeSdk(usdmBridgeConfig);\n\nconst provider = new BrowserProvider(window.ethereum!, 'any');\nconst signer = await provider.getSigner();\nconst walletClient = await ethersSignerToWalletClient(signer);\n\nconst quote = await sdk.getQuote(/* ... */);\nawait sdk.executeSwap({ quote, walletClient });\n```\n\nA working end-to-end React example is in [`examples/react-6963-ethers/`](./examples/react-6963-ethers/) — same UI as `react-6963`, but with zero `viem` imports in user code.\n\n### What the adapter actually does (and doesn't)\n\nThe adapter is a **compatibility shim**, not a viem replacement. Specifically:\n\n- It wraps your ethers `Signer` in a tiny EIP-1193 transport that translates `eth_sendTransaction`, `eth_signTypedData_v4`, `eth_chainId`, `eth_accounts`, and `wallet_switchEthereumChain` into the corresponding ethers calls. Anything else falls through to `signer.provider.send`.\n- It then calls viem's `createWalletClient({ transport: custom(shim) })` and returns the resulting `WalletClient` (typed as `SwapWalletClient`) for the SDK to consume.\n\nThis means:\n\n|                                                          | Reality                                                                                                                                                                                                                                                                                                            |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| Can my application code be 100% ethers, no viem imports? | ✅ Yes. That's the whole point of the adapter.                                                                                                                                                                                                                                                                      |\n| Does using the adapter remove viem from my bundle?       | ❌ No. The SDK's swap pipeline (`executeSwap`, allowance reads, receipt waits, EIP-712 typed-data canonicalization, ABI codec, the Chainalysis sanctions read) is all viem-based internally, and the adapter itself constructs a viem `WalletClient`. Bundle size is roughly the same as the viem-flavored example. |\n| Is `ethers` a hard runtime dependency?                   | ❌ Optional peer. Don't install it (and don't import from `/ethers`) and the SDK works fine.                                                                                                                                                                                                                        |\n| Is `viem` a hard runtime dependency?                     | ✅ Always. It's the SDK's runtime regardless of which adapter you use.                                                                                                                                                                                                                                              |\n| What changes between viem and ethers integration?        | Only the wallet-construction code in your application. The `sdk.bridge({ quote, walletClient, ... })` call site is byte-for-byte identical.                                                                                                                                                                        |\n\nIf you want a truly lib-agnostic core (genuinely no viem in the bundle when using ethers, hard peer-dep guarantees per adapter, separate npm packages), that's a larger architectural change — open an issue to discuss.\n\n## Status tracking only\n\nIf you've already submitted via your own pipeline and just want polling:\n\n```ts\nimport { UsdmBridgeSdk } from '@aori/usdm-bridge-sdk';\n\nconst sdk = new UsdmBridgeSdk({ vtApiBaseUrl: '/api/vt' });\n\nconst status = await sdk.pollStatus(quoteId, {\n  txHash,\n  interval: 4000,\n  timeout: 300_000,\n  onStatusChange: (s) => console.log(s.status),\n  onSuccess: (s) => console.log('settled:', s.dstTxHash),\n  onFailure: (s) => console.warn('did not settle:', s.status),\n  onSettled: (s) => console.log('terminal:', s.status),\n  // onComplete: (s) => …  ← legacy alias of onSettled, fires for any terminal state\n});\n```\n\nTerminal statuses: `SUCCEEDED`, `COMPLETED` (success), `FAILED`, `CANCELLED` (failure). The semantic hooks (`onSuccess` / `onFailure` / `onSettled`) are awaited before `pollStatus` resolves; throws inside them reject the promise. Helpers `isSuccessStatus`, `isFailureStatus`, and `isTerminalStatus` are exported if you want to classify a status string yourself.\n\n## Server-side proxying\n\nIn production, keep your VT API key and any private RPC URLs off the client.\n\n### API proxy (`vtApiBaseUrl`)\n\nPoint `vtApiBaseUrl` at your own backend route. The SDK sends every quote/submit/status request there instead of directly to LayerZero.\n\n```ts\nnew UsdmBridgeSdk({ vtApiBaseUrl: '/api/vt' /* no apiKey needed */ });\n```\n\nYour backend forwards to `https://transfer.layerzero-api.com/v1` with the real `x-api-key` injected from env vars.\n\n### RPC proxy (`rpcOverrides`)\n\nThe SDK uses public RPCs by default for ERC20 allowance reads, receipt waits, and the Chainalysis sanctions oracle. Override per-chain:\n\n```ts\nnew UsdmBridgeSdk({\n  rpcOverrides: {\n    1: '/api/rpc/1',\n    4326: '/api/rpc/4326',\n  },\n});\n```\n\nThis is independent of whatever your wallet provider uses for signing/sending — the SDK only uses these for read-side calls.\n\n## Low-level primitives\n\nWhen you want full control over the orchestration (e.g. interleaving custom UI between approval and deposit), import the standalone helpers and skip `executeSwap`:\n\n```ts\nimport {\n  requestQuote,\n  ChainSwitch,\n  handleApprovalStep,\n  sendTransactionStep,\n  signAndSubmit,\n  pollOrderStatus,\n  SdkEnvironment,\n} from '@aori/usdm-bridge-sdk';\n\nconst env = new SdkEnvironment({ vtApiBaseUrl: '/api/vt' });\nconst quote = await requestQuote({ /* ... */ }, { env });\n\nfor (const step of quote.userSteps) {\n  if (step.type === 'TRANSACTION') {\n    await ChainSwitch(walletClient, /* chainId */);\n    const wasApproval = await handleApprovalStep({ step, walletClient, ownerAddress, quote, env });\n    if (!wasApproval) {\n      await sendTransactionStep({ step, walletClient, userAddress: ownerAddress, fallbackChainKey: quote.srcChainKey });\n    }\n  } else {\n    await signAndSubmit({ quote, signatureStep: step, userAddress: ownerAddress, walletClient, env });\n  }\n}\n\nawait pollOrderStatus(quote.id, env, { onStatusChange: (s) => console.log(s.status) });\n```\n\n## Supported chains\n\nBuilt-in chain registry (extend via `rpcOverrides`):\n\n| Chain ID | Key       |\n| -------- | --------- |\n| 1        | ethereum  |\n| 10       | optimism  |\n| 30       | rootstock |\n| 56       | bsc       |\n| 143      | monad     |\n| 988      | stable    |\n| 4326     | megaeth   |\n| 8453     | base      |\n| 9745     | plasma    |\n| 42161    | arbitrum  |\n\n## License\n\nUNLICENSED — same terms as the rest of the Aori widget stack.\n","readmeFilename":"README.md","_rev":"1-307e93fcfae2778e79cf78910dc45ddb"}