# Instant Login (Account Abstracted)

This section explains how and why we the ERC-4337 “account abstraction” style login is used for Instant Actions. It will cover:

• What ERC-4337 and EIP-1271 bring to the table\
• The end-to-end flow for Instant Login (fetching a nonce, building a SIWE message, hashing/signing, local EIP-1271 validation, HTTP call)\
• A complete TypeScript code snippet showing how to call `/login` and obtain an access token

## Why ERC-4337 / Account Abstraction?

Traditional EOAs (Externally Owned Accounts) sign every transaction with their private key. Smart-contract wallets (Gnosis Safe, Argent, etc.) cannot expose a raw private key.\
ERC-4337 let’s a smart-contract wallet “`personal_sign`” arbitrary data off-chain, then verify that signature on-chain via the standard EIP-1271 `isValidSignature(bytes32, bytes)` call.

**Benefits for Instant Actions:**\
• Users grant a single “session” signature once.\
• Solvers can verify authorization on-chain or locally.\
• No need for the user to re-sign every open/close quote.

## High-Level Login Flow

1. Fetch a nonce from `/nonce/{subAccount}`
2. Construct a SIWE message (domain, wallet address, statement, nonce, issuedAt, expiration)
3. Apply EIP-191 prefix + keccak256 → “ERC-4337 hash”
4. Ask the Safe to personal\_sign the raw SIWE string (SigningMethod.ETH\_SIGN)
5. (Optional) Locally verify the signature via `protocolKit.isValidSignature(erc4337Hash, sig)`
6. POST to `/login` with\
   • `account_address`\
   • `issued_at`\
   • `expiration_time`\
   • `nonce`\
   • `signature` (packed Safe signatures)\
   • `sign_type: “ERC4337”`

{% hint style="info" %}
The `sign_type` field is essential to obtain an access token with Rasa but not necessarily required for other solvers.
{% endhint %}

Server validates on-chain via EIP-1271 and returns an access token

Use that token as\
Authorization: Bearer \<access\_token>\
for all subsequent instant open/close calls.

### Detailed Code Example (TypeScript)

```ts
import Safe, { hashSafeMessage }   from '@safe-global/protocol-kit'
import { SigningMethod }           from '@safe-global/types-kit'
import { ethers }                  from 'ethers'
import axios                       from 'axios'

async function main() {
  // ── Configuration ─────────────────────────────────────────
  const RPC_URL      = 'https://rpc.ankr.com/base/…'
  const SAFE_OWNER   = '0x…'    // your EOA private key
  const SAFE_ADDRESS = '0x…'    // your Safe contract
  const SUBACCOUNT   = '0x…'    // sub-account for login
  const SOLVER_BASE  = 'https://base-hedger82.rasa.capital'
  const DOMAIN       = 'localhost'
  const ORIGIN       = 'http://localhost:3000'
  const CHAIN_ID     = 8453

  // 1) initialize Safe SDK (this is the account compatible with ERC4337)
  const protocolKit = await Safe.init({
    provider:   RPC_URL,
    signer:     SAFE_OWNER,
    safeAddress: SAFE_ADDRESS
  })

  // 2) fetch nonce
  const nonce = await axios
    .get(`${SOLVER_BASE}/nonce/${SUBACCOUNT}`)
    .then(r => r.data.nonce)

  // 3) build SIWE message string (exactly same style as your code)
  const issuedAt       = new Date().toISOString()
  const expirationTime = new Date(Date.now() + 86_400_000).toISOString()
  const siweMessage = `${DOMAIN} wants you to sign in with your Ethereum account:
${SAFE_ADDRESS}

msg: ${SUBACCOUNT}

URI: ${SOLVER_BASE}/login
Version: 1
Chain ID: ${CHAIN_ID}
Nonce: ${nonce}
Issued At: ${issuedAt}
Expiration Time: ${expirationTime}`

  // 4) compute ERC‐4337/EIP-191 hash
  function encodeERC4337(msg: string): string {
    const hexMsg   = Buffer.from(msg, 'utf8').toString('hex')
    const prefix   = `\x19Ethereum Signed Message:\n${msg.length}`
    const prefixHex= Buffer.from(prefix, 'utf8').toString('hex')
    return ethers.keccak256('0x' + prefixHex + hexMsg)
  }
  const erc4337Hash = encodeERC4337(siweMessage)
  console.log('[ERC4337] message hash:', erc4337Hash)

  // 5) personal_sign via the Safe (EIP-191)
  const safeMsg   = protocolKit.createMessage(siweMessage)
  const signed    = await protocolKit.signMessage(
    safeMsg,
    SigningMethod.ETH_SIGN,   // personal_sign / EIP-191
    SAFE_ADDRESS
  )
  const signature = signed.encodedSignatures()

  // 6) local EIP-1271 validation (optional)
  const ok = await protocolKit.isValidSignature(erc4337Hash, signature)
  if (!ok) throw new Error('Local EIP-1271 check failed')

  // 7) send the login request
  const loginBody = {
    account_address:  SUBACCOUNT,
    issued_at:        issuedAt,
    expiration_time:  expirationTime,
    nonce,
    signature,
    sign_type:        'ERC4337'   // tell server “use EIP-1271 flow”
  }

  const resp = await axios.post(
    `${SOLVER_BASE}/login`,
    loginBody,
    {
      headers: {
        'Content-Type': 'application/json',
        Origin:          ORIGIN,
        Referer:         ORIGIN
      }
    }
  )

  console.log('Login response:', resp.data)
  // → { access_token: 'eyJ…' }
}

main().catch(console.error)

```

***

When the server receives your POST body, the solver backend will:

• Reconstruct the same SIWE message (using your domain, URI, nonce, subaccount, etc.)\
• Re-compute the ERC-4337 hash (EIP-191 prefix + keccak256)\
• Call `userSmartWallet.isValidSignature(hash, signature)` on-chain\
– If it returns `0x1626ba7e`, the signature is valid\
• If valid, mint you a JWT access\_token with an expiry and your sub-account embedded

Using Your Access Token

***

Once you have `access_token`, attach it to every instant-action request:

Authorization: Bearer \<access\_token>

The solver will trust that token (no further on-chain checks) until it expires.

You can see how to attach this token to requests in the [Building an Application with SYMMIO](https://docs.symm.io/exchange-builder-documentation/frontend-builder-technical-guidance/instant-trading/sending-a-quote-instant-open) section.
