> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Using Smart Wallets

To use smart wallets, you could use the EIP1193 provider or use a backend. This guide teaches how to make different requests to the smart wallet with the backend and Unity (it's easier to do so in the context of gaming because no need to encode transactions with Unity).

## Methods

### SendTransactionSignatureRequest

Sign and submit a transaction that your backend created with `POST /v2/transactions`. The backend response has `status: "awaiting_signature"` and a `nextAction.hash`; the SDK signs that hash with the embedded signer and broadcasts the transaction.

**Method Signature:**

```csharp
public async UniTask<TransactionResponse> SendTransactionSignatureRequest(TransactionSignatureRequest request)
```

**Parameters:**

* `TransactionSignatureRequest request` - Transaction ID plus the `nextAction.hash` to sign

**Returns:**

* `UniTask<TransactionResponse>` - The transaction, with `Status` and `Receipt.TransactionHash` once mined

**TransactionSignatureRequest Structure:**

```csharp
public class TransactionSignatureRequest
{
    public string transactionId;  // tin_...
    public string hash;           // nextAction.hash - SDK signs it if signature is null
    public string signature;      // Optional - ready-made signature (e.g. from a session key)
    public bool optimistic;       // Optional - resolve at broadcast instead of waiting for the receipt

    public TransactionSignatureRequest(
        string transactionId,
        string hash = null,
        string signature = null,
        bool optimistic = false)
    {
        this.transactionId = transactionId;
        this.hash = hash;
        this.signature = signature;
        this.optimistic = optimistic;
    }
}
```

**Example:**

```csharp
using System;
using UnityEngine;
using UnityEngine.Networking;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using Openfort.OpenfortSDK;
using Openfort.OpenfortSDK.Model;

public class SmartWalletTransactions : MonoBehaviour
{
    private OpenfortSDK openfort;
    private string backendUrl = "https://your-backend.com/api";
    private string authToken; // Your user's auth token
    
    private async void Start()
    {
        openfort = await OpenfortSDK.Init(
            "YOUR_OPENFORT_PUBLISHABLE_KEY",
            "YOUR_SHIELD_PUBLISHABLE_KEY"
        );
    }
    
    public async UniTask<TransactionResponse> ExecuteTransaction(string transactionType, object parameters = null)
    {
        try
        {
            // Step 1: Create the transaction via backend
            var backendResponse = await CreateTransaction(transactionType, parameters);
            
            if (backendResponse?.NextAction == null)
            {
                Debug.Log("Transaction completed without signature required");
                return backendResponse;
            }
            
            // Step 2: Sign the transaction with embedded wallet
            return await SignAndSendTransaction(backendResponse);
        }
        catch (Exception e)
        {
            Debug.LogError($"Transaction failed: {e.Message}");
            throw;
        }
    }
    
    // The backend forwards the /v2/transactions response, which deserializes into the SDK's TransactionResponse
    private async UniTask<TransactionResponse> CreateTransaction(string transactionType, object parameters)
    {
        var requestData = new
        {
            type = transactionType,
            parameters = parameters
        };
        
        string jsonData = JsonConvert.SerializeObject(requestData);
        
        using var webRequest = new UnityWebRequest($"{backendUrl}/transaction", "POST");
        webRequest.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Authorization", $"Bearer {authToken}");
        webRequest.SetRequestHeader("Content-Type", "application/json");
        
        await webRequest.SendWebRequest();
        
        if (webRequest.result != UnityWebRequest.Result.Success)
        {
            throw new Exception($"Backend request failed: {webRequest.error}");
        }
        
        var responseText = webRequest.downloadHandler.text;
        return JsonConvert.DeserializeObject<TransactionResponse>(responseText);
    }
    
    private async UniTask<TransactionResponse> SignAndSendTransaction(TransactionResponse backendResponse)
    {
        // Ensure wallet is ready
        var state = await openfort.GetEmbeddedState();
        if (state != EmbeddedState.READY)
        {
            throw new InvalidOperationException($"Wallet not ready. State: {state}");
        }
        
        // Create signature request
        var signatureRequest = new TransactionSignatureRequest(
            backendResponse.Id,
            backendResponse.NextAction.Hash,
            null, // SDK will generate signature
            true  // optimistic
        );

        // Sign and send
        var result = await openfort.SendTransactionSignatureRequest(signatureRequest);
        
        Debug.Log($"Transaction {result.Id} status: {result.Status}");
        if (result.Receipt?.TransactionHash != null)
        {
            Debug.Log($"Transaction hash: {result.Receipt.TransactionHash}");
        }
        
        return result;
    }
}
```

## Transaction Types

### Contract Interaction

### Contract Function Calls

Execute smart contract functions like minting, transferring tokens, or calling custom contract methods.

**Backend Implementation Example:**

```javascript
// Node.js backend example
app.post('/api/transaction', async (req, res) => {
  const { type, parameters } = req.body;
  
  if (type === 'simple') {
    const transaction = await openfort.transactions.create({
      accountId: req.user.accountId,
      feeSponsorshipId: "pol_...", // Your gas sponsorship ID
      chainId: 80002,              // Polygon Amoy
      calls: [{
        contractId: parameters.contractId,
        functionName: parameters.functionName,
        functionArgs: parameters.functionArgs
      }]
    });
    
    res.json(transaction);
  }
});
```

**Unity Usage:**

```csharp
// Mint a Token
var mintParams = new {
  contractId = "con_...",
  functionName = "mint",
  functionArgs = new[] { playerAddress, tokenId }
};

await ExecuteTransaction("simple", mintParams);
```

### Native Token Transfer

### Send Native Tokens

Transfer native blockchain tokens to other addresses or accounts.

**Backend Implementation Example:**

```javascript
// Node.js backend example
app.post('/api/transaction', async (req, res) => {
  const { type, parameters } = req.body;
  
  if (type === 'native') {
    const transaction = await openfort.transactions.create({
      accountId: req.user.accountId,
      feeSponsorshipId: "pol_...", // gas sponsorship must be linked to a policy with account_functions rule
      chainId: 80002,
      calls: [{
        to: parameters.recipient,     // Address or account ID
        value: parameters.amount      // In wei (string)
      }]
    });
    
    res.json(transaction);
  }
});
```

**Unity Usage:**

```csharp
// Send 0.1 POL (in wei)
var transferParams = new {
  recipient = "0x742d35Cc6Dd62c4532aD096a421c2c80b0B8aAdA",
  amount = "100000000000000000" // 0.1 POL in wei
};

await ExecuteTransaction("native", transferParams);
```

:::tip
Wei calculator: use the [Etherscan unit converter](https://etherscan.io/unitconverter) to convert between ETH/POL and wei.
:::

### Batch Transactions

### Execute Multiple Operations

Combine multiple contract calls or transfers into a single atomic transaction. If any operation fails, the entire batch reverts.

**Benefits:**

* Users only wait for one transaction
* Reduced gas costs
* Atomic execution (all or nothing)

**Limitations:**

* Maximum 9 calls per transaction
* All calls must succeed for the batch to complete

**Backend Implementation Example:**

```javascript
// Node.js backend example
app.post('/api/transaction', async (req, res) => {
  const { type, parameters } = req.body;
  
  if (type === 'batch') {
    const transaction = await openfort.transactions.create({
      accountId: req.user.accountId,
      feeSponsorshipId: "pol_...",
      chainId: 80002,
      calls: [
        {
          // First: Approve token spending
          contractId: parameters.tokenContract,
          functionName: "approve",
          functionArgs: [parameters.spenderAddress, parameters.approveAmount]
        },
        {
          // Second: Execute the main function
          contractId: parameters.mainContract,
          functionName: parameters.mainFunction,
          functionArgs: parameters.mainArgs
        }
      ]
    });
    
    res.json(transaction);
  }
});
```

**Unity Usage:**

```csharp
// Approve and stake tokens in one transaction
var batchParams = new {
  tokenContract = "con_token123",
  spenderAddress = "0x...", 
  approveAmount = "1000000000000000000", // 1 token
  mainContract = "con_staking456",
  mainFunction = "stake",
  mainArgs = new[] { "1000000000000000000" }
};

await ExecuteTransaction("batch", batchParams);
```

## Complete Example

Here's a comprehensive example that handles different transaction types:

```csharp
using System;
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using Openfort.OpenfortSDK;
using Openfort.OpenfortSDK.Model;

public class SmartWalletDemo : MonoBehaviour
{
    [Header("UI References")]
    [SerializeField] private Button mintButton;
    [SerializeField] private Button transferButton;
    [SerializeField] private Button batchButton;
    [SerializeField] private TMPro.TextMeshProUGUI statusText;
    
    private SmartWalletTransactions transactionManager;
    
    private async void Start()
    {
        transactionManager = GetComponent<SmartWalletTransactions>();
        
        mintButton.onClick.AddListener(() => MintToken().Forget());
        transferButton.onClick.AddListener(() => TransferTokens().Forget());
        batchButton.onClick.AddListener(() => BatchTransaction().Forget());
    }
    
    private async UniTaskVoid MintToken()
    {
        statusText.text = "Minting Token...";
        
        try
        {
            var mintParams = new {
                contractId = "con_token_contract",
                functionName = "mint",
                functionArgs = new[] { "player_address" }
            };
            
            var result = await transactionManager.ExecuteTransaction("simple", mintParams);
            statusText.text = $"Token minted! TX: {result.Receipt?.TransactionHash}";
        }
        catch (Exception e)
        {
            statusText.text = $"Mint failed: {e.Message}";
        }
    }
    
    private async UniTaskVoid TransferTokens()
    {
        statusText.text = "Transferring tokens...";
        
        try
        {
            var transferParams = new {
                recipient = "0x742d35Cc6Dd62c4532aD096a421c2c80b0B8aAdA",
                amount = "50000000000000000" // 0.05 POL
            };
            
            var result = await transactionManager.ExecuteTransaction("native", transferParams);
            statusText.text = $"Transfer complete! TX: {result.Receipt?.TransactionHash}";
        }
        catch (Exception e)
        {
            statusText.text = $"Transfer failed: {e.Message}";
        }
    }
    
    private async UniTaskVoid BatchTransaction()
    {
        statusText.text = "Executing batch transaction...";
        
        try
        {
            var batchParams = new {
                tokenContract = "con_token",
                spenderAddress = "0x...",
                approveAmount = "1000",
                mainContract = "con_dex",
                mainFunction = "swap",
                mainArgs = new[] { "1000" }
            };
            
            var result = await transactionManager.ExecuteTransaction("batch", batchParams);
            statusText.text = $"Batch complete! TX: {result.Receipt?.TransactionHash}";
        }
        catch (Exception e)
        {
            statusText.text = $"Batch failed: {e.Message}";
        }
    }
}
```

## Examples

* [Unity Sample Android](https://github.com/openfort-xyz/sample-unity-android-embedded-signer) — GitHub repository. An integration with Google Play Games using Firebase Auth as a third party auth provider to create a non-custodial embedded wallet.
* [Unity Sample WebGL](https://github.com/openfort-xyz/sample-unity-webgl-embedded-signer) — GitHub repository. An integration with Openfort Auth with non-custodial embedded wallet.
