Appearance
Customizing wallet prompts
By default, when you request a signature or transaction from a user's embedded wallet, Privy will display a UI to have your user preview the signature/transaction and confirm it.
These UIs are highly-customizable, allowing you to contextualize onchain actions within the broader experience of your product. Read below to learn more!
INFO
Only signatures and transactions requested from the embedded wallet allow for UI customization, as external wallet actions must be confirmed via the external wallet's UI, outside of the scope of Privy.
Signing messages
To request a signature from a user with a custom UI prompt, use the signMessage
method returned by the usePrivy
hook:
tsx
const {signMessage} = usePrivy();
When invoked, signMessage
will request an EIP-191 personal_sign
signature from the embedded wallet, and returns a Promise for the user's signature as a string.
signMessage
takes the following parameters. In particular, you can use the optional uiConfig
to customize the prompt shown to the user to contextualize their signing action.
Parameter | Type | Description |
---|---|---|
message | string | Required. The message the user must sign as a string . |
uiConfig | SignMessageModalUIOptions | Optional. Options to customize the signature prompt shown to the user. Defaults to the values outlined below |
uiConfig.title | string | Optional. The title text for the signature prompt. Defaults to 'Sign'. |
uiConfig.description | string | Optional. The description text for the signature prompt. Defaults to 'Sign to continue'. |
uiConfig.buttonText | string | Optional. The description text for the signature prompt. Defaults to 'Sign and Continue'. |
Example
Below is an example of customizing the signature prompt:
tsx
import {usePrivy} from '@privy-io/react-auth';
function SignMessageButton() {
const {signMessage} = usePrivy();
const message = 'Hello world';
const uiConfig = {
title: 'Sample title text',
description: 'Sample description text',
buttonText: 'Sample button text',
};
return (
<button
onClick={async () => {
// Use `signature` below however you'd like
const signature = await signMessage(message, uiConfig);
}}
>
Sign
</button>
);
}
Signing typed data
To have a user sign an EIP-712 typed data signature with a custom UI prompt, use the signTypedData
method returned by the usePrivy
hook:
tsx
const {signTypedData} = usePrivy();
When invoked, signTypedData
will request an EIP-721 eth_signTypedData_v4
signature from the embedded wallet, and returns a Promise for the user's signature as a string.
signTypedData
takes the following parameters. In particular, you can use the optional uiConfig
to customize the prompt shown to the user to contextualize their signing action.
Parameter | Type | Description |
---|---|---|
typedData | SignedTypedDataParams | Required. A JSON object that conforms to the EIP-712 TypedData JSON schema. |
uiConfig | SignMessageModalUIOptions | Optional. Options to customize the signature prompt shown to the user. Defaults to the values outlined below |
uiConfig.title | string | Optional. The title text for the signature prompt. Defaults to 'Sign'. |
uiConfig.description | string | Optional. The description text for the signature prompt. Defaults to 'Sign to continue'. |
uiConfig.buttonText | string | Optional. The description text for the signature prompt. Defaults to 'Sign and Continue'. |
Example
Below is an example of customizing the typedData prompt:
tsx
import {usePrivy} from '@privy-io/react-auth';
function SignTypedDataButton() {
const {signTypedData} = usePrivy();
// Example from https://github.com/MetaMask/test-dapp/blob/285ef74eec90dbbb4994eff4ece8c81ba4fc77f9/src/index.js#L1585
const domain = {
name: 'Example Message',
version: '1.0.0',
chainId: 1,
salt: '0',
verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC' as `0x${string}`,
};
// The named list of all type definitions
const types = {
Person: [
{name: 'name', type: 'string'},
{name: 'wallet', type: 'address'},
],
Mail: [
{name: 'from', type: 'Person'},
{name: 'to', type: 'Person'},
{name: 'contents', type: 'string'},
],
// Necessary to define salt param type
EIP712Domain: [
{
name: 'name',
type: 'string',
},
{
name: 'version',
type: 'string',
},
{
name: 'chainId',
type: 'uint256',
},
{
name: 'salt',
type: 'string',
},
{
name: 'verifyingContract',
type: 'string',
},
],
};
// The data to sign
const value = {
from: {
name: 'Cow',
wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
},
to: {
name: 'Bob',
wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB',
},
contents: 'Hello, Bob!',
};
const typedData = {primaryType: 'Mail', domain: domain, types: types, message: value};
const uiConfig = {
title: 'Sample title text',
description: 'Sample description text',
buttonText: 'Sample button text',
};
return (
<button
onClick={async () => {
// Use `signature` below however you'd like
const signature = await signTypedData(typedData, uiConfig);
}}
>
Sign Typed Data
</button>
);
}
Transactions
To request a transaction from a user with a custom UI prompt, use the sendTransaction
method returned by the usePrivy
hook:
tsx
const {sendTransaction} = usePrivy();
When invoked, sendTransaction
will make an eth_sendTransaction
request to your user's wallet to send the specified transaction to the blockchain. The method returns a Promise for a TransactionReceipt
object once it has been confirmed by the network.
INFO
Need to interact with a smart contract? Calling a smart contract method is a special case of a transaction.
sendTransaction
takes the following parameters:
Parameter | Type | Description |
---|---|---|
requestData | UnsignedTransactionRequest | Required. The transaction request to be sent. |
uiConfig | SendTransactionModalUIOptions | Optional. Options to customize the transaction prompt shown to the user. Defaults to the values outlined below |
fundWalletConfig | FundWalletConfig | Optional. Options to customize the fiat on-ramp flow for the wallet, if your app has Privy's fiat on-ramp enabled. |
In particular, you should use:
UnsignedTransactionRequest
to prepare the transaction to be sentSendTransactionModalUIOptions
to customize the transaction prompt shown to the user.
Building the transaction
To prepare the transaction for the user's embedded wallet, set your desired transaction details in the UnsignedTransactionRequest
object.
The possible fields for this object are listed below. All of the fields are optional. If fields relating to gas consumption are left unspecified (e.g. gasPrice
, maxFeePerGas
), Privy will compute a real-time estimate for gas.
TIP
If you choose to manually configure any parameters related to gas fees, please make sure to pass in an explicit value for all of the following parameters: type
(EIP-1559 or not), gasLimit
, and
maxFeePerGas
/maxPriorityFeePerGas
fortype: 2
transactions (EIP-1559), orgasPrice
fortype: 0
or1
transactions (legacy or EIP-2930).
If you only pass a subset of the parameters above, Privy will estimate the rest according to our defaults, which may result in unpredictable behavior for transactions.
Field | Type | Description |
---|---|---|
to | string | Destination address of the transaction. Must be hexadecimal formatted. |
nonce | number | Nonce for this transaction. Defaults to the current transaction count for the wallet. |
chainId | number | Chain ID for the network that this transaction should be sent on. Defaults to the current chain of the wallet. |
data | string | Data to send to the receiving address, especially when calling smart contracts. Must be hexadecimal formatted. |
value | Quantity | Amount (in wei) to transfer from sender to receiver. |
type | number | EIP-2718 type of this transaction. |
gasLimit | Quantity | Max. units of gas that can be used by this transaction. |
gasPrice | Quantity | Price (in wei) per unit of gas for this transaction. This should only be set for non-EIP-1559 transactions. |
maxFeePerGas | Quantity | Maximum price (in wei) per unit of gas to be paid total for this transaction. This includes the base fee and the priority fee. This should only be set for EIP-1559 transactions. |
maxPriorityFeePerGas | Quantity | Maximum price (in wei) per unit of gas to be paid in addition to the base fee, to increase the transaction's priority. This includes the base fee and the priority fee. This should only be set for EIP-1559 transactions. |
Customizing the prompt
To customize the transaction prompt for the user's embedded wallet, set your desired customizations in the SendTransactionModalUIOptions
object.
The possible fields for this object are listed below. All of the fields are optional. If a default is not explicitly listed, Privy will omit that element from the UI if you do not set a value for that parameter. We recommend that you experiment with a number of prompt customizations to arrive at the best option for your users.
Field | Type | Description |
---|---|---|
header | string | Header text for the transaction screen. Defaults to 'Review transaction' or 'Send <token symbol>' if the transaction is a native token transfer. |
description | string | Description text for the transaction. |
buttonText | string | Text to show on the transaction confirmation button. Defaults to 'Submit'. |
successHeader | string | Header text for the transaction success screen. Defaults to 'Transaction complete!' |
successDescription | string | Description text for the transaction success screen. Defaults to 'You're all set.' |
transactionInfo | Object | Information to show in the transaction details accordion. The fields for this object are outlined below. |
transactionInfo.title | Quantity | Title for the transaction details accordion. |
transactionInfo.action | number | Short description of the action taken by the user. Should be <4 words (e.g. 'Send NFT'). Shown within the transaction details accordion. |
transactionInfo.contractInfo | Object | Additional information to show in the transaction details accordion, when the transaction is a smart contract interaction. |
transactionInfo.contractInfo.name | string | Name of the smart contract being called (e.g. 'Uniswap' or 'Seaport'). |
transactionInfo.contractInfo.url | string | URL to show for the smart contract. |
transactionInfo.contractInfo.imgUrl | string | URL of a hosted image to show in the transaction prompt. |
transactionInfo.contractInfo.imgSize | 'sm' | 'lg' | Size of hosted image to show, if transactionInfo.contractInfo.imgUrl is set. |
Customizing price display
By default, Privy's transaction prompt will show prices in both the native token (e.g. ETH) and in USD. If you'd like to customize Privy's transaction prompt to show prices only in the native token and not in USD, update the config.embeddedWallets.priceDisplay
property of your PrivyProvider
component like so:
tsx
<PrivyProvider
appId="insert-your-privy-app-id"
config={{
embeddedWallets: {
priceDisplay: {
primary: 'native-token', // Defaults to 'fiat-currency'
secondary: null, // Defaults to 'native-token'
},
},
}}
>
{children}
</PrivyProvider>
Setting funding options
This section only applies to apps that have funding enabled.
If a user attempts to send a transaction with the embedded wallet and does not has sufficient funds to complete the transaction, Privy will automatically display an "Add funds" button in the transaction prompt for the user.
When a user clicks the "Add funds" button, they will be taken through a funding flow with your selected method, where they can top up their wallet with the assets they need.
You can customize the funding flow for a given transaction by passing in a FundWalletConfig
object as a third, optional parameter to sendTransaction
, like so:
tsx
// Configure the onramp to purchase 0.02 ETH if out of funds
const fundWalletConfig = {amount: '0.02'};
await sendTransaction(insertYourTransaction, insertYourUIConfig, fundWalletConfig);
The chain ID from fundWalletConfig
will be overridden by the transaction's configured chain.
Getting the receipt
Once invoked, sendTransaction
will return a Promise for a TransactionReceipt
object with details about the sent transaction. See a breakdown of this object's parameters below.
Field | Type | Description |
---|---|---|
to | string | Receiving address of the transaction. May either be an externally-owned account or a smart contract. |
from | string | Sending address of the transaction. |
type | number | EIP-2718 type of this transaction. |
status? | number | 1 if the transaction was successful and 0 if the transaction reverted. Only available for transactions sent after the Byzantium Hard Fork. |
blockHash | string | Hash of the block this transaction was included in. |
transactionHash | string | Hash of this transaction. You may use this value to inspect the transaction on a Block Explorer for your respective network. |
blockNumber | number | Height/number of the block this transaction was included in. |
gasUsed | string | Amount of gas consumed by this transaction. Formatted as a hexadecimal string. |
cumulativeGasUsed | string | Amount of gas consumed by this transaction and every preceding transaction that was mined in the same block. |
effectiveGasPrice | string | Effective gas price the transaction charged the sending address. |
contractAddress | string | If the transaction deploys a smart contract, this is the address of the deployed contract. This should only happen if the receiving address (to ) for this transaction is null . |
logs | Array<TransactionLog> | List of all TransactionLog s emitted by this transaction. |
logsBloom | string | Bloom filter of all addresses and topics included in any log in this transaction. |
confirmations | number | Number of blocks that have been mined since this transaction, including the block that contains this transaction. |
transactionIndex | number | The index of this transaction in the list of transactions that were included in the transaction's block. |
root? | string | Intermediate state root of the transaction receipt. Only used by transactions sent before the Byzantium Hard Fork. |
byzantium | boolean | Returns true if the transaction block was mined after the Byzantium Hard Fork. |
Example
Below is an example of customizing the transaction prompt:
tsx
import {usePrivy} from '@privy-io/react-auth';
import type {UnsignedTransactionRequest, SendTransactionModalUIConfig} from '@privy-io/react-auth';
function SendTransactionButton() {
const {sendTransaction} = usePrivy();
const requestData: UnsignedTransactionRequest = {
to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
chainId: 1,
value: '0x3B9ACA00',
};
const uiConfig: SendTransactionModalUIConfig = {
header: 'Sample header text',
description: 'Sample description text',
buttonText: 'Sample button text',
};
return (
<button
onClick={async () => {
const txReceipt = await sendTransaction(unsignedTx, uiConfig);
// The returned `txReceipt` has the type `TransactionReceipt`
}}
>
Send ETH
</button>
);
}
Full UI Customization
Privy also enables your app to use your own, fully custom UIs to prompt users for signatures and transactions.
To do so, set the Add confirmation modals toggle to off in the Embedded wallets page of the dashboard, per this guide.