# Verifying Account Abstracted Instant Actions (ERC-4337)

When using Account Abstraction with ERC-4337, you may need to verify a user’s signature on-chain (EIP-1271). This process allows smart contract wallets to sign off-chain data (via the user’s wallet logic) and then be validated on-chain by calling a contract’s isValidSignature() function.

The example below uses:

* SIWE (Sign-In with Ethereum) to structure the message (nonce, domain, expiration, etc.).
* ERC-4337 style message hashing to produce the correct pre-hashed data.

EIP-1271 on-chain validation via a contract call to `isValidSignature()`.

## Example Code Snippet

```python
import json

from eth_typing import HexStr
from eth_utils import keccak, to_bytes, to_hex
from siwe import SiweMessage
from siwe import siwe
from web3 import Web3

RPC = ''
web3 = Web3(Web3.HTTPProvider(RPC))


def _encode_erc4337(message: SiweMessage) -> HexStr:
    data_str = message.prepare_message()
    print(data_str)
    hex_message = data_str.encode('utf-8').hex()
    presign_message_prefix = f'\x19Ethereum Signed Message:\n{len(data_str)}'
    prefix = presign_message_prefix.encode('utf-8').hex()
    combined_message = f'0x{prefix}{hex_message}'
    return to_hex(keccak(to_bytes(hexstr=combined_message)))


message_dict = {
    "account_address": "0x94b1e92397bC7Bc0964e1d6C736277AF27E5a76a",
    "expiration_time": "2024-12-19T08:09:21.231Z",
    "issued_at": "2024-12-16T08:09:21.231Z",
    "signature": "0xf8582d00465b648c7d3f0f06d1ca41553af52bf70e1f21b115c7a18a0f548dd032000319a7947d17575ffba4a358a9e5be4f9302216a41e22ba1f4aa7f5108ac20",
    "nonce": "1ESHS1N2VcQ"
}

domain = "vibe-ui-git-hedger-whitelist-link-vibe-tradings-projects.vercel.app"
uri = "https://base-hedger82.rasa.capital/login"
signature = message_dict['signature']
owner = "0x3Da94BF626065EF31dab46529ea0A69761a01a1e"

fields = {
    "domain": domain,
    "address": owner,
    "uri": uri,
    "version": siwe.VersionEnum.one,
    "chain_id": "8453",
    "nonce": message_dict["nonce"],
    "issued_at": message_dict["issued_at"],
    "statement": f'msg: {message_dict["account_address"]}',
    "expiration_time": message_dict["expiration_time"]
}

siwe_message = SiweMessage(**fields)
hash_message = _encode_erc4337(siwe_message)

with open('SignerValidatorAbi.json') as f:
    abi = json.load(f)

contract = web3.eth.contract(address=owner, abi=abi)

try:
    magic_value = contract.functions.isValidSignature(hash_message, signature).call()
    print("Magic Value Returned:", magic_value.hex())
    print(magic_value == Web3.to_bytes(hexstr=HexStr('0x1626ba7e')))
except Exception as e:
    print("Error calling isValidSignature:", e)

```

Below is a breakdown of how this code example verifies Instant Trades in an AA context.

## Constructing the SIWE Message

```python
fields = {
    "domain": domain,
    "address": owner,
    "uri": uri,
    "version": siwe.VersionEnum.one,
    "chain_id": "8453",
    "nonce": message_dict["nonce"],
    "issued_at": message_dict["issued_at"],
    "statement": f'msg: {message_dict["account_address"]}',
    "expiration_time": message_dict["expiration_time"]
}
siwe_message = SiweMessage(**fields)
```

* SIWE (Sign-In with Ethereum) is used to define an off-chain message containing:
  * `domain`: The site or application domain.
  * `address`: The user’s (or smart wallets) address—this is an EIP-1271 contract in this example.
  * `nonce` & `issued_at`: Used to prevent replay attacks.
  * `expiration_time`: Defines how long this signed authorization is valid.
  * `statement`: here `account_address` is the ERC-4337 compliant wallet
  * `chain_id`: The chain on which the address is deployed.

## Encoding the Message for EIP-1271 Verification

```python
def _encode_erc4337(message: SiweMessage) -> HexStr:
    data_str = message.prepare_message()
    print(data_str)
    hex_message = data_str.encode('utf-8').hex()
    presign_message_prefix = f'\x19Ethereum Signed Message:\n{len(data_str)}'
    prefix = presign_message_prefix.encode('utf-8').hex()
    combined_message = f'0x{prefix}{hex_message}'
    return to_hex(keccak(to_bytes(hexstr=combined_message)))
```

1. `message.prepare_message()` gives you the SIWE message as a string.
2. The code applies EIP-191 style prefix: `\x19Ethereum Signed Message:\n<length>`. This is the same prefix typically used in MetaMask or other wallets for “personal\_sign.”
3. The prefixed message is then hashed with `keccak-256`.

The resulting `hash_message` becomes the “message hash” that the smart contract will verify.

## On-Chain Signature Verification

```python
contract = web3.eth.contract(address=owner, abi=abi)
magic_value = contract.functions.isValidSignature(hash_message, signature).call()
print("Magic Value Returned:", magic_value.hex())
print(magic_value == Web3.to_bytes(hexstr=HexStr('0x1626ba7e')))
```

1. `owner` is not an EOA– it’s an EIP-1271-compatible contract (smart wallet).
2. `isValidSignature(bytes32 _hash, bytes _signature)` is part of EIP-1271. The contract checks internally if the provided signature is valid for \_hash.
3. A valid signature must return `0x1626ba7e` (the EIP-1271 magic value).
4. If the returned value is not `0x1626ba7e`, the signature is invalid.

## Account Abstracted Flow– Verifying Instant Actions:

1. The frontend generates a SIWE message for "instant actions"
   * E.g., “I authorize solver XYZ to execute opens/closes until expiration\_time.”
   * The user (or user’s wallet) signs it off-chain, producing a signature.
2. The solver/backend calls `_encode_erc4337(siwe_message)` to construct the same SIWE string and get the `hash_message`.
3. The solver then invokes `isValidSignature(hash_message, signature)` on the user’s smart wallet contract:

* Success (`0x1626ba7e`) means the user’s contract acknowledges the signature, so the solver can proceed with “instant actions.”
* Failure (any other value or revert) means the signature is invalid or expired.

This ensures no new signature is required for every single open/close, provided the user already authorized a “session.”


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.symm.io/liquidity-provider-documentation/building-a-solver-on-symmio/instant-trading/verifying-account-abstracted-instant-actions-erc-4337.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
