> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baanx.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Complete EVM Wallet Delegation

> Finalize delegation of spending authority from EVM-compatible wallets (Step 3 of 3)

## Overview

This endpoint finalizes the delegation of spending authority from a user's EVM-compatible external wallet (Linea or Ethereum). After the user has completed wallet connection and blockchain approval transaction in your frontend, call this endpoint to register the wallet with the platform.

<Note>
  **When to Use**: Call this endpoint after the user has successfully completed the blockchain approval transaction on an EVM chain (Linea or Ethereum) in your frontend application (Step 2). This is the final step in the delegation workflow.
</Note>

## EVM-Specific Validation

This endpoint performs strict validation for EVM chains:

* **Address Format**: Must be a valid Ethereum address (0x + 40 hex characters)
* **Transaction Hash**: Must be a valid EVM transaction hash (0x + 64 hex characters)
* **Signature**: Must be a valid EVM signature (0x + 130 hex characters)
* **SIWE Format**: Signature message must follow Sign-In with Ethereum (EIP-4361) standard
* **Network**: Must be either `linea` or `ethereum`

## Authentication

<ParamField header="x-client-key" type="string" required>
  Your public client key for API authentication
</ParamField>

<ParamField header="Authorization" type="string" required>
  Bearer token format: `Bearer {access_token}`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`
</ParamField>

## Query Parameters

<ParamField query="region" type="string">
  Region identifier for environment routing. Use `us` for US-specific Linea routing.

  **Example**: `?region=us`

  **Note**: When `region=us` is set or `x-us-env: true` header is used, Linea transactions are automatically routed to linea-us environment.
</ParamField>

## Request Body

<ParamField body="address" type="string" required>
  EVM wallet address that will be delegated. Must be a valid Ethereum address starting with `0x` followed by 40 hexadecimal characters.

  **Pattern**: `^0x[a-fA-F0-9]{40}$`

  **Example**: `0x3a11a86cf218c448be519728cd3ac5c741fb3424`

  This address must match the one that signed the approval transaction.
</ParamField>

<ParamField body="network" type="string" required>
  EVM blockchain network where the approval transaction was executed.

  **Allowed values**:

  * `linea` - Linea mainnet (Chain ID: 59144)
  * `ethereum` - Ethereum mainnet (Chain ID: 1)
</ParamField>

<ParamField body="currency" type="string" required>
  Stablecoin token that was approved for delegation.

  **Allowed values**:

  * `usdc` - USD Coin
  * `usdt` - Tether USD
</ParamField>

<ParamField body="amount" type="string" required>
  Spending limit approved in the delegation. Specified in the currency's base unit (e.g., 5000 for 5000 USDC). This is the maximum amount the platform can spend from the delegated wallet.

  **Pattern**: `^\d+(\.\d+)?$`

  **Example**: `"5000"` or `"5000.50"`
</ParamField>

<ParamField body="txHash" type="string" required>
  EVM transaction hash of the `approve()` call on the token contract. Must start with `0x` followed by 64 hexadecimal characters.

  **Pattern**: `^0x[a-fA-F0-9]{64}$`

  **Example**: `0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638`

  The API verifies this transaction exists on-chain and is confirmed.
</ParamField>

<ParamField body="sigHash" type="string" required>
  EVM signature hash proving wallet ownership. Result of signing `sigMessage` with the wallet's private key. Must start with `0x` followed by 130 hexadecimal characters.

  **Pattern**: `^0x[a-fA-F0-9]{130}$`

  **Example**: `0x2039b9765a4df76e8bae80f3bbc640e8ae6acc81f7a5cc96fe91ccc1844b6f7d4c3e8f1a2b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b`
</ParamField>

<ParamField body="sigMessage" type="string" required>
  The SIWE (Sign-In with Ethereum) formatted message that was signed. Must follow the EIP-4361 standard format.

  **Minimum length**: 20 characters

  **Required SIWE format**:

  ```
  Website x wants you to sign in with your Ethereum account:
  {address}

  Prove address ownership

  URI: {your_domain}
  Version: 1
  Chain ID: {chain_id}
  Nonce: {random_nonce}
  Issued At: {ISO8601_timestamp}
  Expiration Time: {ISO8601_timestamp}
  ```
</ParamField>

<ParamField body="token" type="string" required>
  Single-use delegation token obtained from `GET /v1/delegation/token` (Step 1). Must be a valid UUID.

  **Format**: UUID v4

  **Example**: `100a99cf-f4d3-4fa1-9be9-2e9828b20ebc`

  **Note**: Token expires after \~10 minutes or after successful use.
</ParamField>

## Response

<ResponseField name="success" type="boolean" required>
  Indicates whether the wallet delegation was completed successfully.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://dev.api.baanx.com/v1/delegation/evm/post-approval' \
    --header 'x-client-key: YOUR_PUBLIC_KEY' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
    --header 'Content-Type: application/json' \
    --data '{
      "address": "0x3a11a86cf218c448be519728cd3ac5c741fb3424",
      "network": "linea",
      "currency": "usdc",
      "amount": "5000",
      "txHash": "0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638",
      "sigHash": "0x2039b9765a4df76e8bae80f3bbc640e8ae6acc81f7a5cc96fe91ccc1844b6f7d4c3e8f1a2b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b",
      "sigMessage": "Website x wants you to sign in with your Ethereum account:\n0x3a11a86cf218c448be519728cd3ac5c741fb3424\n\nProve address ownership\n\nURI: websitex.example.com\nVersion: 1\nChain ID: 59141\nNonce: 7I9rPnTwrvzzzfYz2\nIssued At: 2025-05-22T08:14:06.279Z\nExpiration Time: 2025-05-22T08:44:06.276Z",
      "token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebc"
    }'
  ```

  ```python Python theme={null}
  import requests
  from typing import Dict

  def complete_evm_delegation(
      delegation_token: str,
      wallet_address: str,
      network: str,
      currency: str,
      amount: str,
      tx_hash: str,
      sig_hash: str,
      sig_message: str
  ) -> Dict:
      url = "https://dev.api.baanx.com/v1/delegation/evm/post-approval"

      headers = {
          "x-client-key": "YOUR_PUBLIC_KEY",
          "Authorization": "Bearer YOUR_ACCESS_TOKEN",
          "Content-Type": "application/json"
      }

      payload = {
          "address": wallet_address,
          "network": network,
          "currency": currency,
          "amount": amount,
          "txHash": tx_hash,
          "sigHash": sig_hash,
          "sigMessage": sig_message,
          "token": delegation_token
      }

      response = requests.post(url, headers=headers, json=payload)
      return response.json()

  # Usage
  result = complete_evm_delegation(
      delegation_token="100a99cf-f4d3-4fa1-9be9-2e9828b20ebc",
      wallet_address="0x3a11a86cf218c448be519728cd3ac5c741fb3424",
      network="linea",
      currency="usdc",
      amount="5000",
      tx_hash="0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638",
      sig_hash="0x2039b9765...",
      sig_message="Website x wants you to sign in..."
  )
  ```

  ```javascript JavaScript theme={null}
  const completeDelegation = async (delegationData) => {
    const url = 'https://dev.api.baanx.com/v1/delegation/evm/post-approval';

    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'x-client-key': 'YOUR_PUBLIC_KEY',
        'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        address: delegationData.address,
        network: delegationData.network,
        currency: delegationData.currency,
        amount: delegationData.amount,
        txHash: delegationData.txHash,
        sigHash: delegationData.sigHash,
        sigMessage: delegationData.sigMessage,
        token: delegationData.token
      })
    });

    return await response.json();
  };

  // Usage
  const result = await completeDelegation({
    address: '0x3a11a86cf218c448be519728cd3ac5c741fb3424',
    network: 'linea',
    currency: 'usdc',
    amount: '5000',
    txHash: '0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638',
    sigHash: '0x2039b9765...',
    sigMessage: 'Website x wants you to sign in...',
    token: '100a99cf-f4d3-4fa1-9be9-2e9828b20ebc'
  });
  ```

  ```typescript TypeScript theme={null}
  interface EVMDelegationRequest {
    address: string;
    network: 'linea' | 'ethereum';
    currency: 'usdc' | 'usdt';
    amount: string;
    txHash: string;
    sigHash: string;
    sigMessage: string;
    token: string;
  }

  interface DelegationResponse {
    success: boolean;
  }

  async function completeEVMDelegation(
    data: EVMDelegationRequest
  ): Promise<DelegationResponse> {
    const url = 'https://dev.api.baanx.com/v1/delegation/evm/post-approval';

    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'x-client-key': 'YOUR_PUBLIC_KEY',
        'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      throw new Error(`Delegation failed: ${response.statusText}`);
    }

    return await response.json();
  }

  // Usage
  const result = await completeEVMDelegation({
    address: '0x3a11a86cf218c448be519728cd3ac5c741fb3424',
    network: 'linea',
    currency: 'usdc',
    amount: '5000',
    txHash: '0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638',
    sigHash: '0x2039b9765...',
    sigMessage: 'Website x wants you to sign in...',
    token: '100a99cf-f4d3-4fa1-9be9-2e9828b20ebc'
  });
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Success theme={null}
  {
    "success": true
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "message": "Invalid EVM address format"
  }
  ```

  ```json 401 Unauthorized theme={null}
  {
    "message": "Invalid or expired access token"
  }
  ```

  ```json 422 Validation Error theme={null}
  {
    "message": "Validation failed",
    "errors": [
      {
        "field": "txHash",
        "message": "Invalid transaction hash format. Must be 0x followed by 64 hex characters"
      }
    ]
  }
  ```

  ```json 500 Internal Server Error theme={null}
  {
    "message": "An unexpected error occurred. Please try again later."
  }
  ```
</ResponseExample>

## Response Codes

| Code | Description                                              |
| ---- | -------------------------------------------------------- |
| 201  | Wallet delegation completed successfully                 |
| 400  | Bad request - invalid data format or verification failed |
| 401  | Authentication failed - invalid or expired access token  |
| 422  | Validation error - invalid request parameters            |
| 498  | Invalid client key                                       |
| 499  | Missing client key                                       |
| 500  | Internal server error                                    |

## What Happens After Success

When this endpoint returns successfully:

1. **Wallet Registration**: The external wallet is registered and linked to the user's account
2. **Card Payments Enabled**: User can now make card purchases using delegated wallet funds
3. **Wallet Visibility**: Wallet appears in `GET /v1/wallet/external` endpoint response
4. **Priority Assignment**: Wallet is assigned a default priority for transaction processing
5. **Balance Tracking**: Platform begins tracking wallet balance and allowance

## SIWE Message Format

The `sigMessage` must follow the EIP-4361 Sign-In with Ethereum standard:

```text theme={null}
Website x wants you to sign in with your Ethereum account:
0x3a11a86cf218c448be519728cd3ac5c741fb3424

Prove address ownership

URI: websitex.example.com
Version: 1
Chain ID: 59141
Nonce: 7I9rPnTwrvzzzfYz2
Issued At: 2025-05-22T08:14:06.279Z
Expiration Time: 2025-05-22T08:44:06.276Z
```

