The Universal Accounts SDK provides a complete transaction preview when you call createBuyTransaction()
or any other transaction-related method.
You can use this preview to show users key transaction details before they confirm by including it in your transaction flow UI.
Transaction Preview Overview
When you call createBuyTransaction()
or similar methods, the SDK returns a structured preview object with key transaction details.
Here’s a breakdown of the main fields included in this preview:
interface UniversalTransaction {
type : "universal" ;
mode : "mainnet" ; // Current environment
sender : string ;
receiver : string ;
transactionId : string ;
smartAccountOptions : {
name : string ;
version : string ;
ownerAddress : string ;
smartAccountAddress : string ;
solanaSmartAccountAddress : string ;
senderAddress : string ;
senderSolanaAddress : string ;
};
depositTokens : TokenTransfer []; // Tokens *being deposited* (decreasing from sender)
lendingTokens : TokenTransfer []; // Tokens *being received* via lending or conversion
tokenChanges : {
from : string ;
fromChains : number [];
to : string ;
toChains : number [];
decr : TokenTransfer [];
incr : TokenTransfer [];
};
feeQuotes : {
fees : {
totals : {
feeTokenAmountInUSD : string ;
gasFeeTokenAmountInUSD : string ;
transactionFeeTokenAmountInUSD : string ;
transactionServiceFeeTokenAmountInUSD : string ;
transactionLPFeeTokenAmountInUSD : string ;
solanaRentFeeAmountInUSD : string ;
};
feeTokens : TokenTransfer [];
freeGasFee : boolean ;
freeServiceFee : boolean ;
};
userOps : UserOpExecution [];
}[];
transactionFees : {
freeGasFee : boolean ;
freeServiceFee : boolean ;
transactionServiceFeeAmountInUSD : string ;
transactionLPFeeAmountInUSD : string ;
};
tag : string ;
gasless : boolean | null ;
fallback : boolean ;
data : MerkleProof [];
rootHash : string ;
userOps : UserOpExecution [];
}
See all 61 lines
Previewing a Transaction (Example)
To see a real example, here’s how you can create and log a buy transaction for $1 worth of $PARTI :
const transaction = await universalAccount . createBuyTransaction ({
token: {
chainId: CHAIN_ID . BSC_MAINNET ,
address: "0x59264f02D301281f3393e1385c0aEFd446Eb0F00" , // PARTI token on BNB Chain
},
amountInUSD: "1" ,
});
console . log ( JSON . stringify ( transaction , null , 2 ));
This example uses createBuyTransaction
, but the same approach will apply to any transaction method supported by the SDK.
Displaying Estimated Fees
The returned transaction object includes metadata such as sender and recipient addresses, tokens used, and estimated fees .
Here’s how to extract and display the estimated fees:
import { formatUnits } from "ethers" ;
const feeQuote = transaction . feeQuotes [ 0 ];
const fee = feeQuote . fees . totals ;
console . log ( "Total fee (USD):" , `$ ${ formatUnits ( fee . feeTokenAmountInUSD , 18 ) } ` );
console . log ( "Gas fee (USD):" , `$ ${ formatUnits ( fee . gasFeeTokenAmountInUSD , 18 ) } ` );
console . log ( "Service fee (USD):" , `$ ${ formatUnits ( fee . transactionServiceFeeTokenAmountInUSD , 18 ) } ` );
console . log ( "LP fee (USD):" , `$ ${ formatUnits ( fee . transactionLPFeeTokenAmountInUSD , 18 ) } ` );
We use formatUnits
from ethers.js
, but feel free to use any formatting utility that fits your stack.
Integrating the Transaction Preview
You can integrate the transaction preview however you like in your dApp. In the demo repository below, estimated fees are shown before the user confirms the transaction.
Demo Repository Check out the demo repository for a working example.
The guide and demo app above focus on a minimal example that previews estimated fees only.
For a more complete implementation—including sender and receiver addresses, token transfers, and detailed metadata—refer to the extended widget below:
TransactionDetailsWidget.tsx
import { formatUnits } from "ethers" ;
interface Token {
name : string ;
symbol : string ;
chainId : number ;
decimals : number ;
realDecimals : number ;
address : string ;
assetId : string ;
type : string ;
slotIndex : number ;
image : string ;
rank : number ;
price : number ;
}
interface TokenAmount {
token : Token ;
amount : bigint ;
}
interface FeeToken {
token : Token ;
amount : bigint ;
}
interface FeeTotals {
feeTokenAmountInUSD : string ;
gasFeeTokenAmountInUSD : string ;
transactionFeeTokenAmountInUSD : string ;
transactionServiceFeeTokenAmountInUSD : string ;
transactionLPFeeTokenAmountInUSD : string ;
solanaRentFeeAmountInUSD : string ;
}
interface FeeQuote {
feeTokens : FeeToken [];
freeGasFee : boolean ;
freeServiceFee : boolean ;
totals : FeeTotals ;
}
interface TransactionDetails {
sender : string ;
receiver : string ;
depositTokens ?: TokenAmount [];
lendingTokens ?: TokenAmount [];
feeQuotes ?: { fees : FeeQuote }[];
}
const TokenList = ({
tokens ,
title ,
emoji ,
} : {
tokens : TokenAmount [];
title : string ;
emoji : string ;
}) => {
if ( ! tokens ?. length ) return null ;
return (
< div className = "mb-4" >
< h3 className = "text-lg font-semibold mb-2" >
{ emoji } { title } :
</ h3 >
< div className = "space-y-2 pl-4" >
{ tokens . map (( item , i ) => {
const t = item . token ;
return (
< div key = { i } className = "bg-gray-50 p-3 rounded-lg" >
< div className = "font-medium" >
[ { i + 1 } ] { t . name } ( { t . symbol } )
</ div >
< div className = "text-sm text-gray-600 pl-2" >
< div > Chain ID: { t . chainId } </ div >
< div > Amount: { formatUnits ( item . amount , t . decimals ) } </ div >
</ div >
</ div >
);
}) }
</ div >
</ div >
);
};
const FeeSection = ({ feeQuote } : { feeQuote : FeeQuote }) => {
if ( ! feeQuote ?. feeTokens ?. length ) return null ;
console . log ( JSON . stringify ( feeQuote ));
return (
< div className = "mb-4" >
< h3 className = "text-lg font-semibold mb-2" > 💸 Fee Tokens: </ h3 >
< div className = "space-y-2 pl-4" >
{ feeQuote . feeTokens . map (( fee , i ) => {
const t = fee . token ;
return (
< div key = { i } className = "bg-gray-50 p-3 rounded-lg" >
< div className = "font-medium" >
[ { i + 1 } ] { t . name } ( { t . symbol } )
</ div >
< div className = "text-sm text-gray-600 pl-2" >
< div > Chain ID: { t . chainId } </ div >
< div > Amount: { formatUnits ( fee . amount , t . decimals ) } </ div >
</ div >
</ div >
);
}) }
< div className = "text-sm text-gray-600 mt-2 space-y-1" >
< div > Free Gas Fee: { feeQuote . freeGasFee ? "✅" : "❌" } </ div >
< div > Free Service Fee: { feeQuote . freeServiceFee ? "✅" : "❌" } </ div >
< div className = "pt-2 space-y-1 border-t border-gray-200" >
< div >
Total Fee: { " " }
< span className = "font-medium" >
$ { formatUnits ( BigInt ( feeQuote . totals . feeTokenAmountInUSD ), 18 ) }
</ span >
</ div >
< div >
Gas Fee: { " " }
< span className = "font-medium" >
$ { formatUnits ( BigInt ( feeQuote . totals . gasFeeTokenAmountInUSD ), 18 ) }
</ span >
</ div >
< div >
Transaction Fee: { " " }
< span className = "font-medium" >
$ { formatUnits ( BigInt ( feeQuote . totals . transactionFeeTokenAmountInUSD ), 18 ) }
</ span >
</ div >
< div >
Service Fee: { " " }
< span className = "font-medium" >
$ { formatUnits ( BigInt ( feeQuote . totals . transactionServiceFeeTokenAmountInUSD ), 18 ) }
</ span >
</ div >
< div >
LP Fee: { " " }
< span className = "font-medium" >
$ { formatUnits ( BigInt ( feeQuote . totals . transactionLPFeeTokenAmountInUSD ), 18 ) }
</ span >
</ div >
</ div >
</ div >
</ div >
</ div >
);
};
export function TransactionDetailsWidget ({
transaction ,
} : {
transaction : TransactionDetails ;
}) {
const feeQuotePreview = transaction . feeQuotes ?.[ 0 ]?. fees ;
return (
< div className = "bg-white p-6 rounded-xl shadow-sm border" >
< h2 className = "text-xl font-bold mb-4" > Transaction Details </ h2 >
< div className = "mb-4" >
< div className = "grid grid-cols-1 md:grid-cols-2 gap-4" >
< div >
< span className = "font-semibold" > 🔹 Sender: </ span >
< span className = "font-mono text-sm" > { transaction . sender } </ span >
</ div >
< div >
< span className = "font-semibold" > 🔹 Receiver: </ span >
< span className = "font-mono text-sm" > { transaction . receiver } </ span >
</ div >
</ div >
</ div >
< TokenList
tokens = { transaction . depositTokens || [] }
title = "Deposit Tokens"
emoji = "📥"
/>
< TokenList
tokens = { transaction . lendingTokens || [] }
title = "Lending Tokens"
emoji = "📤"
/>
{ feeQuotePreview && < FeeSection feeQuote = { feeQuotePreview } /> }
</ div >
);
}
See all 189 lines
What’s Next?
Explore the SDK reference for more details: