Fisher Bridge Signature Structure
To authorize cross-chain treasury operations through the Fisher Bridge system, users must generate a cryptographic signature compliant with the EIP-191 standard using the Ethereum Signed Message format.
The signature verification process uses the SignatureRecover
library which implements the standard Ethereum message signing protocol. The signature authorizes fisher executors to process cross-chain deposits and withdrawals on behalf of users, enabling gasless transactions and seamless multi-chain asset management.
Signed Message Format
The signature verification uses the SignatureRecover.signatureVerification
function with the following structure:
SignatureRecover.signatureVerification(
Strings.toString(evvmID), // EVVM ID as string
"fisherBridge", // Action type
string.concat( // Concatenated parameters
AdvancedStrings.addressToString(addressToReceive),
",",
Strings.toString(nonce),
",",
AdvancedStrings.addressToString(tokenAddress),
",",
Strings.toString(priorityFee),
",",
Strings.toString(amount)
),
signature,
signer
);
Internal Message Construction
Internally, the SignatureRecover.signatureVerification
function constructs the final message by concatenating:
string.concat(evvmID, ",", functionName, ",", inputs)
This results in a message format:
"{evvmID},fisherBridge,{addressToReceive},{nonce},{tokenAddress},{priorityFee},{amount}"
EIP-191 Message Hashing
The message is then hashed according to EIP-191 standard:
bytes32 messageHash = keccak256(
abi.encodePacked(
"\x19Ethereum Signed Message:\n",
Strings.toString(bytes(message).length),
message
)
);
Message Components
The signature verification takes three main parameters:
1. EVVM ID (String):
- The result of
Strings.toString(evvmID)
- Purpose: Identifies the specific EVVM instance
2. Action Type (String):
- Fixed value:
"fisherBridge"
- Purpose: Identifies this as a Fisher Bridge cross-chain operation
3. Concatenated Parameters (String): The parameters are concatenated with comma separators:
3.1. Recipient Address (String):
- The result of
AdvancedStrings.addressToString(addressToReceive)
- Purpose: Specifies the address that will receive the assets or EVVM balance credit
- Note: This can be different from the signer address, allowing flexible recipient designation
3.2. Nonce (String):
- The result of
Strings.toString(nonce)
- Purpose: Provides replay protection for the transaction
- Source: Current value from
nextFisherExecutionNonce[signer]
mapping
3.3. Token Address (String):
- The result of
AdvancedStrings.addressToString(tokenAddress)
- Purpose: Identifies the token being transferred
- Special Case:
address(0)
represents native blockchain coins (ETH, MATIC, BNB, etc.)
3.4. Priority Fee (String):
- The result of
Strings.toString(priorityFee)
- Purpose: Specifies the fee paid to the fisher executor for processing the transaction
- Note: Can be
0
if no priority fee is offered
3.5. Amount (String):
- The result of
Strings.toString(amount)
- Purpose: Specifies the quantity of tokens/coins to be transferred
Usage Scenarios
External Chain to EVVM Deposit
Users on external chains sign messages to authorize fisher executors to deposit their assets into EVVM.
EVVM to External Chain Withdrawal
Users sign messages to authorize fisher executors to withdraw assets from EVVM to external chains.
Example Scenarios
Example 1: USDC Deposit from Ethereum to EVVM
Scenario: User wants to deposit 100 USDC from Ethereum to EVVM with 1 USDC priority fee
Parameters:
evvmID
:1
(EVVM instance ID)addressToReceive
:0x742d35Cc6634C0532925a3b8D43C1C16bE8c9123
(recipient in EVVM)nonce
:5
(current fisher execution nonce for user)tokenAddress
:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
(USDC contract)priorityFee
:1000000
(1 USDC with 6 decimals)amount
:100000000
(100 USDC with 6 decimals)
Signature verification call:
SignatureRecover.signatureVerification(
"1", // evvmID as string
"fisherBridge", // action type
"0x742d35cc6634c0532925a3b8d43c1c16be8c9123,5,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,1000000,100000000",
signature,
signer
);
Final message to be signed (after internal concatenation):
1,fisherBridge,0x742d35cc6634c0532925a3b8d43c1c16be8c9123,5,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,1000000,100000000
EIP-191 formatted message hash:
keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n119",
"1,fisherBridge,0x742d35cc6634c0532925a3b8d43c1c16be8c9123,5,0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48,1000000,100000000"
))
Concatenated parameters breakdown:
0x742d35cc6634c0532925a3b8d43c1c16be8c9123
- Recipient address (lowercase with 0x prefix)5
- Current nonce0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
- USDC token address (lowercase with 0x prefix)1000000
- Priority fee (1 USDC)100000000
- Amount (100 USDC)
Example 2: ETH Withdrawal from EVVM to Ethereum
Scenario: User wants to withdraw 0.5 ETH from EVVM to Ethereum with 0.01 ETH priority fee
Parameters:
evvmID
:1
(EVVM instance ID)addressToReceive
:0x9876543210987654321098765432109876543210
(recipient on Ethereum)nonce
:12
(current fisher execution nonce for user)tokenAddress
:0x0000000000000000000000000000000000000000
(native ETH)priorityFee
:10000000000000000
(0.01 ETH in wei)amount
:500000000000000000
(0.5 ETH in wei)
Signature verification call:
SignatureRecover.signatureVerification(
"1", // evvmID as string
"fisherBridge", // action type
"0x9876543210987654321098765432109876543210,12,0x0000000000000000000000000000000000000000,10000000000000000,500000000000000000",
signature,
signer
);
Final message to be signed (after internal concatenation):
1,fisherBridge,0x9876543210987654321098765432109876543210,12,0x0000000000000000000000000000000000000000,10000000000000000,500000000000000000
EIP-191 formatted message hash:
keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n129",
"1,fisherBridge,0x9876543210987654321098765432109876543210,12,0x0000000000000000000000000000000000000000,10000000000000000,500000000000000000"
))
Concatenated parameters breakdown:
0x9876543210987654321098765432109876543210
- Recipient address (lowercase with 0x prefix)12
- Current nonce0x0000000000000000000000000000000000000000
- Native coin representation (address(0))10000000000000000
- Priority fee (0.01 ETH)500000000000000000
- Amount (0.5 ETH)
Security Considerations
Nonce Management
- Sequential Processing: Nonces must be used in sequential order
- Cross-Chain Synchronization: Both host and external chain stations track the same nonce sequence
- Replay Protection: Each nonce can only be used once per user
Signature Binding
- Parameter Integrity: All transaction parameters are included in the signed message
- Address Verification: Signer address is cryptographically verified during signature validation
- Message Format: Exact message format must be maintained for successful verification
Priority Fee Security
- Optional Fee: Priority fee can be set to
0
if no incentive is offered - Fisher Incentive: Higher priority fees may result in faster processing
- Fee Distribution: Priority fees are typically credited to fisher executor's balance
Implementation Notes
Address Formatting
- Uses
AdvancedStrings.addressToString()
for consistent address representation - Addresses are converted to lowercase hex strings without
0x
prefix - Ensures compatibility across different blockchain environments
Nonce Tracking
- Each user has an independent nonce sequence tracked in
nextFisherExecutionNonce
mapping - Nonces increment after each successful fisher bridge operation
- Both host and external chain stations must maintain synchronized nonce values
Cross-Chain Coordination
- Same signature format used on both host and external chains
- Enables verification on both sides of the cross-chain transaction
- Supports multiple interoperability protocols (Hyperlane, LayerZero, Axelar)
Verification Function
The signature verification is performed using the SignatureUtils.verifyMessageSignedForFisherBridge()
function:
function verifyMessageSignedForFisherBridge(
uint256 evvmID,
address signer,
address addressToReceive,
uint256 nonce,
address tokenAddress,
uint256 priorityFee,
uint256 amount,
bytes memory signature
) internal pure returns (bool)
This function uses the SignatureRecover.signatureVerification()
to reconstruct the message string using the provided parameters and verifies that the signature was created by the specified signer address using EIP-191 standard verification.
Signature Implementation Details
The SignatureRecover
library performs signature verification in the following steps:
- Message Construction: Concatenates
evvmID
,functionName
, andinputs
with commas - EIP-191 Formatting: Prepends
"\x19Ethereum Signed Message:\n"
+ message length - Hashing: Applies
keccak256
to the formatted message - Signature Parsing: Splits the 65-byte signature into
r
,s
, andv
components - Recovery: Uses
ecrecover
to recover the signer's address - Verification: Compares recovered address with expected signer
Signature Format Requirements
- Length: Exactly 65 bytes
- Structure:
[r (32 bytes)][s (32 bytes)][v (1 byte)]
- V Value: Must be 27 or 28 (automatically adjusted if < 27)
- Message Format: The final message follows the pattern
"{evvmID},{functionName},{parameters}"
- EIP-191 Compliance: Uses
"\x19Ethereum Signed Message:\n"
prefix with message length - Hash Function:
keccak256
is used for the final message hash before signing - Signature Recovery: Uses
ecrecover
to verify the signature against the expected signer - Address Format:
AdvancedStrings.addressToString
converts addresses to lowercase hex with "0x" prefix - Cross-Chain Compatibility: Same signature format used on both host and external chains
- Fisher Incentives: Higher priority fees may result in faster processing by fisher executors
- Flexible Recipients:
addressToReceive
can differ from signer for flexible asset management - Native Token Support:
address(0)
represents native blockchain coins (ETH, MATIC, BNB, etc.) - Nonce Management: Sequential nonce processing prevents replay attacks across chains
- EVVM ID: Identifies the specific EVVM instance for signature verification
All Fisher Bridge signatures follow the EIP-191 "Signed Data Standard" ensuring compatibility with standard Ethereum wallets and signing tools. The message is prefixed with "\x19Ethereum Signed Message:\n"
during the signing process.
The message format must be followed exactly for signature verification to succeed. Any deviation in parameter ordering, formatting, or separators will cause verification failures and transaction rejection.