TL;DR: Errors are a UI. Viem (Ethereum TypeScript library) sets the bar for developer-facing errors. Here's a real viem error when a contract call reverts — notice how every field helps you debug:
The contract function "transfer" reverted with the following reason:
Internal error
Contract Call:
address: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
function: transfer(address to, uint256 amount)
args: (0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045, 1000000)
sender: 0x0000000000000000000000000000000000000001
Docs: https://viem.sh/docs/contract/simulateContract
Details: Internal error
Version: viem@2.47.6
Human-readable reason, structured call context, docs link, version tag. Now imagine the same for our KYC flow:
KYC verification for session "cos_abc123" was rejected.
Reason: document_expired
- Identity document must not be expired at time of submission.
- Document expiration date detected: 2024-01-15.
- Resubmit with a valid, non-expired government-issued photo ID.
Verification attempt:
session: cos_abc123
user: usr_xyz789
doc_type: passport
submitted: 2026-03-28T14:32:00Z
Next step: Call `createVerificationSession()` to start a new attempt.
Docs: https://docs.stripe.com/crypto/onramp/kyc-verification
SDK: @stripe/crypto-onramp@1.2.3
Runnable viem examples + full pattern breakdown below.
npm install viem
node demo.mjsAddress "0xdeadbeef" is invalid.
- Address must be a hex value of 20 bytes (40 hex characters).
- Address must match its checksum counterpart.
Version: viem@2.47.6
Data size of 2 bytes is too small for given parameters.
Params: (uint256)
Data: 0x1234 (2 bytes)
Version: viem@2.47.6
The contract function "transfer" reverted with the following reason:
Internal error
Contract Call:
address: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
function: transfer(address to, uint256 amount)
args: (0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045, 1000000)
sender: 0x0000000000000000000000000000000000000001
Docs: https://viem.sh/docs/contract/simulateContract
Details: Internal error
Version: viem@2.47.6
| Pattern | What viem does | Why it works |
|---|---|---|
| Echo the input | Address "0xdeadbeef" is invalid |
No guessing which param failed |
| List the rules | Must be 20 bytes, must match checksum |
Developer knows how to fix it without Googling |
| Structured context | Formatted block with address, function, args, sender | Copy-pasteable into a debugger |
| Docs link | Docs: https://viem.sh/docs/contract/simulateContract |
Zero-friction path to help |
| Version tag | Version: viem@2.47.6 |
Instant triage on bug reports |
| Human-readable names | "transfer" reverted not 0xa9059cbb reverted |
Raw data still in Details: if needed |
The validation error pattern is the closest fit for KYC — see the example at the top.
| viem pattern | Onramp SDK equivalent |
|---|---|
| Echo the bad value | Echo rejection reason + detected issue (e.g. expiry date) |
| List the rules | What's required to pass (accepted doc types, validity requirements) |
| Docs link | Link to specific KYC doc page, not generic API reference |
| Structured context | Session ID, state, relevant params |
| Version tag | SDK version |
| Next step (addition) | The concrete API call to recover — don't make devs guess |