For end users: Players can manage their wallet, view balances, and browse transaction history at Immutable Play. Direct your users there for wallet management features.
Overview
This page covers essential wallet operations including retrieving wallet addresses, checking native IMX and ERC-20 token balances, sending transactions (transfers, contract interactions, NFTs), signing messages (ERC-191 personal sign and EIP-712 typed data), and error handling patterns.Prerequisites
Get Wallet Address
- TypeScript
- Unity
- Unreal
Copy
Ask AI
// First, initialize the zkEVM provider
const provider = await connectWallet({ auth });
// Then get the wallet address
const accounts = await provider.request({ method: 'eth_requestAccounts' });
const address = accounts[0];
console.log('Wallet address:', address);
Copy
Ask AI
// First, initialize the zkEVM provider (call once after login)
await passport.ConnectEvm();
// Then get the wallet address
var accounts = await passport.ZkEvmRequestAccounts();
string address = accounts[0];
Debug.Log($"Wallet address: {address}");
Copy
Ask AI
// First, initialize the zkEVM provider (call once after login)
Passport->ConnectEvm(UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [this](FImmutablePassportResult ConnectResult)
{
if (ConnectResult.Success)
{
// Then get the wallet address
Passport->ZkEvmRequestAccounts(UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [this](FImmutablePassportResult Result)
{
if (Result.Success)
{
// Parse JSON response to get accounts
const auto RequestAccountsData = FImmutablePassportZkEvmRequestAccountsData::FromJsonObject(Result.Response.JsonObject);
FString Address = RequestAccountsData->accounts[0];
UE_LOG(LogTemp, Log, TEXT("Address: %s"), *Address);
}
}));
}
}));
Linked Addresses
Users can link external wallets (like MetaMask, WalletConnect, etc.) to their Passport account, allowing them to use the same identity across multiple wallets.- TypeScript
- Unity
- Unreal
Copy
Ask AI
import { getLinkedAddresses } from '@imtbl/wallet';
import { config } from '@imtbl/sdk';
// Initialize API client
const apiClient = new config.ImmutableConfiguration({
environment: config.Environment.SANDBOX,
}).getMultiRollupApiClients();
// Get all wallets linked to the current Passport account
const linkedAddresses = await getLinkedAddresses(auth, apiClient);
console.log('Linked addresses:', linkedAddresses);
// Returns: ['0x123...', '0x456...']
Copy
Ask AI
List<string> addresses = await Passport.Instance.GetLinkedAddresses();
if (addresses.Count > 0)
{
Debug.Log("Linked addresses:");
foreach (string address in addresses)
{
Debug.Log($" {address}");
}
}
else
{
Debug.Log("No linked addresses");
}
Copy
Ask AI
UImmutablePassport* Passport = PassportSubsystem->GetPassport();
Passport->GetLinkedAddresses(UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [](this, FImmutablePassportResult Result)
{
if (Result.Success)
{
const TArray<FString> Addresses = UImmutablePassport::GetResponseResultAsStringArray(Result.Response);
UE_LOG(LogTemp, Log, TEXT("Linked addresses:"));
for (const FString& Address : Addresses)
{
UE_LOG(LogTemp, Log, TEXT(" %s"), *Address);
}
} else
{
UE_LOG(LogTemp, Error, TEXT("Failed to get linked addresses: %s"), *Result.Message);
}
}));
Important:
ConnectEvm() must be called once after login to initialize the zkEVM provider before calling ZkEvmRequestAccounts() or any other wallet operations.Check Balances
Native IMX and ERC-20 Token Balances- TypeScript
- Unity
- Unreal
Copy
Ask AI
import { BrowserProvider, formatEther, Contract } from 'ethers';
const provider = connectWallet({ auth });
const accounts = await provider.request({ method: 'eth_requestAccounts' });
const address = accounts[0];
const balance = await provider.request({
method: 'eth_getBalance',
params: [address, 'latest']
});
console.log('IMX Balance:', formatEther(balance), 'IMX');
// Get ERC-20 token balance
const erc20Abi = [
'function balanceOf(address owner) view returns (uint256)',
'function decimals() view returns (uint8)',
'function symbol() view returns (string)',
];
const tokenAddress = '0x...'; // Your ERC-20 token contract address
const ethersProvider = new BrowserProvider(provider);
const tokenContract = new Contract(tokenAddress, erc20Abi, ethersProvider);
const tokenBalance = await tokenContract.balanceOf(address);
const decimals = await tokenContract.decimals();
const symbol = await tokenContract.symbol();
console.log(`Token Balance: ${formatEther(tokenBalance)} ${symbol}`);
Copy
Ask AI
// Get native IMX balance using Passport's zkEVM provider
string balance = await passport.ZkEvmGetBalance(playerAddress);
Debug.Log($"Balance: {balance} wei");
// For ERC-20 token balances, use the Orderbook TokenBalance API
using Immutable.Orderbook.Api;
var orderbookApi = new OrderbookApi();
var tokenBalance = await orderbookApi.TokenBalanceAsync(
walletAddress: playerAddress,
contractAddress: "0xYourTokenContract..."
);
Debug.Log($"Token balance: {tokenBalance.Quantity}");
The
ZkEvmGetBalance method returns balance in wei as a string. For ERC-20 tokens, use the Orderbook API’s TokenBalanceAsync method.Copy
Ask AI
// Get native IMX balance
FImmutablePassportZkEvmGetBalanceData BalanceData;
BalanceData.Address = PlayerAddress;
BalanceData.BlockNumberOrTag = TEXT("latest"); // Optional: defaults to "latest"
Passport->ZkEvmGetBalance(BalanceData, UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda([](this, FImmutablePassportResult Result)
{
if (Result.Success)
{
// Result.Message contains balance in wei as hex string
FString BalanceWei = Result.Message;
UE_LOG(LogTemp, Log, TEXT("Balance (wei): %s"), *BalanceWei);
} else
{
UE_LOG(LogTemp, Error, TEXT("Failed: %s"), *Result.Message);
}
}));
For ERC-20 token balances, use the Orderbook API’s
TokenBalanceAsync method (same as Unity). The ZkEvmGetBalance method returns balance in wei as a hex string.Send Transactions
Wei Conversion: 1 IMX = 10^18 wei. For TypeScript, use
parseUnits('1', 18) from ethers or viem. Unity/Unreal require string values in wei.Send Transaction with Confirmation
Recommended for critical operations - Waits for blockchain confirmation before returning.- TypeScript
- Unity
- Unreal
Copy
Ask AI
import { BrowserProvider } from 'ethers';
// Get the provider from Passport
const provider = await connectWallet({ auth });
// Wrap provider in ethers BrowserProvider
const browserProvider = new BrowserProvider(provider);
// Get the signer (represents the user's wallet)
const signer = await browserProvider.getSigner();
// Send transaction using signer
const tx = await signer.sendTransaction({
to: '0xRecipient...',
value: '1500000000000000000', // 1.5 IMX in wei
});
// Wait for blockchain confirmation
const receipt = await tx.wait();
console.log('Transaction confirmed!');
console.log('Hash:', receipt.hash);
console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');
Copy
Ask AI
using Immutable.Passport.Model;
TransactionRequest request = new TransactionRequest()
{
to = address,
value = amount,
data = data
}
TransactionReceiptResponse response = await passport.ZkEvmSendTransactionWithConfirmation(request);
switch (response.status)
{
case "1":
// Successful
break;
case "0":
// Failed
break;
}
Copy
Ask AI
DECLARE_DELEGATE_OneParam(FOnTransactionConfirmed, const FZkEvmTransactionReceipt&);
DECLARE_DELEGATE_OneParam(FOnTransactionConfirmationFailed, FString);
UCLASS()
class YOURGAME_API USendTransactionWithConfirmation : public UObject
{
GENERATED_BODY()
public:
static void Execute(const FString& ToAddress, const FString& ValueInWei, const FString& Data, FOnTransactionConfirmed OnSuccess, FOnTransactionConfirmationFailed OnFailure)
{
UImmutableSubsystem* Subsystem = GEngine->GetEngineSubsystem<UImmutableSubsystem>();
if (!Subsystem)
{
OnFailure.ExecuteIfBound(TEXT("No subsystem"));
return;
}
TWeakObjectPtr<UImmutablePassport> Passport = Subsystem->GetPassport();
if (!Passport.IsValid())
{
OnFailure.ExecuteIfBound(TEXT("No passport"));
return;
}
FImtblTransactionRequest Request;
Request.to = ToAddress;
Request.value = ValueInWei;
Request.data = Data;
USendTransactionWithConfirmation* Helper = NewObject<USendTransactionWithConfirmation>();
Helper->AddToRoot();
Helper->Success = OnSuccess;
Helper->Failure = OnFailure;
Passport->ZkEvmSendTransactionWithConfirmation(Request, UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject( Helper, &ThisClass::OnResponse));
}
private:
void OnResponse(FImmutablePassportResult Result)
{
if (Result.Success)
{
TOptional<FZkEvmTransactionReceipt> Receipt = JsonObjectToUStruct<FZkEvmTransactionReceipt>(Result.Response.JsonObject);
if (Receipt.IsSet())
{
Success.ExecuteIfBound(Receipt.GetValue());
}
else
{
Failure.ExecuteIfBound(TEXT("No receipt"));
}
}
else
{
Failure.ExecuteIfBound(Result.Error);
}
RemoveFromRoot();
}
private:
FOnTransactionConfirmed Success;
FOnTransactionConfirmationFailed Failure;
};
Copy
Ask AI
USendTransactionWithConfirmation::Execute(
TEXT("0xRecipient..."),
TEXT("1000000000000000000"), // 1 IMX
TEXT("0x"), // No data
FOnTransactionConfirmed::CreateLambda([](const FZkEvmTransactionReceipt& Receipt)
{
UE_LOG(LogTemp, Log, TEXT("Confirmed! Hash: %s"), *Receipt.hash);
UE_LOG(LogTemp, Log, TEXT("Status: %s"), *Receipt.status);
}),
FOnTransactionConfirmationFailed::CreateLambda([](FString Error)
{
UE_LOG(LogTemp, Error, TEXT("Failed: %s"), *Error);
}));
When to use: Critical operations like transfers, mints, or state changes. Ensures transaction succeeded before updating game state.
Example: Interacting with Smart Contracts
Example: Interacting with Smart Contracts
Copy
Ask AI
import { BrowserProvider, ethers } from 'ethers';
// Setup provider and signer
const provider = await connectWallet({ auth });
const browserProvider = new BrowserProvider(provider);
const signer = await browserProvider.getSigner();
// Define contract ABI (just the functions you need)
const abi = [
'function safeTransferFrom(address from, address to, uint256 tokenId)'
];
// Create contract instance
const contract = new ethers.Contract(
'0xYourNFTContract...', // Contract address
abi,
signer // Signer enables sending transactions
);
// Call contract function - automatically sends transaction
const tx = await contract.safeTransferFrom(
userAddress,
recipientAddress,
tokenId
);
// Wait for confirmation
const receipt = await tx.wait();
console.log('NFT transferred!', receipt.hash);
Send Transaction without Confirmation
For fire-and-forget operations - Returns transaction hash immediately without waiting.- TypeScript
- Unity
- Unreal
Copy
Ask AI
import { BrowserProvider } from 'ethers';
// Get the provider from Passport
const provider = await connectWallet({ auth });
// Wrap provider in ethers BrowserProvider
const browserProvider = new BrowserProvider(provider);
// Get the signer (represents the user's wallet)
const signer = await browserProvider.getSigner();
// Send transaction - returns immediately without waiting
const tx = await signer.sendTransaction({
to: '0xRecipient...',
value: '1500000000000000000', // 1.5 IMX in wei
});
console.log('Transaction sent:', tx.hash);
// User can continue - transaction confirms in background
Fire-and-Forget: Returns immediately without waiting for blockchain confirmation. Use for non-critical operations where you want responsive UI (cosmetic purchases, achievements, analytics).
Copy
Ask AI
using Cysharp.Threading.Tasks;
using Immutable.Passport.Model;
using System.Threading;
async void GetTransactionReceiptStatus()
{
TransactionRequest request = new TransactionRequest()
{
to = address,
value = amount,
data = data
}
string transactionHash = await passport.ZkEvmSendTransaction(request);
string? status = await PollStatus(
passport,
transactionHash,
TimeSpan.FromSeconds(1), // Poll every one second
TimeSpan.FromSeconds(10) // Stop polling after 10 seconds
);
switch (status)
{
case "0x1":
// Successful
break;
case "0x0":
// Failed
break;
}
}
static async UniTask<string?> PollStatus(Passport passport, string transactionHash, TimeSpan pollInterval, TimeSpan timeout)
{
var cancellationTokenSource = new CancellationTokenSource(timeout);
try
{
while (!cancellationTokenSource.Token.IsCancellationRequested)
{
TransactionReceiptResponse response = await passport.ZkEvmGetTransactionReceipt(transactionHash);
if (response.status == null)
{
// The transaction is still being processed, poll for status again
await UniTask.Delay(delayTimeSpan: pollInterval, cancellationToken: cancellationTokenSource.Token);
}
else
{
return response.status;
}
}
}
catch (OperationCanceledException)
{
// Task was canceled due to timeout
}
return null; // Timeout or could not get transaction receipt
}
Copy
Ask AI
DECLARE_DELEGATE_TwoParams(FOnTransactionSent, FString /*TxHash*/, FString /*Status*/);
DECLARE_DELEGATE_OneParam(FOnTransactionFailed, FString);
UCLASS()
class YOURGAME_API USendTransactionWithoutConfirmation : public UObject
{
GENERATED_BODY()
public:
static void Execute(
const FString& ToAddress,
const FString& ValueInWei,
const FString& Data,
FOnTransactionSent OnSuccess,
FOnTransactionFailed OnFailure)
{
UImmutableSubsystem* Subsystem = GEngine->GetEngineSubsystem<UImmutableSubsystem>();
if (!Subsystem)
{
OnFailure.ExecuteIfBound(TEXT("No subsystem"));
return;
}
TWeakObjectPtr<UImmutablePassport> Passport = Subsystem->GetPassport();
if (!Passport.IsValid())
{
OnFailure.ExecuteIfBound(TEXT("No passport"));
return;
}
FImtblTransactionRequest Request;
Request.to = ToAddress;
Request.value = ValueInWei;
Request.data = Data;
USendTransactionWithoutConfirmation* Helper = NewObject<USendTransactionWithoutConfirmation>();
Helper->AddToRoot();
Helper->Passport = Passport;
Helper->Success = OnSuccess;
Helper->Failure = OnFailure;
Passport->ZkEvmSendTransaction(
Request,
UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(
Helper, &ThisClass::OnSent));
}
private:
void OnSent(FImmutablePassportResult Result)
{
if (Result.Success)
{
TxHash = UImmutablePassport::GetResponseResultAsString(Result.Response);
Ticker = FTSTicker::GetCoreTicker().AddTicker(
FTickerDelegate::CreateUObject(this, &ThisClass::Tick));
}
else
{
Failure.ExecuteIfBound(Result.Error);
RemoveFromRoot();
}
}
bool Tick(float DeltaTime)
{
Time += DeltaTime;
if (Time >= 10.0f)
{
Failure.ExecuteIfBound(TEXT("Timeout"));
Cleanup();
return false;
}
if (Time >= NextPoll && !Polling)
{
NextPoll = Time + 1.0f;
Poll();
}
return true;
}
void Poll()
{
if (!Passport.IsValid())
{
Failure.ExecuteIfBound(TEXT("No passport"));
Cleanup();
return;
}
Polling = true;
FZkEvmTransactionReceiptRequest Req;
Req.txHash = TxHash;
Passport->ZkEvmGetTransactionReceipt(
Req,
UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(
this, &ThisClass::OnReceipt));
}
void OnReceipt(FImmutablePassportResult Result)
{
Polling = false;
if (Result.Success)
{
TOptional<FZkEvmTransactionReceipt> Receipt =
JsonObjectToUStruct<FZkEvmTransactionReceipt>(Result.Response.JsonObject);
if (Receipt.IsSet() && !Receipt->status.IsEmpty())
{
Success.ExecuteIfBound(TxHash, Receipt->status);
Cleanup();
}
}
}
void Cleanup()
{
if (Ticker.IsValid())
FTSTicker::GetCoreTicker().RemoveTicker(Ticker);
RemoveFromRoot();
}
private:
TWeakObjectPtr<UImmutablePassport> Passport;
FString TxHash;
FTSTicker::FDelegateHandle Ticker;
float Time = 0.0f;
float NextPoll = 1.0f;
bool Polling = false;
FOnTransactionSent Success;
FOnTransactionFailed Failure;
};
Copy
Ask AI
USendTransactionWithoutConfirmation::Execute(
TEXT("0xRecipient..."),
TEXT("1000000000000000000"),
TEXT("0x"),
FOnTransactionSent::CreateLambda([](FString TxHash, FString Status)
{
UE_LOG(LogTemp, Log, TEXT("Confirmed! %s - Status: %s"), *TxHash, *Status);
}),
FOnTransactionFailed::CreateLambda([](FString Error)
{
UE_LOG(LogTemp, Error, TEXT("Failed: %s"), *Error);
}));
Important: Don’t update game state until transaction is confirmed. Use this pattern only for non-critical operations or when you have custom polling logic.
Transaction Hash ≠ Success: Obtaining the transaction hash does not guarantee a successful transaction. To determine the transaction’s status, use
ZkEvmGetTransactionReceipt along with the transaction hash received. Follow best practices for client-side polling: set maximum attempts, polling intervals, and implement timeout handling.When to use: Non-critical operations or when you need custom polling/retry logic. Most games should use “with confirmation” variant for critical operations.
NFT Transfer
- TypeScript
- Unity
- Unreal
Copy
Ask AI
const ERC721_ABI = [{
name: 'transferFrom',
type: 'function',
inputs: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'tokenId', type: 'uint256' },
],
outputs: [],
stateMutability: 'nonpayable',
}] as const;
export async function transferNFT(
nftContract: `0x${string}`,
from: `0x${string}`,
to: `0x${string}`,
tokenId: bigint
) {
// highlight-start
const hash = await walletClient.writeContract({
account: from,
address: nftContract,
abi: ERC721_ABI,
functionName: 'transferFrom',
args: [from, to, tokenId],
});
// highlight-end
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('NFT transferred! Block:', receipt.blockNumber);
return receipt;
}
Copy
Ask AI
public class NFTTransfer : MonoBehaviour
{
public async void Execute()
{
try
{
string receiverAddress = "0x..."; // Receiver's wallet address
string tokenId = "123"; // NFT Token ID
string tokenAddress = "0x..."; // NFT Contract Address
UnsignedTransferRequest transferRequest = UnsignedTransferRequest.ERC721(receiverAddress, tokenId, tokenAddress);
CreateTransferResponseV1 response = await Passport.Instance.ImxTransfer(transferRequest);
Debug.Log($"NFT transferred successfully! Transfer ID: {response.transfer_id}");
}
catch (System.Exception ex)
{
Debug.LogError($"Transfer failed: {ex.Message}");
}
}
}
Copy
Ask AI
DECLARE_DELEGATE(FOnImxNftTransferSuccess);
DECLARE_DELEGATE_OneParam(FOnImxNftTransferFailed, FString);
UCLASS()
class YOURGAME_API UNftTransfer : public UObject
{
GENERATED_BODY()
public:
static void Execute(const FString& Receiver, const FString& TokenId, const FString& TokenAddress, FOnImxNftTransferSuccess OnSuccess, FOnImxNftTransferFailed OnFailure)
{
UImmutableSubsystem* Subsystem = GEngine->GetEngineSubsystem<UImmutableSubsystem>();
if (!Subsystem)
{
OnFailure.ExecuteIfBound(TEXT("No subsystem"));
return;
}
TWeakObjectPtr<UImmutablePassport> Passport = Subsystem->GetPassport();
if (!Passport.IsValid())
{
OnFailure.ExecuteIfBound(TEXT("No passport"));
return;
}
FImxTransferRequest Request;
Request.receiver = Receiver;
Request.type = TEXT("ERC721");
Request.tokenId = TokenId;
Request.tokenAddress = TokenAddress;
UImxNftTransfer* Helper = NewObject<UImxNftTransfer>();
Helper->AddToRoot();
Helper->Success = OnSuccess;
Helper->Failure = OnFailure;
Passport->ImxTransfer(Request, UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(Helper, &ThisClass::OnResponse));
}
private:
void OnResponse(FImmutablePassportResult Result)
{
if (Result.Success) Success.ExecuteIfBound();
else Failure.ExecuteIfBound(Result.Error);
RemoveFromRoot();
}
private:
FOnImxNftTransferSuccess Success;
FOnImxNftTransferFailed Failure;
};
Sign Messages
Personal Sign (ERC-191)
For authentication or simple message signing:- TypeScript
- Unity
- Unreal
Copy
Ask AI
// Assuming you have the Passport provider from connectEvm()
const provider = connectWallet({ auth });
const accounts = await provider.request({ method: 'eth_requestAccounts' });
const address = accounts[0];
const message = 'Hello from Immutable!';
// Sign the message using personal_sign (ERC-191)
const signature = await provider.request({
method: 'personal_sign',
params: [message, address],
});
console.log('Signature:', signature);
Unity SDK Limitation: Unity SDK does not support ERC-191 personal sign. Only EIP-712 typed data signing is supported via
ZkEvmSignTypedDataV4(). See the Typed Data section below for message signing in Unity.Unreal SDK Limitation: Unreal SDK does not support ERC-191 personal sign. Only EIP-712 typed data signing is supported via
ZkEvmSignTypedDataV4(). See the Typed Data section below for message signing in Unreal.Typed Data (EIP-712)
For structured data signing (used by protocols like Seaport):- TypeScript
- Unity
- Unreal
Copy
Ask AI
// Assuming you have the Passport provider from connectEvm()
const provider = connectWallet({ auth });
const accounts = await provider.request({ method: 'eth_requestAccounts' });
const address = accounts[0];
// Get chain ID
const chainIdHex = await provider.request({ method: 'eth_chainId' });
const chainId = parseInt(chainIdHex, 16);
// Define EIP-712 typed data structure
const typedData = {
domain: {
name: 'My Game',
version: '1',
chainId,
verifyingContract: address, // Your contract address
},
message: {
itemId: 123,
price: '1000000000000000000', // 1 token in wei
},
primaryType: 'Trade',
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' },
],
Trade: [
{ name: 'itemId', type: 'uint256' },
{ name: 'price', type: 'uint256' },
],
},
};
// Sign typed data using eth_signTypedData_v4
const signature = await provider.request({
method: 'eth_signTypedData_v4',
params: [address, JSON.stringify(typedData)],
});
console.log('Typed data signature:', signature);
Copy
Ask AI
// Construct EIP-712 typed data as JSON string
string typedDataJson = @"{
""domain"": {
""name"": ""My Game"",
""version"": ""1"",
""chainId"": 13473,
""verifyingContract"": ""0xYourContract...""
},
""types"": {
""Trade"": [
{ ""name"": ""itemId"", ""type"": ""uint256"" },
{ ""name"": ""price"", ""type"": ""uint256"" }
]
},
""primaryType"": ""Trade"",
""message"": {
""itemId"": 123,
""price"": ""10000000000000000000""
}
}";
var signature = await passport.ZkEvmSignTypedDataV4(typedDataJson);
Debug.Log($"Signature: {signature}");
Copy
Ask AI
FString TypedDataJson = TEXT(R"({
"domain": {
"name": "My Game",
"version": "1",
"chainId": 13473,
"verifyingContract": "0xYourContract..."
},
"types": {
"Trade": [
{ "name": "itemId", "type": "uint256" },
{ "name": "price", "type": "uint256" }
]
},
"primaryType": "Trade",
"message": {
"itemId": "123",
"price": "10000000000000000000"
}
})");
Passport->ZkEvmSignTypedDataV4(TypedDataJson, UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [](FImmutablePassportResult Result)
{
if (Result.Success)
{
FString Signature = UImmutablePassport::GetResponseResultAsString(Result.Response);
UE_LOG(LogTemp, Log, TEXT("Signature: %s"), *Signature);
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to sign: %s"), *Result.Error);
}
}));
Error Handling
- TypeScript
- Unity
- Unreal
Copy
Ask AI
// Assuming you have the Passport provider from connectEvm()
const provider = connectWallet({ auth });
async function sendTransactionWithErrorHandling() {
try {
const accounts = await provider.request({ method: 'eth_requestAccounts' });
const fromAddress = accounts[0];
const hash = await provider.request({
method: 'eth_sendTransaction',
params: [{
from: fromAddress,
to: '0xRecipient...',
value: '1000000000000000000', // 1 IMX in wei
}],
});
console.log('Transaction sent:', hash);
return { success: true, hash };
} catch (error) {
// Handle different error cases
if (error instanceof Error) {
if (error.message.includes('User rejected')) {
console.log('User rejected the transaction');
return { success: false, error: 'User rejected' };
}
if (error.message.includes('insufficient funds')) {
console.error('Insufficient balance');
return { success: false, error: 'Insufficient funds' };
}
console.error('Transaction error:', error.message);
return { success: false, error: error.message };
}
throw error;
}
}
Copy
Ask AI
try
{
var response = await passport.ZkEvmSendTransactionWithConfirmation(request);
Debug.Log($"Success: {response.transactionHash}");
}
catch (Exception e)
{
Debug.LogError($"Transaction failed: {e.Message}");
}
Copy
Ask AI
void UCustomLocalPlayer::SignSubmitApproval(const FString& To, const FString& Data, TFunction<void(FString TransactionHash, FString Status)> Callback)
{
FImtblTransactionRequest Request;
Request.to = To;
Request.data = Data;
Request.value = "0";
Passport->ZkEvmSendTransactionWithConfirmation(Request, UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [this, Callback](FImmutablePassportResult Result)
{
if (!Result.Success)
{
return;
}
auto Receipt = JsonObjectToUStruct<FZkEvmTransactionReceipt>(Result.Response.JsonObject);
FString TransactionHash, Status;
if (Receipt.IsSet())
{
TransactionHash = Receipt.GetValue().hash;
Status = Receipt.GetValue().status;
}
}));
}
Next Steps
Gas Sponsorship
Gas sponsorship and transaction costs
Pre-Approved Transactions
Instant transactions without popups (Unity/Unreal)
Immutable Play
Where users manage their wallet
Architecture
Understand the security model
Minting NFTs
Mint NFTs to user wallets
Operator Allowlist
Contract allowlisting and verification