@web3-onboard/transaction-preview
(Deprecated) Transaction Preview support is going to be sunset on July 1st 2024 and will no longer work after that date
A modular UI for previewing a single or set of unsigned Ethereum transactions.
![Transaction Preview Flow Transaction Preview Flow](https://github.com/blocknative/web3-onboard/blob/develop/assets/transaction-preview.gif?raw=true)
Give Transaction Preview a test run by previewing Vitalik swapping tokens and find full package documentation here
Full Simulation Platform API documentation can be found here
Install
NPM
npm i @web3-onboard/core @web3-onboard/injected-wallets @web3-onboard/transaction-preview
Yarn
yarn add @web3-onboard/core @web3-onboard/injected-wallets @web3-onboard/transaction-preview
Usage with Web3-Onboard Core package
![Transaction Preview Image with Account Center Transaction Preview Image with Account Center](https://github.com/blocknative/web3-onboard/blob/develop/assets/transaction-preview.png?raw=true)
To use the Transaction Preview package with web3-onboard all a developer needs to do is initialize web3-onboard with their Blocknative API key and pass in the module as shown below.
import Onboard from '@web3-onboard/core'
import injectedModule from '@web3-onboard/injected-wallets'
import transactionPreviewModule from '@web3-onboard/transaction-preview'
const injected = injectedModule()
const transactionPreview = transactionPreviewModule({
})
const onboard = Onboard({
transactionPreview,
apiKey: 'xxx387fb-bxx1-4xxc-a0x3-9d37e426xxxx'
wallets: [injected],
chains: [
{
id: '0x1',
token: 'ETH',
label: 'Ethereum',
rpcUrl: 'https://mainnet.infura.io/v3/17c1e1500e384acfb6a72c5d2e67742e'
}
]
})
Standalone Usage
To use the Transaction Preview package without web3-onboard all a developer needs to do is:
- Execute the entry function from the
@web3-onboard/transaction-preview
package and optional params - Run the returned
init
function with their Blocknative API key, an initialized instance of their Blocknative SDK and a containerElement string with the html ID of the target element to append the visualization to - Finally pass a transaction meant for a wallet provider (created using libraries like Ethers or Web3)
With the above steps a UI will be rendered with the balance changes and gas used.
import transactionPreviewModule from '@web3-onboard/transaction-preview'
const {init, previewTransaction} = transactionPreviewModule({
})
await init({
apiKey: string
sdk: SDK
containerElement: string})
const simulate = async provider => {
const ethersProvider = new ethers.providers.Web3Provider(provider, 'any')
const signer = ethersProvider.getSigner()
const addressFrom = '0xcxxxxxx11111999991111'
const CONTRACT_ADDRESS = '0x7a250d5630b4cf539739df2c5dacb4c659f2488d'
const erc20_interface = [
'function approve(address _spender, uint256 _value) public returns (bool success)',
'function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)',
'function balanceOf(address owner) view returns (uint256)'
]
const uniswapV2router_interface = [
'function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'
]
const weth = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
const oneInch = '0x111111111117dc0aa78b770fa6a738034120c302'
let swapTxData
let approveTxData
const swapContract = new ethers.Contract(
CONTRACT_ADDRESS,
uniswapV2router_interface
)
const erc20_contract = new ethers.Contract(oneInch, erc20_interface)
const oneEther = ethers.BigNumber.from('9000000000000000000')
approveTxData = await erc20_contract.populateTransaction.approve(
CONTRACT_ADDRESS,
oneEther
)
const amountOutMin = 0
const amountOutMinHex = ethers.BigNumber.from(amountOutMin).toHexString()
const path = [oneInch, weth]
const deadline = Math.floor(Date.now() / 1000) + 60 * 1
const inputAmountHex = oneEther.toHexString()
swapTxData = await swapContract.populateTransaction.swapExactTokensForETH(
inputAmountHex,
amountOutMinHex,
path,
addressFrom,
deadline
)
const uniswapV2Router = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'
const popApproveTransaction = await signer.populateTransaction(approveTxData)
const popTransaction = await signer.populateTransaction(swapTxData)
const transactions = [
{ ...popApprovedTransaction, value: 0 },
{
...popTransaction,
from: addressFrom,
to: uniswapV2Router,
value: 0
}
]
return await previewTransaction(transactions)
}
const simData = simulate(ethereumProvider)
console.log(simData)
Options & Types
export type TransactionPreviewModule = (
options: TransactionPreviewOptions
) => TransactionPreviewAPI
export type FullPreviewOptions = TransactionPreviewOptions &
TransactionPreviewInitOptions
export type TransactionPreviewAPI = {
patchProvider: (provider: PatchedEIP1193Provider) => PatchedEIP1193Provider
init: (initializationOptions: TransactionPreviewInitOptions) => void
previewTransaction: (
transaction: TransactionForSim[]
) => Promise<MultiSimOutput>
}
export type PatchedEIP1193Provider = EIP1193Provider & { simPatched: boolean }
export interface ProviderReq {
method: string
params?: Array<unknown>
}
export type RequestOptions = Pick<TransactionPreviewInitOptions, 'apiKey'>
export type TransactionPreviewInitOptions = {
apiKey: string
sdk: SDK
containerElement: string
}
export type TransactionPreviewOptions = {
requireTransactionApproval?: boolean
i18n?: i18nOptions
}
export type Locale = string
export type i18nOptions = Record<Locale, i18n>
export type i18n = typeof en
export type DeviceNotBrowser = {
type: null
os: null
browser: null
}
export type TransactionForSim = SimulationTransaction & {
data?: string
}
export interface SimulationTransaction {
from: string
to: string
value: number
gas: number
input: string
gasPrice?: number
maxPriorityFeePerGas?: number
maxFeePerGas?: number
}
export type MultiSimOutput = {
id?: string
contractCall: ContractCall[]
error?: any
gasUsed: number[]
internalTransactions: InternalTransaction[][]
netBalanceChanges: NetBalanceChange[][]
network: Network
simDetails: SimDetails
serverVersion: string
system: System
status: Status
simulatedBlockNumber: number
transactions: InternalTransaction[]
}
export interface ContractCall {
contractType?: string
contractAddress?: string
contractAlias?: string
methodName: string
params: Record<string, unknown>
contractName?: string
contractDecimals?: number
decimalValue?: string
}
export interface InternalTransaction {
type: string
from: string
to: string
input: string
gas: number
gasUsed: number
value: string
contractCall: ContractCall
error?: string
errorReason?: string
}
export interface NetBalanceChange {
address: string
balanceChanges: BalanceChange[]
}
export interface BalanceChange {
delta: string
asset: Asset
breakdown: BreakDown[]
}
export interface Asset {
type: string
symbol: string
contractAddress: string
}
export interface BreakDown {
counterparty: string
amount: string
}
export interface InternalTransaction {
type: string
from: string
to: string
input: string
gas: number
gasUsed: number
value: string
contractCall: ContractCall
}
export type System = 'bitcoin' | 'ethereum'
export type Network =
| 'main'
| 'testnet'
| 'sepolia'
| 'xdai'
| 'bsc-main'
| 'matic-main'
| 'fantom-main'
| 'matic-mumbai'
| 'local'
export type Status =
| 'pending'
| 'confirmed'
| 'speedup'
| 'cancel'
| 'failed'
| 'dropped'
| 'simulated'
export interface SimDetails {
blockNumber: number
e2eMs: number
performanceProfile: any
}