Box Hooks

Use The Box's internal routes directly for maximal customizability.

If you would like to make use of the-box's routes & bridging, but would like to provide your own UI, you can make use of the box-hooks library!


Simply Install the '' UI package from your favorite package manager.

npm i // or pnpm or yarn

Please ensure you have also installed wagmi/core and, if you are using viem, that your wagmi, wagmi/core, and viem versions do not conflict.

Environment Variables

You'll need a Decent API key. Head over to this section to get one. Once you have it, store the API key in your .env file.

# .env file 

Using Box Hooks

TLDR, show me a working example!

If you'd like to skip to a working example and see how box-hooks are used, check out our box-examples repository! You can see the deployment of that repository here. Pretty much all the code for using box-hooks is located in this file.


Use this context provider at the top of the component tree, where you'd want to use The Box's hooks. Usually this is done in your _app.tsx.

export default function ExamplePage() {

  return (
        apiKey={process.env.NEXT_PUBLIC_DECENT_API_KEY as string}
          rest of your app

Note: for the env var you do not have to use the DECENT_API_KEY name. The context provider accepts any API key as an input so feel free to rename it to anything else you would like.

useBoxAction(args: UseBoxActionArgs)

This is the main hook that provides you with:

  1. Cost information
  2. Transaction data that you can submit from your dapp


The argument to useBoxAction has the following type:

type UseBoxActionArgs = {
  sender: string;

  srcToken: string;
  srcChainId: ChainId;
  dstToken: string;
  dstChainId: ChainId;
  slippage: number;

  actionType: ActionType;
  actionConfig: ActionConfig;
  enable?: boolean;

Here's a description of each field.

srcToken & dstTokenERC-20 token addresses. Use the zero address if it's a native token. Viem exports this
srcChainId & dstChainIdThe chain ID's of the source and the destination chains. If your transaction is not crosschain, set them to the same ID
slippageAmount of slippage you'd allow in your route. Please visit this section for more information on slippage.
actionType & actionConfigAny of our supported action types and their corresponding action configs
enable (optional)Set to false to disable the query for automatically running - similar to wagmi's enabled option

An example of this, this is the same example used in our examples repository.

import { ChainId } from '';

const actionArgs: UseBoxActionArgs = {
  actionType: ActionType.NftMint,
  actionConfig: {
    contractAddress: '0x3007E0eB44222AC69E1D3c93A9e50F9CA73F53a1',
    chainId: ChainId.ARBITRUM,
    cost: {
      isNative: true,
      amount: parseUnits('0.00005', 18),
  srcChainId: ChainId.POLYGON,
  sender: sender!,
  slippage: 1, // 1%
  srcToken: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', // USDC on Polygon
  dstToken: '0x0000000000000000000000000000000000000000', // ETH
  dstChainId: ChainId.ARBITRUM,

Then you can pass these args into the hook to get the action response:

  const { actionResponse, isLoading, error } = useBoxAction(actionArgs);

isLoading & error can bee used in your UI to handle loading & error cases.


actionResponse has the following type

export type BoxActionResponse = {
  tx: Transaction;
  tokenPayment?: Payment;
  applicationFee?: Payment;
  bridgeFee?: Payment;
  bridgeId?: BridgeId;

txA Transaction object you can use to submit with your dapp
tokenPaymentPayment for the action taken. E.g., if the box is used to mint an NFT with the price 0.1 Matic on Polygon, and the srcChainId is Arbitrum and srcToken is USDC's address, this will contain the amount of USDC the user will have to pay
applicationFeeFee that Decent collects
bridgeFeeFee that the bridge provider collects
bridgeIdID of the bridge used for this transaction. Eligible ID's are:
0: Stargate
1: Axelar
2: Wormhole
See Common Types

You can then submit the transaction using your favorite Ethereum library as follows:

const tx = actionResponse.tx;
const gas = await publicClient.estimateGas({
} as unknown as EstimateGasParameters);
const response = await sendTransaction({

We recommend viem because it is awesome. You can see all of this in action here.


Given a wallet address & a ChainId, this hook will return all the assets for that address. Check out here for a working example, and here for a code sample using it.

import {
} from '';
import { useAccount } from 'wagmi';

const TokenSelectorUsage = () => {
  const { address } = useAccount();
  const chainId = ChainId.ARBITRUM;
  const [srcToken, setSrcToken] = useState<TokenInfo>(ethGasToken);

  const { tokens } = useUsersBalances({
    address: address!,
    enable: Boolean(address),
// etc. 

Inputs to useUsersBalances:

address: user's address.

chainId: chain id, available types are here.

enable (optional): set to false to disable the query for automatically running - similar to wagmi's enabled option.

Return Type

An array of UserTokenInfo's.

Here's an example:

    "name": "Ether",
    "symbol": "ETH",
    "decimals": 18,
    "balanceFloat": 0.3438013580730053,
    "balance": "343801358073005350n",
    "address": "0x0000000000000000000000000000000000000000",
    "isNative": true,
    "logo": "",
    "chainId": 42161
    "address": "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
    "decimals": 6,
    "name": "USD Coin (Arb1)",
    "symbol": "USDC",
    "totalSupply": {
      "formatted": "0.000678580390713551",
      "value": "678580390713551n"
    "isNative": false,
    "chainId": 42161,
    "logo": "",
    "balanceFloat": 2881.777809,
    "balance": "2881777809n"

    "address": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9",
    "decimals": 6,
    "name": "Tether USD",
    "symbol": "USDT",
    "totalSupply": {
      "formatted": "0.000666157066365669",
      "value": "666157066365669n"
    "isNative": false,
    "chainId": 42161,
    "logo": "",
    "balanceFloat": 5.004789,
    "balance": "5004789n"

What’s Next