Mempool SDK

Access Blocknative notification messages in Javascript with our simple web socket library

Open source GitHub repo: https://github.com/blocknative/sdk

The SDK uses our API, which has rate limits. Please see Rate Limits for more details.

As Blocknative community members create SDKs in additional languages, we list them here. We greatly appreciate community member contributions. Golang: https://github.com/bonedaddy/go-blocknative

These SDKs are not supported directly by Blocknative.

Quickstart

It should take less than 5 minutes to get going with the SDK.

Create a Blocknative Account

Go to the Account page at https://explorer.blocknative.com/account and setup an account with an email address. You will receive an email to confirm your account.

You can read the Getting Started guide on the API Docs to get more information on how to set up an API Key for your purpose. API keys segment analytics data. Consider using different API keys for development, staging, and production releases.

Install the library using npm

npm install bnc-sdk

Initialize the Library

// For Node < v13 / CommonJS environments
const BlocknativeSdk = require('bnc-sdk');
const WebSocket = require('ws');
const Web3 = require('web3');

// OR

// For Node >= v13 / es module environments
import BlocknativeSdk from 'bnc-sdk'
import Web3 from 'web3'
import WebSocket from 'ws' // only neccessary in server environments

// create options object
const options = {
  dappId: 'Your dappId here',
  networkId: 1,
  system: 'bitcoin', // optional, defaults to ethereum
  transactionHandlers: [event => console.log(event.transaction)],
  ws: WebSocket, // only neccessary in server environments 
  name: 'Instance name here', // optional, use when running multiple instances
  onerror: (error) => {console.log(error)} //optional, use to catch errors
}

// initialize and connect to the api
const blocknative = new BlocknativeSdk(options)

Track transactions

Send a transaction using web3.js and get the transaction hash while its processing. Let the SDK know the hash and it will track its progress through the mempool and into a block.

// initialize web3
const web3 = new Web3(window.ethereum)

// get current account
const accounts = await web3.eth.getAccounts();
const address = accounts[0];

// create transaction options object
const txOptions = {
  from: address,
  to: "0x792ec62e6840bFcCEa00c669521F678CE1236705",
  value: "1000000"
}

