Send Universal Transaction
Overview
Universal transactions let you send native transactions from any Layer 1 chain—EVM or non-EVM, even Push Chain itself—without wrapping, bridging or extra tooling required.
Under the hood, the SDK automatically estimates gas, orchestrates signatures, and replays any EVM or non-EVM proofs, so you can focus on your app—not the network plumbing.
Sending Universal Transaction
pushChainClient.universal.sendTransaction({tx}): Promise<TransactionReceipt>
const txHash = await pushChainClient.universal.sendTransaction({
to: '0xa54E96d3fB93BD9f6cCEf87c2170aEdB1D47E1cF',
value: PushChain.utils.helpers.parseUnits('0.1', 18), // 0.1 PC in uPC
// value: BigInt('100000000000000000') is equivalent here
});
TheseArguments
are mandatory
Arguments | Type | Description |
---|---|---|
tx.to | string | The address of the recipient. |
tx.value | BigInt | Amount of PC to send (in uPC, the smallest unit, like wei in ETH). |
tx.data | string | The data to send. |
tx.gasLimit | BigInt | The gas limit for the transaction. |
tx.maxFeePerGas | BigInt | The maximum fee per gas for the transaction. |
tx.maxPriorityFeePerGas | BigInt | The maximum priority fee per gas for the transaction. |
tx.funds | { amount: BigInt; token?: MoveableToken } | Moves supported assets from the origin chain to Push Chain within the same call. If tx.data is provided, assets are moved and then the contract call is executed atomically. |
Advanced Arguments
Arguments | Type | Default | Description |
---|---|---|---|
tx.deadline | BigInt | - | The deadline for the transaction. |
tx.progressHook | (progress: ProgressHookType) => void | - | A callback function to receive progress updates during transaction lifecycle, especially useful for tracking cross-chain transactions. |
ProgressHook Type and Response
Field | Type | Description |
---|---|---|
progress | Object | The progress of the transaction. |
progress.id | string | Unique identifier for the progress event. |
progress.title | string | Brief title of the progress event. |
progress.message | string | Detailed message describing the event. |
progress.level | INFO | SUCCESS | WARNING | ERROR | Severity level of the event. |
progress.timestamp | string | ISO-8601 timestamp when the event occurred (e.g. 2025-06-26T15:04:05.000Z ). |
ID | Title | Message | Level |
---|---|---|---|
SEND-TX-01 | Origin Chain Detected | Origin chain: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 | INFO |
SEND-TX-02-01 | Estimating Gas | Estimating and fetching gas limit, gas price for TX… | INFO |
SEND-TX-02-02 | Gas Estimated | Total execution cost (Gas cost + value): 1011250000000000000 UPC | SUCCESS |
SEND-TX-03-01 | Resolving UEA | Resolving Execution Account (UEA) – computing address, checking deployment status and balance | INFO |
SEND-TX-03-02 | UEA Resolved | UEA: 0x4f17B798A7d643d4F89cb2d8D42A72F84e83e566, Deployed: true | SUCCESS |
SEND-TX-04-01 | Awaiting Signature for Tx Execution | Universal Payload Hash: 0x1a579f18…fabac4309 | INFO |
SEND-TX-04-02 | Generated Universal Payload | Signature Completed | SUCCESS |
SEND-TX-05-01 | Locking Origin Chain Fee | Locking fee: 1000063864000000000 UPC on origin chain | INFO |
SEND-TX-05-02 | Awaiting Origin Chain Confirmations | Tx sent: 0xabc..234, waiting for 2 confirmations. | INFO |
SEND-TX-05-03 | Confirmations Received | Required confirmations received. | SUCCESS |
SEND-TX-06 | Broadcasting to Push Chain | Sending Tx to Push Chain… | INFO |
SEND-TX-99-01 | Push Chain TX Success | … | SUCCESS |
SEND-TX-99-02 | Push Chain TX Failed | … | ERROR |
Returns `TxResponse` <object>
{
hash: '0xe2302bd21ab0902f37cb605d491ce5f95ee35ce4083405dddf3657d782acae35',
origin: 'eip155:42101:0xFd6C2fE69bE13d8bE379CCB6c9306e74193EC1A9',
blockNumber: 0n,
blockHash: '',
transactionIndex: 0,
chainId: '42101',
from: '0xFd6C2fE69bE13d8bE379CCB6c9306e74193EC1A9',
to: '0x35B84d6848D16415177c64D64504663b998A6ab4',
nonce: 341,
data: '0x',
value: 1000n,
gasLimit: 21000n,
gasPrice: 1325000000n,
maxFeePerGas: 1325000000n,
maxPriorityFeePerGas: 125000000n,
accessList: [],
wait: [Function: wait],
type: '2',
typeVerbose: 'eip1559',
signature: {
r: '0x556566ba1304bf8e93025fc82daff32eb24b7ee9804a76d0baa0098dfa7237de',
s: '0x4495d7811d3dcb1beac16f29261903b542b0b65f51aa5942f65dbaf67e735724',
v: 1,
yParity: 1
},
raw: {
from: '0xFd6C2fE69bE13d8bE379CCB6c9306e74193EC1A9',
to: '0x35B84d6848D16415177c64D64504663b998A6ab4',
nonce: 341,
data: '0x',
value: 1000n
}
}
Property | Type | Description |
---|---|---|
hash | string | Unique transaction hash identifier |
origin | string | Origin identifier in format "eip155:chainId:address" or "solana:chainId:address" |
blockNumber | BigInt | Block number where transaction was included |
blockHash | string | Hash of the block containing this transaction |
transactionIndex | number | Position/index of transaction within the block |
chainId | string | Chain identifier (e.g. Push Chain = 42101 ) |
from | string | UEA (Universal Execution Address) that executed the transaction |
to | string | Target address the UEA executed against |
nonce | number | Derived nonce for the UEA |
data | string | Perceived calldata (transaction input data) |
value | BigInt | Amount of native tokens transferred (in wei) |
gasLimit | BigInt | Maximum gas units allocated for transaction |
gasPrice | BigInt | Gas price for legacy transactions (in wei) |
maxFeePerGas | BigInt | Maximum fee per gas for EIP-1559 transactions |
maxPriorityFeePerGas | BigInt | Maximum priority fee (tip) per gas for EIP-1559 |
accessList | array | EIP-2930 access list for optimized storage access |
type | string | Transaction type identifier |
typeVerbose | string | Human-readable transaction type |
signature | object | ECDSA signature components |
signature.r | string | R component of ECDSA signature |
signature.s | string | S component of ECDSA signature |
signature.v | number | Recovery ID (legacy format) |
signature.yParity | number | Y-parity for EIP-1559 (0 or 1) |
raw | object | Original on-chain transaction data |
raw.from | string | Actual from address that went on chain |
raw.to | string | Actual to address that went on chain |
raw.nonce | number | Actual raw nonce used on chain |
raw.data | string | Actual raw data that went on chain |
raw.value | BigInt | Actual derived value that went on chain |
wait | function | Async function that returns a Promise resolving to UniversalTxReceipt |
Getting `txReceipt` <object> from `txResponse` <object>
Calling the wait()
function from txResponse
object will give you a Promise<UniversalTxReceipt>
once the transaction is confirmed on-chain.
const txReceipt = await txResponse.wait(1); // number of blocks confirmations to wait for
{
hash: '0xb52706db4116dd6bbea87be5142ac2c69b17fe8ccf8e2b88ac176adb30b90dd6',
blockNumber: 3413247n,
blockHash: '0x5a7b6e2716f7d4450b6ca08aebfe74cea3d876367a8afe6f603196ba8c346a2d',
transactionIndex: 0,
from: '0xFd6C2fE69bE13d8bE379CCB6c9306e74193EC1A9',
to: '0x35B84d6848D16415177c64D64504663b998A6ab4',
contractAddress: null,
gasPrice: 1325000000n,
gasUsed: 21000n,
cumulativeGasUsed: 21000n,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: 1,
raw: {
from: '0xFd6C2fE69bE13d8bE379CCB6c9306e74193EC1A9',
to: '0x35B84d6848D16415177c64D64504663b998A6ab4',
nonce: 342,
data: '0x',
value: 1000n
}
}
Property | Type | Description |
---|---|---|
hash | string | Transaction hash (same as in transaction response) |
blockNumber | BigInt | Block number where transaction was confirmed |
blockHash | string | Hash of the block containing the transaction |
transactionIndex | number | Position/index of transaction within the block |
from | string | Executor account address (UEA on Push Chain) |
to | string | Actual intended target address of the transaction |
contractAddress | string | null | Address of deployed contract (null for regular transfers) |
gasPrice | BigInt | Gas price used for the transaction (in wei) |
gasUsed | BigInt | Actual gas consumed by the transaction |
cumulativeGasUsed | BigInt | Total gas used by all transactions in the block up to this one |
logs | array | Array of log objects emitted by the transaction |
logsBloom | string | Bloom filter for efficient log searching |
status | number | Transaction status (1 = success, 0 = failure) |
raw | object | Raw on-chain transaction data |
raw.from | string | Actual from address that executed on chain |
raw.to | string | Actual to address that was called on chain |
raw.nonce | number | Actual nonce used on chain |
raw.data | string | Actual calldata sent on chain |
raw.value | BigInt | Actual value transferred on chain |
Send Transaction with Contract Interaction
When calling a smart contract method via sendTransaction, supply the ABI-encoded function call as a hex string in the data field. You can choose ethers
or viem
or any of your favorite libraries to encode the function data. Or, use our utility function PushChain.utils.helpers.encodeTxData
to encode the function data.
// Define the ABI for the ERC20 transfer function
const erc20Abi = [
'function transfer(address to, uint256 amount) returns (bool)',
];
// Generate the encoded function data using viem
const data = PushChain.utils.helpers.encodeTxData({
abi: erc20Abi,
functionName: 'transfer',
// Transfer 10 tokens, converted to 18 decimal places
args: ['0xRecipientAddress', PushChain.utils.helpers.parseUnits('10', 18)],
});
// Send the transaction using Push Chain SDK
const txHash = await pushChainClient.universal.sendTransaction({
to: '0xTokenContractAddress', // The smart contract address on Push Chain
value: BigInt('0'), // No $PC being sent, just contract interaction
data: data, // The encoded function call
});
Send Transaction with Funds
You can move supported assets (e.g., USDT, USDC, or other tokens) from your origin chain to Push Chain and execute your call in a single transaction.
Use the funds field to specify the amount of assets to move, and optionally the data field to specify the function call to execute on Push Chain.
Note: funds transactions are only supported from external origin chains.
Native Push Chain users should call ERC-20transfer
ortransferFrom
directly (instead of using funds).
// Send 1 USDT to the recipient address
const txHash = await pushChainClient.universal.sendTransaction({
to: '0xRecipientAddress', // The recipient address on Push Chain
data: data, // pass this if you want to execute a function on Push Chain as well
funds: {
amount: PushChain.utils.helpers.parseUnits('1', 6), // 1 USDT
token: client.moveable.token.USDT, // MoveableToken accessor from client
},
});
Send Batch Transactions (Multicall)
You can batch multiple calls into a single transaction on Push Chain. This pattern is commonly referred to as Multicall in EVM ecosystems.
To do so, instead of passing a single data
field, supply an array of calls (each with to
, value
, and data
) to sendTransaction.
Note: Batch transactions are only supported from external origin chains.
Native Push Chain users cannot use batch mode.
// Execute two increment() calls atomically
const incrementData = PushChain.utils.helpers.encodeTxData({
abi: CounterABI,
functionName: "increment",
});
await client.universal.sendTransaction({
// Must be the Universal Execution Account (UEA) for this signer
to: client.universal.account,
data: [
{ to: "0xCounterContract", value: 0n, data: incrementData },
{ to: "0xCounterContract", value: 0n, data: incrementData },
],
});
For multicall, the to field must always be set to the Universal Execution Account (UEA) of the signer. You can access it with client.universal.account. The SDK will throw an error if you pass any other address.
Live Playground
- Quickstart
- Ethers (v6)
- Viem
- Solana (Web3 JS)
- UI Kit
// Import necessary components from @pushchain/ui-kit import { PushUniversalWalletProvider, PushUniversalAccountButton, usePushWalletContext, usePushChainClient, PushUI, } from '@pushchain/ui-kit'; function App() { // Define Wallet Config const walletConfig = { network: PushUI.CONSTANTS.PUSH_NETWORK.TESTNET, }; function Component() { const [txnHash, setTxnHash] = useState<string | null>(null); const [isLoading, setIsLoading] = useState(false); const { connectionStatus } = usePushWalletContext(); const { pushChainClient } = usePushChainClient(); const handleSendTransaction = async () => { if (pushChainClient) { setIsLoading(true); try { const res = await pushChainClient.universal.sendTransaction({ to: '0xFaE3594C68EDFc2A61b7527164BDAe80bC302108', value: PushChain.utils.helpers.parseUnits('0.001', 18), // 0.001 PC in uPC data: '0x', }); setTxnHash(res.hash); } catch (err) { console.log(err); } finally { setIsLoading(false); } } }; return ( <div> <PushUniversalAccountButton /> {connectionStatus == PushUI.CONSTANTS.CONNECTION.STATUS.CONNECTED && <button disabled={isLoading} style={{ background: 'transparent', border: '1px solid', borderRadius: '10px', padding: '12px 18px', cursor: 'pointer', margin: '8px 0', }} onClick={handleSendTransaction} > Send Transaction </button> } {txnHash && ( <> <p>Txn Hash: {txnHash}</p> <a href={pushChainClient.explorer.getTransactionUrl(txnHash)} target="_blank" rel="noopener noreferrer" > View in Explorer </a> </> )} </div> ); } return ( <PushUniversalWalletProvider config={walletConfig}> <Component /> </PushUniversalWalletProvider> ); }
Live Playground: Interact with Smart Contract
- From Push Chain
- From Sepolia
- From Solana Devnet
Live Playground: Move Funds to Push Chain
- Move Funds (Native)
- Move Funds (ERC-20)
- Move Funds with Payload
Live Playground: Do Batch Transactions in a Single Universal Transaction
Live Playground: (Advanced) Track the progress lifecycle of a transaction
Next Steps
- Sign arbitrary data with Sign Universal Message
- Surface live feedback in your UI by handling the
progressHook
events - Leverage pre-built utilities in Contract Helpers
- Integrate transaction flows into your frontend app using the UI Kit