Mint Mizuhiki Verified SBT
The Mizuhiki Verified SBT is an on-chain proof of KYC that can be attached to a verified user's EVM address(es), creating KYC'd EVM addresses without exposing any personal information or linkage on-chain.
The Mizuhiki Verified SBT adheres to the ERC-5484 standard.
The SBT can only be minted via the Mizuhiki SDK to users who completed KYC via Mizuhiki. Please make sure you have completed the KYC integration steps from our Verify Users guide first.
Integrate Mizuhiki Verified SBT issuance
Once the user's identity is verified, you can issue a Mizuhiki Verified soulbound token (SBT) to the user's EVM address(es).
We require proof of control from the user in the form of an EIP-4361 signed message. To issue the SBT, follow these steps:
- Get the nonce and message content to be signed by the user
- Obtain a signed EIP-4361 message from the user's wallet
- Submit the user's wallet address, message (without the
\x19Ethereum Signed Message:prefix), nonce, and signature to the Mizuhiki SDK
1. Get the nonce and message content to be signed by the user
The returned SIWE fields correspond to EIP-4361 message fields.
- iOS
- Android
// After successful KYC verification, request the SIWE message fields
//
// SIWEMessage type in the Mizuhiki SDK
//
// public struct SIWEMessage: Codable, Sendable, Equatable {
// public let statement: String
// public let domain: String
// public let uri: String
// public let chainId: Int
// public let nonce: String
// public let issuedAt: String // "2025-12-01T21:34:56.789+09:00",
// public let expirationTime: String // 2025-12-01T22:04:56.789+09:00"
// }
do {
let siweMessage = try await appEnv.mizuhiki.generateSIWEMessage()
} catch {
print("❌ Failed to get SIWE message fields: \(error.localizedDescription)")
}
// After successful KYC verification, request the SIWE message fields
//
// SIWEMessage type in the Mizuhiki SDK
//
// data class SIWEMessage(
// val statement: String,
// val domain: String,
// val uri: String,
// val chainId: Int,
// val nonce: String,
// val issuedAt: String, // "2025-12-01T21:34:56.789+09:00"
// val expirationTime: String, // "2025-12-01T22:04:56.789+09:00"
// )
try {
val siweMessage = client.generateSIWEMessage()
} catch (e: Exception) {
println("❌ Failed to get SIWE message fields: ${e.message}")
}
2. Obtain a signed EIP-4361 message from the user's wallet
Ask the user to sign the constructed SIWE message using your preferred wallet integration.
For WalletConnect (now Reown) follow the documentation here.
3. Submit the signature to the Mizuhiki server
Parameters
| Parameter | Type | Required | Description | Example |
|---|---|---|---|---|
address | String | Yes | EVM-compatible wallet address | "0x742...9a5c61" |
nonce | String | Yes | One-time usable nonce included in the message to avoid replay attacks | gLMlRC...rMEYPw |
message | String | Yes | Original message without EIP-191 prefix | "jsc.dev wants to sign in with your Ethereum account: ..." |
signature | String | Yes | Wallet signature of the message | "0x1c5e...a4b2" |
walletAppName | String | Optional | Name of wallet app used for signing | "MetaMask", "WalletConnect" |
- iOS
- Android
// After successful KYC verification, bind the user's EVM address
do {
let result = try await appEnv.mizuhiki.addAddress(
address: ethereumAddress, // User's wallet address
message: originalMessage, // SIWE message constructed in step 2 without EIP-191 prefix
nonce: nonce, // Nonce from step 1
signature: walletSignature, // Signature from user's wallet
walletAppName: "MetaMask" // Optional: wallet app identifier
)
// Handle successful SBT issuance
print("✅ SBT successfully issued to address: \(ethereumAddress)")
} catch {
print("❌ Failed to issue SBT: \(error.localizedDescription)")
}
// After successful KYC verification, bind the user's EVM address
try {
val result = client.addAddress(
address = ethereumAddress, // User's wallet address
message = originalMessage, // SIWE message constructed in step 2 without EIP-191 prefix
nonce = nonce, // Nonce from step 1
signature = walletSignature, // Signature from user's wallet
walletAppName = "MetaMask" // Optional: wallet app identifier
)
// Handle successful SBT issuance
println("✅ SBT successfully issued to address: $ethereumAddress")
} catch (e: Exception) {
println("❌ Failed to issue SBT: ${e.message}")
}
Troubleshooting
Common Issues and Solutions
Issue: Request Validation Failed
- Symptoms:
addAddress()throws request validation error - Root Causes & Solutions:
Cause Solution Invalid Signature Format Verify signature is valid hex string starting with 0xInvalid Address Format Ensure address is a valid EVM-compatible format 0x...
Issue: Signature Verification Failed
- Symptoms:
addAddress()throws signature verification error - Root Causes & Solutions:
Cause Solution EIP-191 prefix included Remove \x19Ethereum Signed Message:prefix from the message submitted to the Mizuhiki serverWrong message signed Ensure a correctly formatted EIP-4361 message is signed using fields provided by the Mizuhiki server Invalid signature format Verify signature is valid hex string starting with 0x
Getting Help
If you encounter issues not covered here:
- Check SDK Version: Ensure you're using the latest SDK version
- Contact Support: Email [email protected] with:
- SDK version and platform (iOS/Android)
- Environment (mock/production)
- Complete error messages and stack traces
- Minimal reproduction code example