### SIWE Components

* **Account Address**: Must match the `address` field
* **Statement**: Can be any text explaining the purpose
* **URI**: Your application's domain
* **Version**: Should be `1`
* **Chain ID**: `59144` for Linea, `1` for Ethereum mainnet
* **Nonce**: Random string for replay protection
* **Issued At**: ISO8601 timestamp when message was created
* **Expiration Time**: ISO8601 timestamp when signature expires

## Generating SIWE Messages

<CodeGroup>
  ```javascript ethers.js theme={null}
  import { ethers } from 'ethers';

  function generateSIWEMessage(address, chainId) {
    const domain = window.location.host;
    const now = new Date();
    const expirationTime = new Date(now.getTime() + 30 * 60000); // 30 minutes

    const nonce = ethers.utils.hexlify(ethers.utils.randomBytes(16));

    const message = `Website x wants you to sign in with your Ethereum account:
  ${address}

  Prove address ownership

  URI: ${domain}
  Version: 1
  Chain ID: ${chainId}
  Nonce: ${nonce}
  Issued At: ${now.toISOString()}
  Expiration Time: ${expirationTime.toISOString()}`;

    return message;
  }

  // Usage
  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = await provider.getSigner();
  const address = await signer.getAddress();
  const network = await provider.getNetwork();

  const message = generateSIWEMessage(address, network.chainId);
  const signature = await signer.signMessage(message);
  ```

  ```javascript siwe library theme={null}
  import { SiweMessage } from 'siwe';

  async function createSIWEMessage(address, chainId) {
    const message = new SiweMessage({
      domain: window.location.host,
      address: address,
      statement: 'Prove address ownership',
      uri: window.location.origin,
      version: '1',
      chainId: chainId,
      nonce: generateNonce(), // Your nonce generation function
      issuedAt: new Date().toISOString(),
      expirationTime: new Date(Date.now() + 30 * 60000).toISOString()
    });

    return message.prepareMessage();
  }

  function generateNonce() {
    return Array.from(crypto.getRandomValues(new Uint8Array(16)))
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
  ```
