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,
})