# 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

### SendSignatureTransactionIntentRequest

Sign and send a transaction intent that was created by your backend.

**Method Signature:**

```csharp
public async UniTask<TransactionIntentResponse> SendSignatureTransactionIntentRequest(SignatureTransactionIntentRequest request)
```

**Parameters:**

* `SignatureTransactionIntentRequest request` - Transaction intent request with ID and user operation hash

**Returns:**

* `UniTask<TransactionIntentResponse>` - Transaction response with transaction hash

**SignatureTransactionIntentRequest Structure:**

```csharp
public class SignatureTransactionIntentRequest
{
    public string transactionIntentId;
    public string userOperationHash;  // Optional
    public string signature;          // Optional - SDK generates if null
    public bool optimistic;           // Optional - default false

    public SignatureTransactionIntentRequest(
        string transactionIntentId,
        string userOperationHash = null,
        string signature = null,
        bool optimistic = false)
    {
        this.transactionIntentId = transactionIntentId;
        this.userOperationHash = userOperationHash;
        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<TransactionIntentResponse> ExecuteTransaction(string transactionType, object parameters = null)
    {
        try
        {
            // Step 1: Create transaction intent via backend
            var backendResponse = await CreateTransactionIntent(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;
        }
    }
    
    private async UniTask<TransactionIntentResponse> CreateTransactionIntent(string transactionType, object parameters)
    {
        var requestData = new
        {
            type = transactionType,
            parameters = parameters
        };
        
        string jsonData = JsonConvert.SerializeObject(requestData);
        
        using var webRequest = UnityWebRequest.Post($"{backendUrl}/transaction-intent", jsonData);
        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<TransactionIntentResponse>(responseText);
    }
    
    private async UniTask<TransactionIntentResponse> SignAndSendTransaction(TransactionIntentResponse 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 SignatureTransactionIntentRequest(
            backendResponse.Id,
            backendResponse.NextAction.Payload.UserOpHash,
            null, // SDK will generate signature
            true  // optimistic
        );

        // Sign and send
        var result = await openfort.SendSignatureTransactionIntentRequest(signatureRequest);
        
        Debug.Log($"Transaction executed successfully: {result.Id}");
        if (result.Response?.TransactionHash != null)
        {
            Debug.Log($"Transaction hash: {result.Response.TransactionHash}");
        }
        
        return result;
    }
}

// Response models
[Serializable]
public class TransactionIntentResponse
{
    public string Id { get; set; }
    public NextAction NextAction { get; set; }
    public TransactionResponse Response { get; set; }
}

[Serializable]
public class NextAction
{
    public string Type { get; set; }
    public NextActionPayload Payload { get; set; }
}

[Serializable]
public class NextActionPayload
{
    public string UserOpHash { get; set; }
}

[Serializable]
public class TransactionResponse
{
    public string TransactionHash { get; set; }
}
```

## Transaction Types

<MultiOptionDisplay
  options={[
{ id: 'simple', label: 'Contract Interaction' },
{ id: 'native', label: 'Native Token Transfer' },
{ id: 'batch', label: 'Batch Transactions' },
]}
/>

<span id="simple" className="hidden [&>*]:mb-6!">
  ### 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-intent', async (req, res) => {
    const { type, parameters } = req.body;
    
    if (type === 'simple') {
      const transactionIntent = await openfort.transactionIntents.create({
        account: req.user.accountId,
        policy: "pol_...", // Your gas sponsorship ID
        chainId: 80002,    // Polygon Amoy
        interactions: [{
          contract: parameters.contractId,
          functionName: parameters.functionName,
          functionArgs: parameters.functionArgs
        }]
      });
      
      res.json(transactionIntent);
    }
  });
  ```

  **Unity Usage:**

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

  await ExecuteTransaction("simple", mintParams);
  ```
</span>

<span id="native" className="hidden [&>*]:mb-6!">
  ### Send Native Tokens

  Transfer native blockchain tokens to other addresses or accounts.

  **Backend Implementation Example:**

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

  **Unity Usage:**

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

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

  :::tip
  Wei Calculator: Use [eth-converter.com](https://eth-converter.com/) to convert between ETH/MATIC and wei.
  :::
</span>

<span id="batch" className="hidden [&>*]:mb-6!">
  ### 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 interactions per transaction intent
  * All interactions must succeed for the batch to complete

  **Backend Implementation Example:**

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

  **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);
  ```
</span>

## 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.Response?.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 MATIC
            };
            
            var result = await transactionManager.ExecuteTransaction("native", transferParams);
            statusText.text = $"Transfer complete! TX: {result.Response?.TransactionHash}";
        }
        catch (Exception e)
        {
            statusText.text = $"Transfer failed: {e.Message}";
        }
    }
    
    private async UniTaskVoid BatchTransaction()
    {
        statusText.text = "Executing batch transaction...";
        
        try
        {
            var batchParams = new {
                operations = new[] {
                    new { contract = "con_token", function = "approve", args = new[] { "spender", "1000" } },
                    new { contract = "con_dex", function = "swap", args = new[] { "1000" } }
                }
            };
            
            var result = await transactionManager.ExecuteTransaction("batch", batchParams);
            statusText.text = $"Batch complete! TX: {result.Response?.TransactionHash}";
        }
        catch (Exception e)
        {
            statusText.text = $"Batch failed: {e.Message}";
        }
    }
}
```

## Examples

<HoverCardLayout>
  <HoverCardLink description="An integration with Google Play Games using Firebase Auth as a third party auth provider to create a non-custodial embedded wallet" href="https://github.com/openfort-xyz/sample-unity-firebaseauth-embedded-signer" title="Unity Sample Android" subtitle="GitHub repository" icon={Smartphone} color="#10B981" />

  <HoverCardLink description="An integration with Openfort Auth with non-custodial embedded wallet" href="https://github.com/openfort-xyz/sample-unity-webgl-embedded-signer" title="Unity Sample WebGL" subtitle="GitHub repository" icon={Globe} color="#8B5CF6" />
</HoverCardLayout>