</CodeGroup>

## Complete Implementation Example

Here's a full end-to-end example of the EVM delegation flow:

<CodeGroup>
  ```javascript Complete Flow theme={null}
  import { ethers } from 'ethers';

  async function delegateEVMWallet(delegationToken, amount, network = 'linea') {
    try {
      // Step 1: Connect wallet
      const provider = new ethers.providers.Web3Provider(window.ethereum);
      await provider.send('eth_requestAccounts', []);
      const signer = await provider.getSigner();
      const address = await signer.getAddress();
      const networkInfo = await provider.getNetwork();

      console.log(`Connected to ${address} on chain ${networkInfo.chainId}`);

      // Step 2: Request token approval
      const tokenAddress = getTokenAddress(network, 'usdc');
      const spenderAddress = getPlatformSpenderAddress(network);

      const tokenContract = new ethers.Contract(
        tokenAddress,
        ['function approve(address spender, uint256 amount) returns (bool)'],
        signer
      );

      const amountInWei = ethers.utils.parseUnits(amount, 6); // USDC has 6 decimals
      const tx = await tokenContract.approve(spenderAddress, amountInWei);

      console.log('Approval transaction sent:', tx.hash);
      const receipt = await tx.wait();
      console.log('Approval confirmed in block:', receipt.blockNumber);

      // Step 3: Generate and sign SIWE message
      const siweMessage = generateSIWEMessage(address, networkInfo.chainId);
      const signature = await signer.signMessage(siweMessage);

      console.log('Message signed successfully');

      // Step 4: Submit to API
      const response = await fetch(
        'https://dev.api.baanx.com/v1/delegation/evm/post-approval',
        {
          method: 'POST',
          headers: {
            'x-client-key': 'YOUR_PUBLIC_KEY',
            'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            address: address,
            network: network,
            currency: 'usdc',
            amount: amount,
            txHash: receipt.transactionHash,
            sigHash: signature,
            sigMessage: siweMessage,
            token: delegationToken
          })
        }
      );

      const result = await response.json();

      if (response.ok) {
        console.log('Delegation completed successfully!');
        return { success: true, data: result };
      } else {
        console.error('Delegation failed:', result);
        return { success: false, error: result };
      }

    } catch (error) {
      console.error('Error during delegation:', error);
      return { success: false, error: error.message };
    }
  }

  // Helper functions
  function getTokenAddress(network, currency) {
    const addresses = {
      linea: {
        usdc: '0x176211869cA2b568f2A7D4EE941E073a821EE1ff',
        usdt: '0xA219439258ca9da29E9Cc4cE5596924745e12B93'
      },
      ethereum: {
        usdc: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
        usdt: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
      }
    };
    return addresses[network][currency];
  }

  function getPlatformSpenderAddress(network) {
    // Replace with actual platform spender addresses
    const spenders = {
      linea: '0x...',
      ethereum: '0x...'
    };
    return spenders[network];
  }

  function generateSIWEMessage(address, chainId) {
    const domain = window.location.host;
    const now = new Date();
    const expirationTime = new Date(now.getTime() + 30 * 60000);
    const nonce = ethers.utils.hexlify(ethers.utils.randomBytes(16));

    return `Website x wants you to sign in with your Ethereum account:
  ${address}

  Prove address ownership

  URI: ${domain}
  Version: 1
  Chain ID: ${chainId}
  Nonce: ${nonce}
  Issued At: ${now.toISOString()}
  Expiration Time: ${expirationTime.toISOString()}`;
  }
  ```
</CodeGroup>

## Redelegation

To update an existing delegation (change allowance, upgrade contracts, etc.), simply call this endpoint again with the same wallet address. The new delegation will replace the previous one.

**Common redelegation scenarios:**

* **Increase spending limit**: User wants to approve a higher amount
* **Contract upgrade**: Platform deploys new smart contracts
* **Network migration**: Moving from one blockchain to another

