Skip to content

Send Transactions

Base Url: box-v2.api.decent.xyz/api
Endpoint: /getBoxAction

Transaction execution API's enable developers to use Decent server side or in clients without worrying about potential dependency conflicts. Server-side (Node.js) transactions are frequently useful for operational transactions, internal to your processes, or to abstract significant onchain complexity away from end users.

Query Parameters

ParameterDescriptionTypeRequired
chainIdTarget contract's chain ID.ChainIdtrue
actionTypeTransaction execution type.ActionTypetrue
actionConfigVaries by target action. Please see Box Common Types for more information.anytrue

Sample Request & Implementation

The request below is an example of how to use the EvmFunction action type to mint an NFT; however, API's can be configured to execute any onchain transaction. Please see example use cases for further information.

Step one: Import recommended libraries

@decent.xyz/box-common provides helper functions and types to ease development. The example below uses wagmi; however, you can use whatever your preferred web3 library is to send Decent transactions.

import {
  bigintSerializer,
  bigintDeserializer,
  ActionType,
} from '@decent.xyz/box-common';
import { sendTransaction } from '@wagmi/core';
import { wagmiConfig } from '../your_wagmi_config_path';
 
// wagmi a required prop in v2.1.0 and higher.
// Create a config for your project following these docs:
// https://wagmi.sh/react/api/createConfig

Step two: Configure the transaction request

Build the transaction configuration based on the required parameters for your selected ActionType. For assistance with this configuration, please visit the Playground page of Decent's Developer Console. The Playground will help parse ABI's for any verified smart contract and provide a no-code interface to configure transaction requests.

If your smart contract function takes ETH, you can specify isNative is true, and you do not need a tokenAddress.

import {
  bigintSerializer,
  bigintDeserializer,
  ActionType,
} from '@decent.xyz/box-common';
import { sendTransaction } from '@wagmi/core';
import { wagmiConfig } from '../your_wagmi_config_path';
 
const txConfig: BoxActionRequest = { 
  actionType: ActionType.EvmFunction, 
  sender: 'Connected Wallet', 
  srcToken: '0x0000000000000000000000000000000000000000', 
  dstToken: '0x0000000000000000000000000000000000000000', 
  srcChainId: "Connected Chain", 
  dstChainId: ChainId.OPTIMISM, 
  slippage: 1, // 1%, Note: cannot be 0
  actionConfig: { 
    chainId: ChainId.OPTIMISM, 
    contractAddress: '0xa9de16b1484C11b481B23dbdEC534a29F5668a22', 
    cost: { 
      amount: 100000000000000n, 
      isNative: true, 
      tokenAddress: '0x0000000000000000000000000000000000000000', 
    }, 
    signature: 'function mint(address to, uint256 numberOfTokens)', 
    args: ['0x5D7370fCD6e446bbC14A64c1EFfe5FBB1c893232', 1n], 
  }, 
}; 

Step three: Build the transaction

Decent's /getBoxAction endpoint functions similarly to the prepareTransactionRequest of other web3 libraries, like wagmi. Given your configuration from step two, this API endpoint will return the calldata required to actually execute this transaction.

import {
  bigintSerializer,
  bigintDeserializer,
  ActionType,
} from '@decent.xyz/box-common';
import { sendTransaction } from '@wagmi/core';
import { wagmiConfig } from '../your_wagmi_config_path';
 
const txConfig: BoxActionRequest = {
  actionType: ActionType.EvmFunction,
  sender: 'Connected Wallet',
  srcToken: '0x0000000000000000000000000000000000000000',
  dstToken: '0x0000000000000000000000000000000000000000',
  srcChainId: "Connected Chain",
  dstChainId: ChainId.OPTIMISM,
  slippage: 1, // 1%, Note: cannot be 0
  actionConfig: {
    chainId: ChainId.OPTIMISM,
    contractAddress: '0xa9de16b1484C11b481B23dbdEC534a29F5668a22',
    cost: {
      amount: 0n,
      isNative: true,
      tokenAddress: '0x0000000000000000000000000000000000000000',
    },
    signature: 'function mint(address to, uint256 numberOfTokens)',
    args: ['0x5D7370fCD6e446bbC14A64c1EFfe5FBB1c893232', 1n],
  },
};
 
