Your user pasted an address from their clipboard. It looked right—but it wasn’t. The first four characters matched. The last four matched. But in the middle, a scammer had swapped characters. $68 million in WBTC, gone in one transaction. This guide shows you how to protect your users from these attacks before they happen.
Why Wallets Need Security
Send Protection Verify addresses before your users send funds to scammers
Transaction Preview Show users what they’re signing before they sign it
Phishing Defense Block malicious dApps before they drain wallets
Why wallet developers choose Webacy:
Address poisoning detection — The attack that stole $68M in one transaction
Transaction simulation — Show users exactly what will happen before signing
EIP-712 signature analysis — Catch permit phishing attacks
URL screening — Block known phishing sites in your dApp browser
Sub-500ms response times — Security without blocking the user experience
Prerequisites
Before implementing wallet security, ensure you have:
A Webacy API key (sign up here )
Basic familiarity with REST APIs or the Webacy SDK
Your wallet’s send flow and transaction signing identified for integration points
Send Flow Protection
The send flow is where users are most vulnerable. A simple address check can save them from losing everything.
Quick Profile Before Sending
Before showing the send confirmation, check the destination address.
curl -X GET "https://api.webacy.com/addresses/0xd9A1C3788D81257612E2581A6ea0aDa244853a91/quick-profile?chain=eth" \
-H "x-api-key: YOUR_API_KEY"
Use the response to guide users:
Risk Level Action lowProceed normally mediumShow caution banner highRequire explicit confirmation criticalBlock and explain why
Address Poisoning Detection
Address poisoning attacks are devastating because they exploit user trust. Scammers create addresses that look nearly identical to legitimate ones.
Real Case : In May 2024, a victim copied what they thought was their own address from transaction history. The attacker’s address matched the first and last characters perfectly. $68 million in WBTC, gone.
Can you spot the difference?
Address ✅ Legitimate 0xd9A1b0B1e1aE382DbDc898Ea68012FfcB2853a91❌ Attacker 0xd9A1C3788D81257612E2581A6ea0aDa244853a91
Both start with 0xd9A1. Both end with 53a91. The scammer generated a vanity address specifically to match the victim’s transaction history.
curl -X GET "https://api.webacy.com/addresses/0xd9A1C3788D81257612E2581A6ea0aDa244853a91/poisoning?chain=eth" \
-H "x-api-key: YOUR_API_KEY"
Integration tip : Run this check whenever a user pastes an address. The 300ms latency is invisible to users—but catching a poisoning attack is priceless.
Sanctions Screening
Prevent users from sending to sanctioned addresses and protect your platform from regulatory risk.
curl -X GET "https://api.webacy.com/addresses/sanctioned/0x098B716B8Aaf21512996dC57EB0615e2383E2f96?chain=eth" \
-H "x-api-key: YOUR_API_KEY"
Transaction Preview
Show users exactly what they’re about to sign. Transaction simulation catches drainers, malicious approvals, and unexpected asset transfers.
Pre-Signing Simulation
Simulate the transaction before the user signs to reveal what will actually happen.
curl -X POST "https://api.webacy.com/scan/transaction" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"walletAddress": "0xUserAddress...",
"tx": {
"from": "0xUserAddress...",
"to": "0xContractAddress...",
"data": "0xTransactionData...",
"value": "0x0"
},
"chain": 1,
"domain": "app.example.com"
}'
Key response fields:
Field Description assetChanges[]What tokens/NFTs will move and in which direction riskLevellow, medium, high, or criticalwarnings[]Specific risks detected (drainer, phishing, etc.)
EIP-712 Signature Analysis
Permit signatures are the most dangerous attack vector. A user can lose everything without sending a single transaction—just by signing a message.
How Permit Phishing Works : The user signs what looks like a simple message. In reality, they’ve authorized a contract to spend all their tokens. Hours or days later, the attacker drains everything.
curl -X POST "https://api.webacy.com/scan/eip712" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"walletAddress": "0xUserAddress...",
"msg": {
"from": "0xUserAddress...",
"data": {
"types": {
"Permit": [
{"name": "owner", "type": "address"},
{"name": "spender", "type": "address"},
{"name": "value", "type": "uint256"}
]
},
"primaryType": "Permit",
"message": {
"owner": "0xUserAddress...",
"spender": "0xSuspiciousAddress...",
"value": "115792089237316195423570985008687907853269984665640564039457584007913129639935"
}
}
},
"domain": "suspicious-site.com"
}'
The max uint256 red flag : If you see value: "115792089237316195423570985008687907853269984665640564039457584007913129639935", that’s unlimited approval. Always flag this for users.
Phishing Protection
If your wallet has a dApp browser, you need URL screening.
URL Scanning
Check URLs before allowing users to connect their wallet.
curl -X POST "https://api.webacy.com/url/check" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://uniswap-claim.com"}'
Approval Management
Help users understand and manage their existing token approvals.
Get Risky Approvals
Show users their current approvals and highlight dangerous ones.
curl -X GET "https://api.webacy.com/wallets/0xUserAddress.../approvals?chain=eth" \
-H "x-api-key: YOUR_API_KEY"
Help users clean up:
Show unlimited approvals prominently
Flag approvals to contracts that no longer exist
Identify approvals to known drainer contracts
Provide easy revoke functionality
Complete Integration Workflow
Send Flow
Transaction Signing Flow
Full TypeScript Implementation
Complete Wallet Security Module
import { ThreatClient , Chain } from '@webacy-xyz/sdk' ;
const client = new ThreatClient ({
apiKey: process . env . WEBACY_API_KEY ,
defaultChain: Chain . ETH ,
});
// Send flow protection
async function validateSendAddress ( address : string , chain : Chain ) {
// 1. Address poisoning (most critical)
const poisoning = await client . addresses . checkPoisoning ( address , { chain });
if ( poisoning . is_poisoned ) {
return {
allowed: false ,
severity: 'critical' ,
reason: 'Address poisoning attack detected' ,
details: 'This address looks similar to legitimate addresses and may be part of a scam.'
};
}
// 2. Sanctions check (compliance)
const sanctions = await client . addresses . checkSanctioned ( address , { chain });
if ( sanctions . is_sanctioned ) {
return {
allowed: false ,
severity: 'critical' ,
reason: 'Sanctioned address' ,
details: 'This address is on the OFAC sanctions list.'
};
}
// 3. Quick profile (risk assessment)
const profile = await client . addresses . getQuickProfile ( address , { chain });
if ( profile . riskLevel === 'critical' ) {
return {
allowed: false ,
severity: 'critical' ,
reason: 'Extremely high risk address' ,
riskScore: profile . riskScore
};
}
if ( profile . riskLevel === 'high' ) {
return {
allowed: true ,
severity: 'high' ,
requireConfirmation: true ,
reason: 'High risk address detected' ,
riskScore: profile . riskScore
};
}
return {
allowed: true ,
severity: profile . riskLevel ,
riskScore: profile . riskScore
};
}
// Transaction preview
async function previewTransaction (
userAddress : string ,
txData : { to : string ; data : string ; value : string },
chain : number ,
dappDomain ?: string
) {
const simulation = await client . scan . scanTransaction ( userAddress , {
tx: {
from: userAddress ,
to: txData . to ,
data: txData . data ,
value: txData . value ,
},
chain ,
domain: dappDomain ,
});
return {
riskLevel: simulation . riskLevel ,
warnings: simulation . warnings ,
assetChanges: simulation . assetChanges ,
shouldBlock: simulation . riskLevel === 'critical' ,
requiresExtraConfirmation: simulation . riskLevel === 'high' ,
};
}
// Signature analysis
async function analyzeSignature (
userAddress : string ,
typedData : object ,
dappDomain : string
) {
const result = await client . scan . scanEip712 ( userAddress , {
msg: {
from: userAddress ,
data: typedData ,
},
domain: dappDomain ,
});
return {
riskLevel: result . riskLevel ,
messageType: result . messageType ,
shouldBlock: result . riskLevel === 'critical' ,
isPermit: result . messageType ?. toLowerCase (). includes ( 'permit' ),
};
}
// URL screening for dApp browser
async function checkDappUrl ( url : string ) {
const result = await client . url . check ( url );
return {
safe: ! result . isPhishing && ! result . isMalware ,
isPhishing: result . isPhishing ,
isMalware: result . isMalware ,
riskScore: result . riskScore ,
categories: result . categories ,
};
}
Example Addresses for Testing
Address Poisoning
Address Chain Description 0xd9A1C3788D81257612E2581A6ea0aDa244853a91ETH Known poisoned address ($68M WBTC attack) 0x5f90e59d0a03fd2f8c56b8cc896c5b42594eb3a0ETH $50M address poisoning drain
Sanctioned Addresses
Address Chain Attribution 0x098B716B8Aaf21512996dC57EB0615e2383E2f96ETH Lazarus Group (Ronin Bridge) 0x566f827a4988d4a3eb9da469d8d3d0b536da196eETH OFAC SDN List
Phishing & Drainers
Address Chain Attribution 0x84672cc56b6dad30cfa5f9751d9ccae6c39e29cdETH Permit phishing drainer 0xe7d13137923142a0424771e1778865b88752b3c7ETH WalletConnect phishing campaign 0x624Fc3Dc249E37E8BFd3e834C4dF81Ff2dA1D0CaBSC Malicious Permit scammer
Clean Addresses (for comparison)
Address Chain Description 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045ETH Vitalik’s wallet (low risk)
API Quick Reference
Endpoint Use Case Response Time GET /addresses/{address}/quick-profileFast risk assessment ~200ms GET /addresses/{address}/poisoningAddress poisoning check ~300ms GET /addresses/sanctioned/{address}Sanctions screening ~100ms POST /scan/transactionTransaction simulation ~500ms POST /scan/eip712Signature analysis ~300ms POST /url/checkURL screening ~200ms GET /wallets/{address}/approvalsToken approvals ~400ms
Next Steps
Get Your API Key Start protecting users today
API Reference Complete endpoint documentation
Install the SDK TypeScript SDK for faster integration
Risk Tags Reference Understand what each risk means