Skip to main content
POST
/
v1
/
delegation
/
evm
/
post-approval
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"
  }'
{
  "success": true
}

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.
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.

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

x-client-key
string
required
Your public client key for API authentication
Authorization
string
required
Bearer token format: Bearer {access_token}
Content-Type
string
required
Must be application/json

Query Parameters

region
string
Region identifier for environment routing. Use us for US-specific Linea routing.Example: ?region=usNote: When region=us is set or x-us-env: true header is used, Linea transactions are automatically routed to linea-us environment.

Request Body

address
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: 0x3a11a86cf218c448be519728cd3ac5c741fb3424This address must match the one that signed the approval transaction.
network
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)
currency
string
required
Stablecoin token that was approved for delegation.Allowed values:
  • usdc - USD Coin
  • usdt - Tether USD
amount
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"
txHash
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: 0xb92de09d893e8162b0861c0f7321f68df02212efbc58f208839ae3f176d89638The API verifies this transaction exists on-chain and is confirmed.
sigHash
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
sigMessage
string
required
The SIWE (Sign-In with Ethereum) formatted message that was signed. Must follow the EIP-4361 standard format.Minimum length: 20 charactersRequired 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}
token
string
required
Single-use delegation token obtained from GET /v1/delegation/token (Step 1). Must be a valid UUID.Format: UUID v4Example: 100a99cf-f4d3-4fa1-9be9-2e9828b20ebcNote: Token expires after ~10 minutes or after successful use.

Response

success
boolean
required
Indicates whether the wallet delegation was completed successfully.
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"
  }'
{
  "success": true
}

Response Codes

CodeDescription
201Wallet delegation completed successfully
400Bad request - invalid data format or verification failed
401Authentication failed - invalid or expired access token
422Validation error - invalid request parameters
498Invalid client key
499Missing client key
500Internal 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:
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

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);

Complete Implementation Example

Here’s a full end-to-end example of the EVM delegation flow:
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()}`;
}

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
When redelegating, the user must complete a new blockchain approval transaction with the updated parameters before calling this endpoint.

Verification After Delegation

After successful delegation, verify the wallet was registered:
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'

Common Error Scenarios

Invalid Transaction Hash

{
  "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

{
  "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

{
  "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

{
  "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

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)
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
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
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

Further Reading