Appearance
Integrating a wallet from another app
Privy allows you to easily integrate embedded wallets from other apps, to verify ownership of users' wallet addresses or even request signatures and transactions from.
This reduces friction around having users transact onchain in your app, as users can easily pull in their assets and identity from other apps where they may already have embedded wallets.
Within this setup, your app is known as the requester app.
Finding available providers
To integrate embedded wallets from another app, first visit the Privy Dashboard and navigate to the Ecosystem page for your app. From there, open the Integrations tab to see a list of provider app IDs that consent to sharing their wallets with other apps.
For any providers you'd like to integrate, note down the provider's Privy app ID, as you will use this value in the interfaces outlined below.
TIP
Some providers may only consent to sharing their users' wallets in read-only mode, in which case your app can verify that the user owns a particular address, but cannot request signatures or transactions from it.
Logging in with an account from a provider
With the Privy login modal
You can add any provider app to the list of login options in the Privy login modal by adding "privy:"
+ the provider's app ID to loginMethodsAndOrder
in the Privy SDK configuration.
tsx
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || ""}
config={{
loginMethodsAndOrder: {
primary: ['email', 'google', 'privy:insert-provider-app-id'],
},
...
}}
>
Headlessly
To prompt users to log into your app with an account from a provider app, use the loginWithCrossAppAccount
method from the useCrossAppAccounts
hook:
tsx
const {loginWithCrossAppAccount} = useCrossAppAccounts();
As a parameter to this method, pass an object containing the following fields:
Parameter | Type | Description |
---|---|---|
appId | string | Required. The Privy app ID of the provider app from which you'd like a user to link their account. You can find a list of Privy app IDs for provider apps in the Cross-app ecosystem page of the Privy Dashboard. |
When loginWithCrossAppAccount
is invoked, the user will be redirected to a page hosted on the domain of the provider app you specified to authorize access to your own app.
If the user successfully authorizes access, the user will be redirected back to your app, and an account of type: 'cross_app'
will be added to the linkedAccounts
array of their user
object.
loginWithCrossAppAccount
will throw an error if:
- The user does not authorize access to your app or exits the flow prematurely.
- The provider app you request has not opted-in to share their wallets.
- The user is not
authenticated
and thus cannot link an account from the provider app to an existing account within your requester's app. - The user does not already have an account with the provider app.
TIP
If the user is already logged in on the domain of the source appId
you specify in loginWithCrossAppAccount
, they will not have to login again and will only have to consent to sharing access to that account in your app.
As an example, you might set up a button to link a cross-app wallet like so:
tsx
import {usePrivy} from '@privy-io/react-auth';
function Button() {
const {ready, authenticated} = usePrivy();
const {loginWithCrossAppAccount} = useCrossAppAccounts();
return (
<button
onClick={() => loginWithCrossAppAccount({appId: 'insert-provider-app-id'})}
disabled={!ready || !authenticated}
>
Log in with [insert-provider-app-name]
</button>
);
}
Linking a wallet from a provider
To prompt users to link their embedded wallet from a provider app, use the linkCrossAppAccount
method from the useCrossAppAccounts
hook:
tsx
const {linkCrossAppAccount} = useCrossAppAccounts();
As a parameter to this method, pass an object containing the following fields:
Parameter | Type | Description |
---|---|---|
appId | string | Required. The Privy app ID of the provider app from which you'd like a user to link their account. You can find a list of Privy app IDs for provider apps in the Cross-app ecosystem page of the Privy Dashboard. |
When linkCrossAppAccount
is invoked, the user will be redirected to a page hosted on the domain of the provider app you specified to authorize access to your own app.
If the user successfully authorizes access, the user will be redirected back to your app, and an account of type: 'cross_app'
will be added to the linkedAccounts
array of their user
object.
linkCrossAppAccount
will throw an error if:
- The user does not authorize access to your app or exits the flow prematurely.
- The provider app you request has not opted-in to share their wallets.
- The user is not
authenticated
and thus cannot link an account from the provider app to an existing account within your requester's app.
TIP
If the user is already logged in on the domain of the source appId
you specify in linkCrossAppAccount
, they will not have to login again and will only have to consent to sharing access to that account in your app.
As an example, you might set up a button to link a cross-app wallet like so:
tsx
import {usePrivy} from '@privy-io/react-auth';
function Button() {
const {ready, authenticated} = usePrivy();
const {linkCrossAppAccount} = useCrossAppAccounts();
return (
<button
onClick={() => linkCrossAppAccount({appId: 'insert-provider-app-id'})}
disabled={!ready || !authenticated}
>
Link your [insert-provider-app-name] account
</button>
);
}
Getting linked provider wallets
Once a user has successfully linked their account from a provider app, an account of type: 'cross_app'
will be added to the linkedAccounts
array of their user
object. You can find this account like so:
tsx
const {user} = usePrivy();
const crossAppAccount = user.linkedAccounts.find((account) => account.type === 'cross_app');
This crossAppAccount
is an object with the following properties:
Field | Type | Value |
---|---|---|
type | 'cross_app' | Indicates that the linked account is a cross-app account. |
embeddedWallets | {address: string}[] | An array of the user's embedded wallet(s) from the provider app for the cross-app account. |
providerApp | ProviderAppMetadata | Metadata about the provider app for the cross-app account. |
firstVerifiedAt | Date | Datetime when the cross-app account was first linked to the user. |
latestVerifiedAt | Date | Datetime for when the user last logged in/linked the cross-app account. |
To get the user's embedded wallet address from this account, simply inspect the embeddedWallets
field of the crossAppAccount
:
tsx
const embeddedWalletAddress = crossAppAccount.embeddedWallets[0].address;
To get metadata about the provider app for the wallet, such as its Privy app ID, name, and logo, inspect the providerApp
field of crossAppAccount
:
tsx
const providerApp = crossAppAccount.providerApp;
This providerApp
metadata is an object with the following properties:
Field | Type | Value |
---|---|---|
id | string | Privy app ID of the source app. |
name | string? | Name of the source app. |
logoUrl | string? | Logo of the source app. |
Using wallets from a provider
Once a user has successfully linked a cross-app account, the user may use their embedded wallet from the provider app within your app per the interfaces below.
Getting the wallet address
To find a user's cross-app embedded wallet, first find the account of type: 'cross_app'
from their linkedAccounts
array. If your app permits users to link cross-app accounts from multiple provider apps, you should also filter the linkedAccounts
by the providerApp.id
on the cross-app account.
tsx
// Replace `providerAppId` with the Privy app ID of your desired provider app
const providerAppId = 'insert-provider-app-id';
const crossAppAccount = user.linkedAccounts.find(
(account) => account.type === 'cross_app' && account.providerApp.id === providerAppId,
);
Next, from the crossAppAccount
, inspect the embeddedWallets
array to get the user's embedded wallet address(es) from the source app.
tsx
const address = crossAppAccount.embeddedWallets[0].address;
INFO
Most users only have one embedded wallet per app. Certain apps, however, leverage multiple embedded wallets (e.g. HD wallets) per user to support certain use cases. In kind, the embeddedWallets
field is designed as an array, but generally will be a singleton containing one entry.
Requesting signatures and transaction
INFO
When you request a signature or transaction from a user's embedded wallet from another app, Privy requires the user to explicitly confirm the signature or transaction. This is accomplished by opening a popup to the provider app's domain, where the user confirms the action in an isolated environment.
To request signatures and transactions from a user's embedded wallet from a provider app, use the signMessage
, signTypedData
, and sendTransaction
methods returned by Privy's useCrossAppAccounts
hook:
tsx
import {useCrossAppAccounts} from '@privy-io/react-auth';
const {signMessage, signTypedData, sendTransaction} = useCrossAppAccounts();
These methods are similar to the signMessage
, signTypedData
, and [`sendTransaction](/guide/react/wallets/embedded/transact.md#send-transaction) methods returned by `usePrivy` except they all require an additional `CrossAppWalletOptions` object of the following type:
Parameter | Type | Description |
---|---|---|
address | string | Required. The address for the cross-app embedded wallet that you'd like to request a signature/transaction from. |
If the address
you specify in this CrossAppWalletOptions
object is not a valid embedded wallet that has been linked to the current user from a provider app, these wallet methods will error.
Continue below for the specific parameters required by each method.
signMessage
To the signMessage
method returned by useCrossAppAccounts
, pass the following parameters:
Parameter | Type | Description |
---|---|---|
message | string | Required. The message the user must sign as a string . |
options | CrossAppWalletOptions | Required. Options for the cross-app embedded wallet, which must include the requested wallet's address |
signTypedData
Parameter | Type | Description |
---|---|---|
typedData | SignedTypedDataParams | Required. A JSON object that conforms to the EIP-712 TypedData JSON schema. |
options | CrossAppWalletOptions | Required. Options for the cross-app embedded wallet, which must include the requested wallet's address |
sendTransaction
Parameter | Type | Description |
---|---|---|
requestData | UnsignedTransactionRequest | Required. The transaction request to be sent. |
options | CrossAppWalletOptions | Required. Options for the cross-app embedded wallet, which must include the requested wallet's address |
Example signature request
As an example, you might request a signature from a user's cross-app wallet like so:
tsx
import {usePrivy, useCrossAppAccounts} from '@privy-io/react-auth';
function Button() {
const {user} = usePrivy();
const {signMessage} = useCrossAppAccounts();
const crossAppAccount = user.linkedAccounts.find((account) => account.type === 'cross_app');
const address = crossAppAccount.embeddedWallets[0].address;
return (
<button onClick={() => signMessage('Hello world', {address: address})} disabled={!address}>
Sign a message with your cross-app wallet
</button>
);
}