Kevo Docs

EVM Wallets

Kevo provisions embedded EVM wallets for each user and signs through an authenticated server-backed flow. Your app never needs to manage seed phrases or browser extensions.

How EVM wallets work

Provisioned on demand

Call /v1/wallets/me/ensure or use the SDK hooks and Kevo creates the embedded wallet on first use.

Signed through Kevo

Message, typed-data, transaction, and userOp signing all flow through the authenticated /v1/wallets/me/sign endpoint.

Getting wallets

React hook

typescript
import { useWallets } from '@kevo-ws/sdk/react'

function WalletInfo() {
  const { evmWallet, isLoading } = useWallets()

  if (isLoading) return <Spinner />
  if (!evmWallet) return <p>No EVM wallet yet</p>

  return <p>Address: {evmWallet.address}</p>
}

KevoWallet type

typescript
interface KevoWallet {
  id: string
  address: string
  createdAt: string
}

Signing messages

typescript
import { useSignMessage } from '@kevo-ws/sdk/react'

function SignDemo() {
  const { signMessage, isLoading, error } = useSignMessage()

  const handle = async () => {
    const signature = await signMessage('Hello from Kevo!')
    console.log(signature)
  }

  return <button onClick={handle} disabled={isLoading}>Sign</button>
}

HTTP API

http
POST /v1/wallets/me/sign
Authorization: Bearer <accessToken>
Content-Type: application/json

{
  "kind": "message",
  "message": "Hello from Kevo!"
}

# Response: { "signature": "0x..." }

Signing typed data (EIP-712)

typescript
import { useSignTypedData } from '@kevo-ws/sdk/react'

const typedData = {
  domain: { name: 'MyApp', version: '1', chainId: 1, verifyingContract: '0x...' },
  types: {
    Mail: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'contents', type: 'string' },
    ],
  },
  primaryType: 'Mail',
  message: { from: '0xSender', to: '0xRecipient', contents: 'Hello!' },
}

const sig = await signTypedData(typedData)

Sending transactions

sendTransaction automatically fills missing nonce, gasLimit, fee fields, and RPC details. If gas sponsorship is enabled for the project and chain, the SDK can take the sponsored path automatically.

typescript
import { useSignTransaction } from '@kevo-ws/sdk/react'
import { nativeTransfer } from '@kevo-ws/sdk/helpers'

function SendDemo() {
  const { sendTransaction, isLoading } = useSignTransaction()

  const send = async () => {
    const txHash = await sendTransaction(
      nativeTransfer({
        to: '0xRecipient',
        value: 1_000_000_000_000_000n,
        chainId: 1,
      })
    )
    console.log('TX hash:', txHash)
  }

  return <button onClick={send} disabled={isLoading}>Send 0.001 ETH</button>
}
Use signTransaction() when you need raw signed bytes but want to broadcast yourself.

Helpers

typescript
import {
  erc20Transfer, erc20Approve, nativeTransfer, encodeFunctionCall,
} from '@kevo-ws/sdk/helpers'

const approval = erc20Approve({
  token: '0xTokenAddress',
  spender: '0xSpenderContract',
  amount: 2n ** 256n - 1n,
  chainId: 1,
})

const tx = encodeFunctionCall({
  to: '0xContractAddress',
  functionSignature: 'transfer(address,uint256)',
  params: [
    { type: 'address', value: '0xRecipient' },
    { type: 'uint256', value: 1_000_000n },
  ],
  chainId: 1,
})