async function prepareDecentTransaction({ txConfig }) { 
  const url = new URL('https://box-v2.api.decent.xyz/api/getBoxAction'); 
  url.searchParams.set('arguments', JSON.stringify(txConfig, bigintSerializer)); 
 
  const requestOptions = { 
    method: 'GET', 
    headers: { 'x-api-key': DECENT_API_KEY }, 
  };
 
  const response = await fetch(url.toString(), requestOptions); 
  const textResponse = await response.text(); 
  const { tx, tokenPayment } = JSON.parse(textResponse, bigintDeserializer); 
 
  return { 
    tx, 
    tokenPayment, 
  }; 
}; 

Step four: Send your transaction

Use your preferred web3 library to actually send the transaction returned from step three onchain for execution.

import {
  bigintSerializer,
  bigintDeserializer,
  ActionType,
  BoxActionRequest
} from '@decent.xyz/box-common';
import { sendTransaction } from '@wagmi/core';
import { wagmiConfig } from '../your_wagmi_config_path';
 
const txConfig: BoxActionRequest = {
  actionType: ActionType.EvmFunction,
  sender: 'Connected Wallet',
  srcToken: '0x0000000000000000000000000000000000000000',
  dstToken: '0x0000000000000000000000000000000000000000',
  srcChainId: "Connected Chain",
  dstChainId: ChainId.OPTIMISM,
  slippage: 1, // 1%, Note: cannot be 0
  actionConfig: {
    chainId: ChainId.OPTIMISM,
    contractAddress: '0xa9de16b1484C11b481B23dbdEC534a29F5668a22',
    cost: {
      amount: 0n,
      isNative: true,
      tokenAddress: '0x0000000000000000000000000000000000000000',
    },
    signature: 'function mint(address to, uint256 numberOfTokens)',
    args: ['0x5D7370fCD6e446bbC14A64c1EFfe5FBB1c893232', 1n],
  },
};
 
async function prepareDecentTransaction({ txConfig }) {
  const url = new URL('https://box-v2.api.decent.xyz/api/getBoxAction');
  url.searchParams.set('arguments', JSON.stringify(txConfig, bigintSerializer));
 
  const requestOptions = {
    method: 'GET',
    headers: { 'x-api-key': DECENT_API_KEY },
  };
 
  const response = await fetch(url.toString(), requestOptions);
  const textResponse = await response.text();
  const { tx, tokenPayment } = JSON.parse(textResponse, bigintDeserializer);
 
  return {
    tx,
    tokenPayment,
  };
}
 
export async function sendDecentTransaction() { 
  const { tx } = await prepareDecentTransaction(); 
 
  const gas = await publicClient?.estimateGas({ 
    account,
    ...tx,
  });
 
  const hash = await sendTransaction(wagmiConfig, { 
    ...tx, 
    gas, 
  }); 
  return hash; 
}

Step five (optional): Poll for your transaction's status

Same chain transactions - either direct or swap & execute - will confirm in one block, which is typically 1-3 seconds. Cross-chain transactions are subject to both the source and destination chains confirming the transaction. This typically takes between 20-40 seconds.

Decent's transactions are atomic, meaning any transaction that confirms on the source chain is guaranteed to execute on the destination chain. Therefore, we recommend updating UI states based on the source chain confirmation for the best user experience. You may still want to track the full transactions' status; the example below demonstrates how you might poll Decent's /getStatus endpoint.

const fetchTransactionStatus = async () => {
  const queryParams = new URLSearchParams({
    chainId: TX_SOURCE_CHAIN_ID,
    txHash: SOURCE_CHAIN_TX_HASH
  });
 
  const options = { method: 'GET', headers: { 'x-api-key': `${process.env.DECENT_API_KEY}` }};
  let waitTime = 20000;
  let attempts = 0;
 
  while (attempts < 20) {
    console.log(`Waiting for ${waitTime / 1000} seconds before next poll (Attempt ${attempts + 1}/20)...`);
    await new Promise(resolve => setTimeout(resolve, waitTime));
 
    try {
      const response = await fetch(`https://api.decentscan.xyz/getStatus?${queryParams}`, options);
      const data = await response.json();
 
      console.log('Transaction status:', data.status);
      
      if (data.status === 'Executed') {
        console.log('Transaction has been executed successfully.');
        break;
      } else if (data.status === 'Failed') {
        console.log('Transaction has failed.');
        break;
      }
    } catch (err) {
      console.error('Error fetching transaction status:', err);
    }
 
    waitTime = Math.max(1000, waitTime / 2);
    attempts++;
  }
 
  if (attempts >= 20) {
    console.log('Maximum number of retries reached without success.');
  }
};
 
fetchTransactionStatus();