Author: Oleksandr Dendeberia
Original post on Foojay: Read More
Backend validation often starts with simple questions.
Is the input empty? Does it have the expected shape? Can it be parsed? Does it belong to the selected domain?
Crypto wallet addresses are no different, except the formats vary across chains and simple regular expressions are rarely enough.
If a Java backend accepts crypto withdrawal addresses, address-book entries, CSV imports, support-tool input, or transaction preflight requests, it should reject clearly invalid addresses before calling RPC nodes, explorers, exchange APIs, or internal money-movement systems.
That first validation layer can be done offline.
This article explains what can be validated locally, what cannot be proven offline, and how to implement a practical Java validation layer using Chainwarden, an open-source library published on Maven Central.
What Can Be Validated Offline?
Offline validation checks properties that are fully contained in the address string itself.
For example:
- syntax
- allowed character set
- encoded length
- decoded byte length
- network prefix
- address version
- checksum
- EIP-55 casing for EVM addresses
These checks are deterministic and do not require network access.
That makes them a good fit for:
- request validation
- form validation
- withdrawal preflight checks
- address-book validation
- import jobs
- support tools
- logging and diagnostics
What Cannot Be Validated Offline?
Offline validation is not a complete safety check.
It cannot prove:
- account existence
- balance
- ownership
- smart contract status
- whether a token can be received
- whether a custodial address requires a memo or destination tag
- whether an exchange will accept a transfer
For example, XRP has destination-tag requirements in many custodial flows. An offline validator can check whether the XRP address format is valid. It cannot know whether the receiving platform requires a tag for that specific deposit.
That distinction is important in production systems.
Offline validation should be the first gate, not the final decision.
Adding Chainwarden
Add the high-level facade from Maven Central:
<dependency>
<groupId>org.chainwarden</groupId>
<artifactId>chainwarden-core</artifactId>
<version>0.1.0</version>
</dependency>
For Gradle:
implementation("org.chainwarden:chainwarden-core:0.1.0")
Validating an Address
The main API is:
AddressValidators.validate(chain, address)
Here is a Bitcoin example:
import org.chainwarden.AddressValidators;
import org.chainwarden.Chain;
import org.chainwarden.common.validation.AddressValidationResult;
public class ValidateBitcoinAddress {
public static void main(String[] args) {
AddressValidationResult result = AddressValidators.validate(
Chain.BITCOIN,
"bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8"
);
if (result.valid()) {
System.out.println("Valid " + result.chain() + " address");
System.out.println("Format: " + result.format());
} else {
System.out.println("Invalid address");
System.out.println("Error: " + result.error());
System.out.println("Reason: " + result.reason());
}
}
}
Possible output:
Valid bitcoin address
Format: BECH32
The result contains:
valid()– whether validation succeededchain()– canonical chain idformat()– detected address formaterror()– stable machine-readable error codereason()– human-readable diagnostic text
Returning structured results is useful in production because validation failures can be logged, counted, translated into user-facing messages, or mapped to API error responses.
Validating By Chain Id
Many systems receive the chain as a string from an API request, database field, or configuration file.
Chainwarden also accepts canonical ids and aliases:
import org.chainwarden.AddressValidators;
import org.chainwarden.common.validation.AddressValidationResult;
public class ValidateByChainId {
public static void main(String[] args) {
AddressValidationResult result = AddressValidators.validate(
"ethereum",
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
);
System.out.println(result.valid());
System.out.println(result.chain());
System.out.println(result.format());
}
}
Output:
true
ethereum
EIP55
For example, BNB Smart Chain can be validated with bnb-smart-chain, bsc, or BNB_SMART_CHAIN:
boolean valid = AddressValidators.isValid(
"bsc",
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
);
Mapping Validation Errors
A boolean is enough for quick checks, but applications usually need better feedback.
import org.chainwarden.AddressValidators;
import org.chainwarden.common.validation.AddressValidationResult;
public class ValidationMessage {
public static String messageFor(String chain, String address) {
AddressValidationResult result = AddressValidators.validate(chain, address);
if (result.valid()) {
return "Address is valid";
}
return switch (result.error()) {
case UNSUPPORTED_CHAIN -> "This chain is not supported yet";
case EMPTY -> "Address is required";
case SURROUNDING_WHITESPACE -> "Remove leading or trailing spaces";
case INVALID_CHECKSUM -> "Address checksum is invalid";
case INVALID_PREFIX -> "Address prefix does not match the selected chain";
case INVALID_LENGTH -> "Address length is invalid";
case INVALID_CHARACTER -> "Address contains invalid characters";
case INVALID_WORKCHAIN -> "TON workchain is not supported";
case INVALID_ENCODING -> "Address encoding is invalid";
case INVALID_FORMAT -> "Address format is invalid";
case NONE -> "Address is valid";
};
}
}
This keeps the validation layer deterministic while giving API consumers a useful reason when validation fails.
Supported Formats
Chainwarden currently supports:
| Chain | Formats |
|---|---|
| Bitcoin | Base58Check, Bech32, Bech32m |
| Ethereum | EVM 0x address, EIP-55 |
| BNB Smart Chain | EVM 0x address, EIP-55 |
| Base | EVM 0x address, EIP-55 |
| Arbitrum One | EVM 0x address, EIP-55 |
| Polygon PoS | EVM 0x address, EIP-55 |
| Avalanche C-Chain | EVM 0x address, EIP-55 |
| TRON | Base58Check with TRON prefix |
| Solana | Base58-encoded 32-byte public key |
| XRP Ledger | Classic address, mainnet X-address |
| TON | Raw and user-friendly formats |
EVM chains share the same address format: a 0x prefix and 20 bytes encoded as 40 hexadecimal characters. Mixed-case addresses are checked using EIP-55.
Conclusion
Crypto address validation is a good candidate for boring, deterministic infrastructure. The first layer should not need a network call. It should decode the address, check local rules, return structured results, and be clear about its limits.
Chainwarden provides that first layer for Java applications, while deliberately avoiding claims it cannot prove offline.
Project links:
- Website and demo: https://chainwarden.org
- Maven Central: https://central.sonatype.com/artifact/org.chainwarden/chainwarden-core
The post Offline Crypto Address Validation in Java appeared first on foojay.
NLJUG – Nederlandse Java User Group NLJUG – de Nederlandse Java User Group – is opgericht in 2003. De NLJUG verenigt software ontwikkelaars, architecten, ICT managers, studenten, new media developers en haar businesspartners met algemene interesse in alle aspecten van Java Technology.