Code Examples
Overview
The following examples show a complete Address Proving flow:
- Call API 1 to generate the
challengeMsg - Build the
proofPayload - Sign the payload with your private key
- Call API 2 to submit the proof
The examples below are provided for integration reference. You may adapt them to your own SDK, key storage, or HSM workflow.
Shell Example
This example assumes:
- you already have a Base64 public key
- you already have the corresponding private key in PEM format
- your signing algorithm is
SHA256withECDSA
BASE_URL="${BASE_URL}"
TOKEN="[YOUR_ADDRESS_PROVING_TOKEN]"
PUBLIC_KEY_BASE64="[BASE64_PUBLIC_KEY]"
PUBLIC_KEY_TYPE="secp256k1"
PROOF_ALGO="SHA256withECDSA"
WALLET_ADDRESS="[WALLET_ADDRESS]"
USER_NONCE="123e4567-e89b-12d3-a456-426614174000"
PUBLIC_KEY_FORMAT="PEM"
PRIVATE_KEY_FILE="./private_key.pem"
CHALLENGE_MSG=$(curl --silent --location --request GET "$BASE_URL/ext/api/cosmos/v1/address/proof/generate" \
--header 'Content-Type: application/json' \
--header "X-Authorization: $TOKEN" \
--data-raw "{
\"publicKey\": \"$PUBLIC_KEY_BASE64\",
\"publicKeyType\": \"$PUBLIC_KEY_TYPE\",
\"proofAlgo\": \"$PROOF_ALGO\",
\"walletAddress\": \"$WALLET_ADDRESS\",
\"userNonce\": \"$USER_NONCE\",
\"publicKeyFormat\": \"$PUBLIC_KEY_FORMAT\"
}" | python3 -c 'import sys, json; print(json.load(sys.stdin)["data"]["challengeMsg"])')
PROOF_PAYLOAD="PublicKey:${PUBLIC_KEY_BASE64};PublicKeyType:${PUBLIC_KEY_TYPE};WalletAddress:${WALLET_ADDRESS};Vendor:gtr;ChallengeMsg:${CHALLENGE_MSG};ProofAlgo:${PROOF_ALGO};UserNonce:${USER_NONCE};PublicKeyFormat:${PUBLIC_KEY_FORMAT};"
BASE64_SIGNATURE=$(printf "%s" "$PROOF_PAYLOAD" | openssl dgst -sha256 -sign "$PRIVATE_KEY_FILE" | openssl base64 -A)
curl --silent --location --request POST "$BASE_URL/ext/api/cosmos/v1/address/proof/submit" \
--header 'Content-Type: application/json' \
--header "X-Authorization: $TOKEN" \
--data-raw "{
\"proofPayload\": \"$PROOF_PAYLOAD\",
\"base64SignedMessage\": \"$BASE64_SIGNATURE\"
}"
Java Example
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class AddressProvingExample {
public static void main(String[] args) throws Exception {
String baseUrl = System.getenv("BASE_URL");
String token = "[YOUR_ADDRESS_PROVING_TOKEN]";
String publicKey = "[BASE64_PUBLIC_KEY]";
String publicKeyType = "secp256k1";
String proofAlgo = "SHA256withECDSA";
String walletAddress = "[WALLET_ADDRESS]";
String userNonce = "123e4567-e89b-12d3-a456-426614174000";
String publicKeyFormat = "PEM";
String privateKeyBase64Pkcs8 = "[BASE64_PKCS8_PRIVATE_KEY]";
HttpClient client = HttpClient.newHttpClient();
String generateRequestJson = String.format("""
{
\"publicKey\": \"%s\",
\"publicKeyType\": \"%s\",
\"proofAlgo\": \"%s\",
\"walletAddress\": \"%s\",
\"userNonce\": \"%s\",
\"publicKeyFormat\": \"%s\"
}
""", publicKey, publicKeyType, proofAlgo, walletAddress, userNonce, publicKeyFormat);
HttpRequest generateRequest = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/ext/api/cosmos/v1/address/proof/generate"))
.header("Content-Type", "application/json")
.header("X-Authorization", token)
.method("GET", HttpRequest.BodyPublishers.ofString(generateRequestJson))
.build();
HttpResponse<String> generateResponse = client.send(generateRequest, HttpResponse.BodyHandlers.ofString());
String challengeMsg = extractChallengeMsg(generateResponse.body());
String proofPayload = String.format(
"PublicKey:%s;PublicKeyType:%s;WalletAddress:%s;Vendor:gtr;ChallengeMsg:%s;ProofAlgo:%s;UserNonce:%s;PublicKeyFormat:%s;",
publicKey, publicKeyType, walletAddress, challengeMsg, proofAlgo, userNonce, publicKeyFormat
);
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64Pkcs8);
PrivateKey privateKey = KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
Signature signature = Signature.getInstance("SHA256withECDSA");
signature.initSign(privateKey);
signature.update(proofPayload.getBytes(StandardCharsets.UTF_8));
String base64SignedMessage = Base64.getEncoder().encodeToString(signature.sign());
String submitRequestJson = String.format("""
{
\"proofPayload\": \"%s\",
\"base64SignedMessage\": \"%s\"
}
""", escapeJson(proofPayload), base64SignedMessage);
HttpRequest submitRequest = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/ext/api/cosmos/v1/address/proof/submit"))
.header("Content-Type", "application/json")
.header("X-Authorization", token)
.POST(HttpRequest.BodyPublishers.ofString(submitRequestJson))
.build();
HttpResponse<String> submitResponse = client.send(submitRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(submitResponse.body());
}
private static String extractChallengeMsg(String json) {
String marker = "\"challengeMsg\":\"";
int start = json.indexOf(marker);
if (start < 0) throw new IllegalStateException("challengeMsg not found in response: " + json);
start += marker.length();
int end = json.indexOf('"', start);
return json.substring(start, end);
}
private static String escapeJson(String text) {
return text.replace("\\", "\\\\").replace("\"", "\\\"");
}
}
Python Example
import base64
import json
import os
import requests
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.serialization import load_pem_private_key
BASE_URL = os.environ["BASE_URL"]
TOKEN = "[YOUR_ADDRESS_PROVING_TOKEN]"
PUBLIC_KEY = "[BASE64_PUBLIC_KEY]"
PUBLIC_KEY_TYPE = "secp256k1"
PROOF_ALGO = "SHA256withECDSA"
WALLET_ADDRESS = "[WALLET_ADDRESS]"
USER_NONCE = "123e4567-e89b-12d3-a456-426614174000"
PUBLIC_KEY_FORMAT = "PEM"
with open("private_key.pem", "rb") as f:
private_key = load_pem_private_key(f.read(), password=None)
generate_payload = {
"publicKey": PUBLIC_KEY,
"publicKeyType": PUBLIC_KEY_TYPE,
"proofAlgo": PROOF_ALGO,
"walletAddress": WALLET_ADDRESS,
"userNonce": USER_NONCE,
"publicKeyFormat": PUBLIC_KEY_FORMAT,
}
generate_resp = requests.get(
f"{BASE_URL}/ext/api/cosmos/v1/address/proof/generate",
headers={
"Content-Type": "application/json",
"X-Authorization": TOKEN,
},
data=json.dumps(generate_payload),
timeout=15,
)
generate_resp.raise_for_status()
challenge_msg = generate_resp.json()["data"]["challengeMsg"]
proof_payload = (
f"PublicKey:{PUBLIC_KEY};"
f"PublicKeyType:{PUBLIC_KEY_TYPE};"
f"WalletAddress:{WALLET_ADDRESS};"
f"Vendor:gtr;"
f"ChallengeMsg:{challenge_msg};"
f"ProofAlgo:{PROOF_ALGO};"
f"UserNonce:{USER_NONCE};"
f"PublicKeyFormat:{PUBLIC_KEY_FORMAT};"
)
signature = private_key.sign(
proof_payload.encode("utf-8"),
ec.ECDSA(hashes.SHA256()),
)
base64_signed_message = base64.b64encode(signature).decode("ascii")
submit_payload = {
"proofPayload": proof_payload,
"base64SignedMessage": base64_signed_message,
}
submit_resp = requests.post(
f"{BASE_URL}/ext/api/cosmos/v1/address/proof/submit",
headers={
"Content-Type": "application/json",
"X-Authorization": TOKEN,
},
json=submit_payload,
timeout=15,
)
submit_resp.raise_for_status()
print(submit_resp.json())
Notes
- The language examples above are simplified integration references.
- In production, you may prefer using an HSM, KMS, or internal signing service instead of loading the private key directly in application code.
- Make sure your chosen signing algorithm matches both
publicKeyTypeandproofAlgo. - If you use
DERforpublicKeyFormat, ensure that the key bytes follow the raw uncompressed EC point format expected by GTR.