Skip to main content

Link external wallets

Passport has a feature where users can link their external wallets (e.g. MetaMask, WalletConnect etc) to their account. Note that smart contract wallets are not currently supported, any attempts to link will result in a validation error. This allows the user to take control over their most valuable assets security while still being able to use them in-game in a read-only capacity. You can find a guide on getting a users linked addresses here.

Instead of needing to send your user to the Passport Dashboard to link their external wallets you can create the link within your own game/application.

1. Prove wallet ownership

To prove the user owns the wallet being linked you will need to obtain an EIP-712 signature.

You can obtain this signature via whichever Ethereum library you choose. You must follow the typed data structure below, providing values for walletAddress, immutablePassportAddress, and nonce.

You will need to find a way for generating the nonce, in this example we've utilized the siwe library.

Here is an example getting the signature with wagmi.

Note: the wallet will need to be connected to the Ethereum chain (id: 1) to sign the message. If it isn't you will need to prompt the user to change or attempt a switch chain via your wallet connecting library.

import { generateNonce } from 'siwe';
import { Connector, useConnect, useSignTypedData } from 'wagmi';

const { connectAsync } = useConnect();
const { signTypedDataAsync } = useSignTypedData();

try {
// Connect user's wallet - available connectors depends on your wagmi config
const connectResult = await connectAsync({ connector });

// Get Passport address
const passportProvider = passport.connectEvm();
const accounts = await passportProvider.request({ method: "eth_requestAccounts" });
const nonce = generateNonce();

const signature = await signTypedDataAsync({
types: {
EIP712Domain: [
{
name: "chainId",
type: "uint256"
}
],
LinkWallet: [
{
name: "walletAddress",
type: "address"
},
{
name: "immutablePassportAddress",
type: "address"
},
{
name: "condition",
type: "string"
},
{
name: "nonce",
type: "string"
}
]
},
primaryType: "LinkWallet",
domain: {
chainId: BigInt(1)
},
message: {
walletAddress: connectResult.accounts[0],
immutablePassportAddress: accounts[0],
condition: "I agree to link this wallet to my Immutable Passport account.",
nonce
}
})
} catch (e) {
// handle error
}

There are two methods for linking the wallet, you can call the API directly (refer to the API reference) or if you are using the TypeScript SDK (version 1.46.0-alpha.2 or higher) there is a method available:

try {
const result = await passport.linkExternalWallet({
type: connector.id,
walletAddress: connectResult.accounts[0],
signature: signature,
nonce: nonce
});
} catch (e) {
// handle error
}

For both methods you will need to provide:

  • the wallet type - this should be the rdns value (as per EIP-6963). If using wagmi it will be the connector.id value.
  • address of the external wallet being connected
  • the resulting signature
  • the generated nonce

If you don't provide these correct values the linking will fail.

Error handling

There are a few conditions that can lead to 400 errors with this API. Below shows the code from the API response body and what it means:

  • ALREADY_LINKED: the wallet is already linked to a Passport account.
  • MAX_WALLETS_LINKED: a Passport account can have a maximum of 10 wallets linked to it at any time.
  • DUPLICATE_NONCE: the provided nonce has previously been used to link a wallet, try again with a new nonce.
  • VALIDATION_ERROR: the type, signature, nonce, or bearer token provided was invalid.

The TypeScript SDK will map these to a PassportError and you can handle them accordingly.

3. Fetch all linked wallets

Once you've successfully linked the wallet you can then fetch the users linked addresses (guide here). With this list you can build a combined inventory for your player that includes assets from all of their wallets.