SDKs & tools
React hooks
@layerhop/react — LayerHopProvider plus useQuote, useSwap, useOrderStatus, useTokens and friends, on top of the SDK and TanStack Query.
@layerhop/react wraps the TypeScript SDK in React hooks built on TanStack Query v5. It is wallet-agnostic: useSwap asks your sign(tx) callback to sign, so it works with wagmi/viem, ethers or the Solana wallet adapter.
Source: github.com/layerhop/layerhop-js (
packages/react) — source repository (opens at launch).
The hooks never hold keys. They fetch quotes and build unsigned transactions; your wallet signs.
Install#
npm install @layerhop/react @layerhop/sdk @tanstack/react-queryPeer dependencies: react >= 18, @tanstack/react-query >= 5. Inside the monorepo use "@layerhop/react": "workspace:*".
LayerHopProvider#
import { LayerHopProvider } from "@layerhop/react";
<LayerHopProvider options={{ integrator: "myapp" }}>
<App />
</LayerHopProvider>| Prop | Description |
|---|---|
client |
A new LayerHop({...}) instance (or any object with the same methods). Wins over options. |
options |
LayerHopOptions for a client the provider creates. |
queryClient |
Your TanStack QueryClient. If you leave it out, an existing <QueryClientProvider> above is reused, or the provider creates one. |
Quickstart: quote → build → sign → report → track#
Amounts are integer strings in base units (100 USDC = "100000000").
import { useQuote, useSwap } from "@layerhop/react";
import { useAccount, useSendTransaction } from "wagmi";
function Swap() {
const { address } = useAccount();
const { sendTransactionAsync } = useSendTransaction();
const quote = useQuote({
srcChain: "base", srcToken: "USDC", amount: "100000000",
dstChain: "solana", dstToken: "SOL",
sender: address, recipient: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
});
const swap = useSwap({
sign: async (tx) => {
if (tx.vm !== "evm") throw new Error("connect a Solana wallet for this route");
// send tx.approval first when present (see Approvals)
return sendTransactionAsync({ to: tx.to as `0x${string}`, data: tx.data as `0x${string}`, value: BigInt(tx.value) });
},
});
return (
<button disabled={!quote.data || swap.isBusy} onClick={() => swap.start({ quote: quote.data!.best })}>
{swap.phase === "idle" ? `Get ${quote.data?.best.dstAmount ?? "…"} lamports` : swap.phase}
</button>
);
}useSwap#
useSwap({ sign, pollIntervalMs?, trackTimeoutMs?, onPhase? }) runs one swap as a state machine:
idle → quoting → building → awaiting-signature → submitted → tracking → done | error
start(params)gets quotes and takes the best one.start({ quote })skips quoting.- It builds the transaction, then calls
sign(tx, { quote }). Your callback returns the broadcast tx hash, either as astringor as{ txHash }. For Solana txs withsubmitViaApi, return{ signedTransaction }(base64) and the hook submits it for you. - When the SDK has
reportSubmitted, the hook reports the hash (POST /v1/orders/{quoteId}/submitted). This is best effort: a failed report never fails the swap. - It polls the order status until
filled→done.refundedorfailed→errorwith aSwapError(codeORDER_REFUNDED/ORDER_FAILED). Same-chain providers always reportunknown, which counts as done.
The result is { phase, quote, tx, txHash, status, error, start, reset, isBusy }. reset() goes back to idle and abandons a run in flight, for example while the wallet prompt is open.
Other hooks#
| Hook | What it does |
|---|---|
useQuote(params, { enabled, refetchInterval, refreshMarginSeconds }) |
Quotes from every provider. Idle until params are complete and amount > 0. refetchInterval: "auto" (default) refetches 5 s before the best quote's expiresAt. 4xx errors are not retried. |
useBuildTx() |
Mutation: mutate(quoteId) → the BuiltTx. |
useOrderStatus(provider, id, { intervalMs, enabled }) |
Polls every 3 s until filled / refunded / failed, then stops. Adds isFinal. |
useChains() |
Supported chains (cached for the session). |
useTokens(chain) |
Built-in tokens for one chain; idle while chain is empty. |
useLayerHop() |
The client from the provider. |
Query keys are exported as layerhopKeys for invalidation. API failures are LayerHopApiErrors with a stable error code.