ML-DSA verify precompile
ecrecover cannot verify a BKC account's signature — there is no ECDSA key. BKC adds a precompile that verifies ML-DSA-65 signatures from Solidity.
| Address | 0x0000000000000000000000000000000000000900 |
| Algorithm | ML-DSA-65 (FIPS 204), empty context |
| Public key | exactly 1,952 bytes |
| Signature | exactly 3,309 bytes |
| Gas | 30,000 + 2 × ABI-encoded calldata bytes |
solidity
interface IMldsaVerify {
function mldsaVerify(bytes calldata pubkey, bytes calldata message, bytes calldata signature)
external pure returns (bool);
}
contract Gate {
IMldsaVerify constant MLDSA = IMldsaVerify(0x0000000000000000000000000000000000000900);
function check(bytes calldata pubkey, bytes calldata message, bytes calldata sig)
external view returns (bool)
{
return MLDSA.mldsaVerify(pubkey, message, sig);
}
}A typical call costs a little over 40,000 gas (30,000 base plus about 10,700 bytes of calldata × 2).
Signing off-chain, verifying on-chain
The SDK signs ADR-36 messages with an account's ML-DSA key and can check them either locally or through the precompile:
ts
import { signMessage, verifyMessage, verifyMessageOnChain } from "@bkc-network-lib/sdk";
const signed = await signMessage(wallet, "Sign in to example.app");
await verifyMessage(signed); // off chain
await verifyMessageOnChain(signed, "https://rpc.bkc.network"); // through 0x…0900This replaces signer.signMessage() / ecrecover-based login, which cannot work on BKC.
Notes
- Verification is stateless and independent of accounts: any ML-DSA-65 key pair works, including keys that never touched the chain.
- The precompile deliberately does not use the node's public-key cache, so arbitrary keys passed as calldata cannot evict account keys from it.
- Standard
ecrecoverat0x01stays enabled for verifying externally created secp256k1 signatures; those never control BKC accounts. - The interface is unaudited and intended for testnet integration.
