Wallet Signatures

Which operations need a wallet signature, who signs, and how each wallet type encodes it.

Before You Start

Read the following guides before proceeding:

GuideWhy
AuthenticationThe corporation token whose employee_address signs
PermissionsPermissions are checked before any signature

Overview

Some corporate operations require the employee to sign a sentence with their wallet. Each operation's page
gives its sentence and its errors. This page covers what they share: who signs and how each wallet type
encodes the signature.

OperationSignatureSentence and rules
Read PAN and expiry, CVV or PINAction confirmation, alwaysAction Confirmation Signatures
Card transferTransaction authorization, in Signature modeCard Transfer
Corridor transferTransaction authorization, in Signature modeCorridor Transfers
Bank transferTransaction authorization, in Signature modeBank Transfers
Create a recipientTransaction authorization, optionalCreating a Recipient

Transaction authorization — the mode, the transaction_authorization object, nonce and reuse rules, and
its errors — is described once in Transaction Authorization.

Estimates, confirmations, reads, card management and bulk transfers take no signature. A bulk transfer is
signed on-chain.


Who Signs

The signer is always the wallet in the corporation token's employee_address claim — the X-User-Wallet
sent to POST /api/v1/corporations/login.

  • An employee signs with their own wallet. The corporation owner's signature does not authorize an
    employee's request, even over the same sentence.
  • A smart account is a different signer from the EOA that controls it. If the employee logged in with
    an EOA, the EOA signs. ERC-1271 applies only when employee_address itself is the contract.

Encoding by Wallet Type

Wallet behind employee_addressSignatureEncoding
EVM EOAEIP-191 personal_sign over the sentence. v as 27/28 or 0/10x hex, 65 bytes
EVM smart-contract walletAny bytes the wallet's ERC-1271 isValidSignature(hash, signature) accepts, where hash is the EIP-191 hash of the sentence0x hex, passed to the contract exactly as sent
StellarEd25519 over the SHA-256 of the sentenceStandard base64
XRPLSignature over the sentence. Transaction authorization onlyHex, with the signer's public_key

The sentence is signed character for character. Any difference — case, spacing, a different id or nonce —
produces a signature that is rejected.

Smart-Contract Wallets

  • The contract must be deployed on the chain in the corporation token's chain_id claim — the
    X-Chain-Id sent at login, or the default chain when none was sent. An undeployed (counterfactual)
    account cannot sign.
  • Wirex calls isValidSignature only when the signature does not recover to employee_address as an EOA
    signature, so an EOA signature never depends on a node call.
  • The call has a 3-second timeout. A timeout, a revert, an unreachable node or any return value other than
    0x1626ba7e rejects the signature.

Signing the Sentence

The examples sign with an EOA key (EIP-191). With an embedded or browser wallet, pass the same sentence to
its personal_sign / signMessage.

import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(employeePrivateKey); // the employee_address wallet
const signature = await account.signMessage({ message: sentence }); // 0x…, 65 bytes
from eth_account import Account
from eth_account.messages import encode_defunct

signature = Account.sign_message(encode_defunct(text=sentence), employee_private_key).signature.hex()
if not signature.startswith("0x"):
    signature = "0x" + signature
sig, _ := crypto.Sign(accounts.TextHash([]byte(sentence)), employeeKey)
sig[crypto.RecoveryIDOffset] += 27
signature := hexutil.Encode(sig)

Did this page help you?