// initiate a transaction via web3.js
web3.eth.sendTransaction(txOptions).on('transactionHash', hash => {
  // call with the transaction hash of the
  // transaction that you would like to receive status updates for
  const { emitter } = blocknative.transaction(hash)

  // listen to some events
  emitter.on('txPool', transaction => {
    console.log(`Sending ${transaction.value} wei to ${transaction.to}`)
  })

  emitter.on('txConfirmed', transaction => {
    console.log('Transaction is confirmed!')
  })

  // catch every other event that occurs and log it
  emitter.on('all', transaction => {
    console.log(`Transaction event: ${transaction.eventCode}`)
  })

And you are live!

Screencasts

See how to get started with the SDK in this screencast:

SDK Quickstart Screencast

Initialization Options

The following options object needs to be passed when initializing and connecting

type InitializationOptions = {
  dappId?: string
  networkId: number
  system?: System
  name?: string
  appVersion?: string
  transactionHandlers?: TransactionHandler[]
  ws?: any
  onopen?: () => void
  ondown?: (closeEvent: CloseEvent) => void
  onreopen?: () => void
  onerror?: (error: SDKError) => void
  onclose?: () => void
  apiUrl?: string
}

dappId - [OPTIONAL]

Your unique API key that identifies your application. You can generate a dappId by visiting the Blocknative account page and creating a free account. It is generally required, but is optional in the case of using the apiUrl parameter to pass the API key in as a query parameter.

system - [OPTIONAL]

The system that you would like to monitor transactions on. Currentlyethereum is supported. Defaults to ethereum if no value is passed in.

networkId - [REQUIRED]

Valid Ethereum and EVM compatible network ids (use ethereum for system):

  • 1 Main Network

  • 11155111 Sepolia Test Network

  • 100 xDai POA Network

  • 137 Polygon Matic Mainnet Network

  • 80001 Polygon Matic Mumbai Test Network

transactionHandlers - [OPTIONAL]

An array of functions that will each be called once for every status update for every transaction that is associated with this connection on a watched address or a watched transaction.

const handleTransactionEvent = event => {
  const {
    transaction, // transaction object
    emitterResult // data that is returned from the transaction event listener defined on the emitter
  } = event
  
  console.log(transaction)
}

const options = {
  // other options
  transactionHandlers: [handleTransactionEvent]
}

See the Transaction Object section for more info on what is included in the transaction parameter.

ws - [OPTIONAL]

If you are running the sdk in a server environment, there won't be a native WebSocket instance available for the sdk to use, so you will need to pass one in. You can use any WebSocket library that you prefer as long as it correctly implements the WebSocket specifications. We recommend ws.

name - [OPTIONAL]

If you are running multiple instances of the sdk on the same client, passing in a name property allows the sdk to properly manage persistent state.

onopen - [OPTIONAL]

A function that is called once the WebSocket has successfully connected to the Blocknative backend infrastructure.

onerror - [OPTIONAL]

A function that is called for every error that happens within the SDK including WebSocket connection errors. The function is called with an error object with the following parameters:

message : String - The error message, describing what went wrong

error : ErrorObject - An error object if ine exist (for instance a WebSocket error)

transaction : String - The hash or txid passed to the call to transaction that caused the error

account : String - The address passed to the call to account that caused the error

If this function is not passed in then error will be thrown.

ondown - [OPTIONAL]

A function that is called when the WebSocket connection has dropped. The SDK will automatically reconnect.

onreopen - [OPTIONAL]

A function that is called once the WebSocket has successfully re-connected after dropping.

onclose - [OPTIONAL]

A function that is called when the WebSocket has successfully been destroyed.

apiUrl - [OPTIONAL]

An optional (required if no dappId provided) parameter that allows for the SDK to create a WebSocket connection to a url other than the default Blocknative WebSocket server. This can be useful in the case that you would like to use a WebSocket proxy server rather than including your API key client side.

Initialize and Connect

Import and initialize the SDK with the configuration options described above for client and server environments.

Client/Browser Environment

import BlocknativeSdk from 'bnc-sdk'

// create options object
const options = {
  dappId: 'Your dappId here',
  networkId: 1,
  transactionHandlers: [event => console.log(event.transaction)],
  onerror: (error) => {console.log(error)}
}

// initialize and connect to the api
const blocknative = new BlocknativeSdk(options)

Server/Node.js Environment

// For Node < v13 / CommonJS environments
const BlocknativeSdk = require('bnc-sdk');
const WebSocket = require('ws');

// OR

// For Node >= v13 / es module environments
import BlocknativeSdk from 'bnc-sdk'
import WebSocket from 'ws' // only neccessary in server environments

// create options object
const options = {
  dappId: 'Your dappId here',
  networkId: 1,
  transactionHandlers: [event => console.log(event.transaction)],
  ws: WebSocket,
  onerror: (error) => {console.log(error)}
}

// initialize and connect to the api
const blocknative = new BlocknativeSdk(options)

Events

Watch a Transaction

Now that your application is successfully connected via a WebSocket connection to the Blocknative back-end, you can register transactions to watch for updates (notifications). \

Ethereum

Once you have initiated a transaction and have received the transaction hash, you can pass it in to the transaction function:

// initiate a transaction via web3.js
web3.eth.sendTransaction(txOptions).on('transactionHash', hash => {
  // call with the transaction hash of the transaction that you would like to receive status updates for
  const {
    emitter, // emitter object to listen for status updates
    details // initial transaction details which are useful for internal tracking: hash, timestamp, eventCode
  } = blocknative.transaction(hash)
})

The emitter is used to listen for status updates. See the Emitter Section for details on how to use the emitter object to handle specific transaction state changes.

The details object contains the initial transaction details which are useful for internal tracking.

If the library was initialized with transaction handlers, those handlers will also be called on each status change for the watched transaction.

If a transaction is watched that is currently in the txpool or was updated in the last 60 minutes, the SDK will immediately send a notification with the last detected status for that transaction.

If a watched transaction is replaced (status speedup or cancel), the SDK will automatically watch the hash of the replacement transaction for the client and start delivering notifications for it.

Watch an Account Address

You can also register an account address to listen to any incoming and outgoing transactions that occur on that address using the account method:

Ethereum

// get the current accounts list of the user via web3.js
const accounts = await web3.eth.getAccounts()

// grab the primary account
const address = accounts[0]

// call with the address of the account that you would like to receive status updates for
const {
  emitter, // emitter object to listen for status updates
  details // initial account details which are useful for internal tracking: address
} = blocknative.account(address)

This will tell the Blocknative back-end to watch for any transactions that occur involving this address and any updates to the transaction status over time. The return object from successful calls to account will include an event emitter that you can use to listen for those events and a details object which includes the address that is being watched.

Un-watch an Account Address or Transaction Hash

If you no longer want to receive notifications for an account address or transaction hash, you can use the unsubscribe method:

// unsubscribe from address
blocknative.unsubscribe(address)

// unsubscribe from Ethereum transaction hash
blocknative.unsubscribe(hash)

Log an Event

You may want to log an event that isn't associated with a transaction for analytics purposes. Events are collated and displayed in the developer portal and are segmented by your dappId. To log an event, simple call event with a categoryCode and an eventCode, both of which can be any String that you like:

blocknative.event({
  categoryCode: String, // [REQUIRED] - The general category of the event
  eventCode: String // [REQUIRED] - The specific event
})

Emitter

The emitter object is returned from calls to account and transaction and is used to listen to status updates via callbacks registered for specific event codes.

// register a callback for a txPool event
emitter.on("txPool", transaction => {
  console.log("Transaction is pending")
})

The first parameter is the eventCode string of the event that you would like to register a callback for. For a list of the valid event codes, see the event codes section.

The second parameter is the callback that you would like to register to handle that event and will be called with a transaction object that includes all of the relevant details for that transaction. See the Transaction Object section for more info on what is included.

Any data that is returned from the listener callback for transaction emitters will be included in the object that the global transactionHandlers are called with under the emitterResult property.

To prevent memory leaks on long running processes, you can use the off method on the emitter to remove your callback listener:

// remove callback for txPool event
emitter.off('txPool')

Transaction Object

The callback that is registered for events on an emitter or included in the transactionHandlers array will be called with the following transaction object:

Ethereum

{
  status: String, // current status of the transaction
  hash: String,
  to: String,
  from: String,
  gas: Number,
  gasPrice: String,
  gasPriceGwei: Number,
  gasUsed: Number, // present on on-chain txns
  nonce: Number,
  value: String,
  eventCode: String,
  blockHash: String,
  blockNumber: Number,
  input: String,
  baseFeePerGasGwei: number // option value 
  maxPriorityFeePerGasGwei: number// option value if transaction is of Type2
  maxFeePerGasGwei: number // option value if transaction is of Type2
  gasPriceGwei: number// option value
  timeStamp: string // the UTC time of first detection of current status
  dispatchTimeStamp: string // the UTC time of time of event dispatch
  pendingTimeStamp: string // the UTC time of initial pending status detection
  pendingBlockNumber: Number // the chain head block number at time of pending detection
  transactionIndex: Number, // optional, present if status confirmed, failed
  blockTimeStamp: String, // optional, present if status confirmed, failed - UTC time of miner block creation
  counterParty: String, // address of the counterparty of the transaction when watching an account
  direction: String, // the direction of the transaction in relation to the account that is being watched ("incoming" or "outgoing")
  watchedAddress: String, // the address of the account being watched
  timePending: String, // optional, present if status confirmed, failed, speedup, cancel. "-1" if first detection is on-chain.
  blocksPending: Number, // optional, present if status confirmed, failed, speedup, cancel
  originalHash: String, // if a speedup or cancel status, this will be the hash of the original transaction
  asset: String, // the asset that was transfered
  v: String,
  r: String,
  s: String,
  contractCall: { // if transaction was a contract call otherwise undefined
    contractAddress: String,
    contractType: String,
    methodName: String,
    params: {
      // params that the contract method was called with
    },
    contractName: String,
    contractDecimals: Number (optional),
    decimalValue: String (optional),
  }
}

Internal Transactions (Ethereum)

The SDK will send confirmednotifications when a watchedAddress is detected in the internal transactions of a contract call. In this case, the confirmed transaction object will include details of the internal transactions and balance changes resulting from those internal transactions. Fields are not ordered.

"internalTransactions": [],
"netBalanceChanges": Object
FieldDescription

internalTransactions

Array of objects containing details of each internal transaction (see below)

netBalanceChanges

Object containing details of balance changes for all addresses involved in internal transactions (see below)

The internalTransactions array contains details on each internal transaction executed by the contract call of the parent (main) transaction. Fields are not ordered.

"internalTransactions": [
  {      
    "type": String,
    "from": String,
    "to": String,
    "input": String,
    "gas": Number,
    "gasUsed": Number,
    "value": String,
    "contractCall": Object (optional, contains an additional param 'contractAlias' which will be the symbol of the token if this is an erc20 transfer or transferFrom)
  },
  ...
]
FieldDescription

type

Type of internal transaction (one of CALL, DELEGATECALL, STATICCALL, CALLCODE)

from

Address initiating the internal transaction call (typically the parent (main) transaction's contract address

to

Address the internal transaction is calling or sending value to

input

Data sent to internal transaction. For value transfers from external account initiating parent (main) transaction to another external account, this field contains 0x. For contract calls, this value contains the contract method signature and params as a hex string.

gas

Maximum amount of gas available to the internal transaction

gasUsed

Amount of gas actually used executing the internal transaction

value

Amount of ETH transferred directly to to address from parent (main) transaction from address.

contractCall

Optional. A series of keys and values specific to the contract method . This object is present only if the contract method call includes parameters and Blocknative decodes the internal transaction contract call (e.g. an ERC20 transfer). For details see decoded contract payload above. NOTE: If the Internal transaction is an ERC20 transfer or transferFrom call, the contractCall object will include an additional field, contractAlias with the symbol of the token transferred.

The netBalanceChanges object contains details of all the balance changes resulting from the internal transactions details in the internalTransactions array.

"netBalanceChanges": {
    "<address>": [
      {
        "delta": String,
        "asset": {
          "type": String,
          "symbol": String
        },
        "breakdown": [
          {
            "counterparty": Atring,
            "amount": String
          }
        ]
      }
    ],
    ...
  }
FieldDescription

<address>

Address involved in internal transaction. Each address contains an array of balances changes, one for each counterparty

asset

Details of the asset being transferred. Contains type and symbol.

delta

Amount of value transfer (balance change) in wei to the <address>. Outgoing value is represented as a negative balance change and incoming value is represented as a positive balance change

type

The type of asset transferred (e.g. "ether")

symbol

The symbol of the asset transferred. "ETH" or appropriate ERC20 symbol

breakdown

Array of individual transfers to <address> for the current asset

counterparty

Address of the other side of the transfer relative to the <address>

amount

The amount of asset transferred with this counterparty

Event Codes

The following is a list of event codes that are valid, and the events that they represent:

  • all: Will be called for all events that are associated with that emitter. If a more specific listener exists for that event, then that will be called instead. This is useful to catch any remaining events that you haven't specified a handler for

  • txSent: Transaction has been sent to the network

  • txPool: Transaction was detected in the "pending" area of the mempool and is eligible for inclusion in a block

  • txPoolSimulation: Transaction was detected in the "pending" area of the mempool and is eligible for inclusion in a block and has been simulated against the latest block

  • txStuck: Transaction was detected in the "queued" area of the mempool and is not eligible for inclusion in a block

  • txConfirmed: Transaction has been confirmed

  • txFailed: Transaction has failed

  • txSpeedUp: A new transaction has been submitted with the same nonce and a higher gas price, replacing the original transaction

  • txCancel: A new transaction has been submitted with the same nonce, a higher gas price, a value of zero and sent to an external address (not a contract)

  • txDropped: Transaction was dropped from the mempool without being added to a block

Filtering and Decoding Ethereum Transactions

You may want to filter events that occur on an Ethereum address and/or have the Blocknative server automatically decode the input data for a particular contract. To do this the configuration function can be used to send a configuration that will be scoped globally or to a particular address:

await blocknative.configuration({
  scope: String, // [required] - either 'global' or valid Ethereum address
  filters: Array, // [optional] - array of valid searchjs filter strings
  abi: Array, // [optional] - valid contract ABI
  watchAddress: Boolean // [optional] - Whether the server should automatically watch the "scope" value if it is an address
})

// returns a promise that resolves once the configuration has been applied
// or rejects if there was a problem with the configuration

There is a limit of 50 configurations per connection / API key. If you need to watch more than 50 addresses and also filter them, it is recommended that you use the blocknative.account method to subscribe to all of the addresses and then use the blocknative.configuration method to add a 'global' scoped filter that will apply to all of those addresses.

Options

scope - [required]

The scope that the configuration will be applied to. Can be a 'global' configuration so that you can filter all events for all addresses that you are watching or a valid Ethereum address to scope the configuration to events that occur only on that address.

filters - [optional]

The filters that you would like applied to events in scope. The Blocknative server uses jsql a JavaScript query language to filter events. Documentation for how to create filter queries can be found here

abi - [optional]

A JavaScript ABI (application binary interface) so that the Blocknative server can automatically decode transaction input data

watchAddress - [optional]

If the scope is an Ethereum address, then watchAddress can be set to true so that the Blocknative server can automatically watch the address in scope, rather than needing to send an extra call to blocknative.account

scope establishes the context in which a filter is applied. The global scope applies the filter to all transactions/addresses watched. Therefore a global scope only supports generic transaction elements. A scope set to an address can include filters, such as methodName,that are specific to the contract at that address. Similarly, the abi field only works for a contract specific scope.

Simulation Platform over WebSockets

Simulation platform provides visibility into the effects of internal transactions. It notably highlights netBalanceChanges of the contract calls a pending transaction is making, based on the most current state of the chain. Check the Simulation Platform docs for detailed information.

The SDK exposes a multiSim method that can be used to simulate transactions over a current WebSocket connection. The multiSim method takes an array of transaction objects (can be an array of 1) that you would like to simulate against the next block.

export interface Transaction {
  from: string
  to: string
  value: number
  gas: number
  input: string
  gasPrice?: number
  maxPriorityFeePerGas?: number
  maxFeePerGas?: number
}
const transactionsToSim = [
  {
    from: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
    to: "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
    gas: 332160,
    gasPrice: 0,
    input:
      "0xfb3bdb4100000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000746dff22206ce4e3da6d7eb4d537398be8f236d600000000000000000000000000000000000000000000000000000000624FD77D0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000094d916873b22c9c1b53695f1c002f78537b9b3b2",
    value: 28981678599856504,
  },
  {
    from: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
    to: "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
    gas: 332160,
    gasPrice: 0,
    input:
      "0xfb3bdb4100000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000746dff22206ce4e3da6d7eb4d537398be8f236d600000000000000000000000000000000000000000000000000000000624FD77D0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000094d916873b22c9c1b53695f1c002f78537b9b3b2",
    value: 28981678599856504,
  },
];

const result = await blocknative.multiSim(transactionsToSim)

console.log(result)

// {
//   simulatedBlockNumber: 15016676,
//   transactions: [
//     {
//       from: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
//       to: "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
//       value: 28981678599856504,
//       gas: 332160,
//       gasPrice: 0,
//       input:
//         "0xfb3bdb4100000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000746dff22206ce4e3da6d7eb4d537398be8f236d600000000000000000000000000000000000000000000000000000000624FD77D0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000094d916873b22c9c1b53695f1c002f78537b9b3b2",
//       type: 0,
//     },
//     {
//       from: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
//       to: "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
//       value: 28981678599856504,
//       gas: 332160,
//       gasPrice: 0,
//       input:
//         "0xfb3bdb4100000000000000000000000000000000000000000000003635c9adc5dea000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000746dff22206ce4e3da6d7eb4d537398be8f236d600000000000000000000000000000000000000000000000000000000624FD77D0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000094d916873b22c9c1b53695f1c002f78537b9b3b2",
//       type: 0,
//     },
//   ],
//   gasUsed: 288832,
//   internalTransactions: [[], []],
//   netBalanceChanges: [[], []],
//   error: ["execution reverted", "execution reverted"],
//   simDetails: {
//     blockNumber: 15016676,
//     performanceProfile: {
//       breakdown: [
//         {
//           label: "detected",
//           timeStamp: "2022-06-24T05:18:52.555Z",
//         },
//         {
//           label: "traceStart",
//           timeStamp: "2022-06-24T05:18:52.558Z",
//         },
//         {
//           label: "traceEnd",
//           timeStamp: "2022-06-24T05:18:52.572Z",
//         },
//         {
//           label: "dispatch",
//           timeStamp: "2022-06-24T05:18:52.572Z",
//         },
//       ],
//     },
//     e2eMs: 17,
//   },
//   serverVersion: "0.137.0-0.1.0",
//   system: "ethereum",
//   network: "main",
//   contractCall: [
//     {
//       status: "fulfilled",
//       value: {
//         methodName: "swapETHForExactTokens",
//         params: {
//           amountOut: "1000000000000000000000",
//           deadline: "1649399677",
//           path: [
//             "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
//             "0x94d916873b22c9c1b53695f1c002f78537b9b3b2",
//           ],
//           to: "0x746dff22206ce4e3da6d7eb4d537398be8f236d6",
//         },
//         contractAddress: "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
//         contractType: "Uniswap V2: Router 2",
//       },
//     },
//     {
//       status: "fulfilled",
//       value: {
//         methodName: "swapETHForExactTokens",
//         params: {
//           amountOut: "1000000000000000000000",
//           deadline: "1649399677",
//           path: [
//             "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
//             "0x94d916873b22c9c1b53695f1c002f78537b9b3b2",
//           ],
//           to: "0x746dff22206ce4e3da6d7eb4d537398be8f236d6",
//         },
//         contractAddress: "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
//         contractType: "Uniswap V2: Router 2",
//       },
//     },
//   ],
//   status: "simulated",
//   eventCode: "txSimulation",
//   timeStamp: "2022-06-24T05:18:50.157Z",
// };

Mempool Explorer Configurations

Export & Import

You can download a Mempool Explorer configuration to be used with the SDK. You can also use your account page to download a configuration that was saved to a specific API key. Once you have the files (configuration.json, sdk-setup.js) downloaded, drop them in to your project directory. They can then be imported and setup with the SDK:

import BlocknativeSDK from 'bnc-sdk'
import configuration from './configuration'
import sdkSetup from './sdk-setup'

// function to handle all transaction events
function handleTransactionEvent(transaction) {
  console.log('Transaction event:', transaction)
}

// initialize the SDK
const blocknative = BlocknativeSDK({
  // ...other initialization options
  transactionHandlers: [handleTransactionEvent]
})

// setup the configuration with the SDK
sdkSetup(blocknative, configuration)

Get Started Today

Sign up for a free Blocknative Account at https://explorer.blocknative.com/ with your work email address.

If you have any questions, connect with the team on our discord

Last updated