<Warning>
  When redelegating, the user must complete a new blockchain approval transaction with the updated parameters before calling this endpoint.
</Warning>

## Verification After Delegation

After successful delegation, verify the wallet was registered:

<CodeGroup>
  ```bash Verify Wallet theme={null}
  curl --request GET \
    --url 'https://dev.api.baanx.com/v1/wallet/external' \
    --header 'x-client-key: YOUR_PUBLIC_KEY' \
    --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
  ```

  ```json Expected Response theme={null}
  [
    {
      "id": 551,
      "address": "0x3a11a86cf218c448be519728cd3ac5c741fb3424",
      "currency": "usdc",
      "balance": "5000.00",
      "allowance": "5000",
      "network": "linea",
      "priority": 1
    }
  ]
  ```
</CodeGroup>

## Common Error Scenarios

### Invalid Transaction Hash

```json theme={null}
{
  "message": "Transaction not found on blockchain"
}
```

**Causes:**

* Transaction hasn't been confirmed yet
* Wrong network specified
* Invalid transaction hash format

**Solutions:**

* Wait for transaction confirmation (1-2 blocks)
* Verify the network matches where transaction was executed
* Check transaction hash format (0x + 64 hex characters)

### Signature Verification Failed

```json theme={null}
{
  "message": "Signature verification failed"
}
```

**Causes:**

* `sigHash` doesn't match the wallet that signed
* `sigMessage` was modified after signing
* Address in SIWE message doesn't match `address` field

**Solutions:**

* Ensure the same wallet signs both the approval and SIWE message
* Don't modify `sigMessage` after user signs it
* Verify address in SIWE message matches the request address field

### Expired Delegation Token

```json theme={null}
{
  "message": "Delegation token has expired or already been used"
}
```

**Causes:**

* More than 10 minutes passed since token generation
* Token was already used in a previous request

**Solutions:**

* Generate a new delegation token from Step 1
* Complete the flow more quickly
* Don't reuse tokens

### Invalid SIWE Format

```json theme={null}
{
  "message": "Invalid SIWE message format"
}
```

**Causes:**

* Missing required SIWE fields
* Incorrect field formatting
* Wrong chain ID for network

**Solutions:**

* Use a SIWE library to generate messages
* Verify all required fields are present
* Ensure chain ID matches network (59144 for Linea, 1 for Ethereum)

## Best Practices

<Accordion title="Transaction Confirmation">
  **Always wait for transaction confirmation** before calling this endpoint:

  * Wait for at least 1 block confirmation
  * For higher security, wait for 3-5 confirmations
  * Display transaction status to users
  * Implement timeout handling (typically 2-3 minutes max)
</Accordion>

<Accordion title="Error Handling">
  **Implement comprehensive error handling**:

  * Retry failed transactions with higher gas price
  * Handle user rejection gracefully
  * Provide clear error messages to users
  * Log errors for debugging (without exposing sensitive data)
  * Implement exponential backoff for API retries
</Accordion>

<Accordion title="Security">
  **Follow security best practices**:

  * Never store private keys
  * Validate all addresses and hashes before submission
  * Use HTTPS only for all API calls
  * Implement rate limiting on your backend
  * Verify transaction on blockchain before submitting to API
</Accordion>

<Accordion title="User Experience">
  **Create a smooth user experience**:

  * Show progress indicators during each step
  * Display transaction fees before approval
  * Explain what delegation means in simple terms
  * Provide transaction links to block explorers
  * Allow users to easily retry on failure
</Accordion>

## Related Endpoints

* [GET /v1/delegation/token](/api-reference/delegation/token) - Generate delegation token (Step 1)
* [POST /v1/delegation/solana/post-approval](/api-reference/delegation/solana-post-approval) - Complete Solana wallet delegation
* [GET /v1/wallet/external](/api-reference/wallet/external) - List delegated wallets
* [Manage External Wallet Priority](/api-reference/wallet/external-priority) - Get and update wallet priority order

## Further Reading

* [Delegation Overview](/guides/delegation/overview) - Understand delegation concepts
* [Implementation Guide](/guides/delegation/implementation) - Complete integration walkthrough
* [EVM Chains](/guides/delegation/evm-chains) - Detailed EVM-specific implementation
* [Priority Management](/guides/delegation/priority) - Managing multiple wallets
* [Redelegation](/guides/delegation/redelegation) - Updating existing delegations
