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