Skip to main content


How do I generate a Avatar?

As in glossary, technically, avatars/ is the same as Ethereum wallet: they're both secp256k1 elliptic curve asymmetric keypair.

So, generating and managing Persona should be the same as generating / managing wallets. Search for an secp256k1 SDK in your project's programming language to generate one.



  • secret key should be 32-bytes long
  • public key has 2 shapes:
    • Uncompressed(65-bytes, started with 0x04), or
    • compressed(33-bytes, started with 0x02 or 0x03, in most case 0x02)

I got "bad signature" error in POST /v1/proof

  1. Check if created_at and uuid is the same as POST /v1/proof/payload result.
  2. Check if you're using Ethereum Personal Sign.
    • If your DApp is using wallet SDK (e.g. MetaMask), make sure you're using correct signature RPC method.
How do I implement personal sign by myself?
  • In pseudo-code, personal_sign is:
sign(keccak256("\x19Ethereum Signed Message:\n" + dataToSign.length + dataToSign)))
  • Make sure dataToSign.length is length of bytes (Buffer length in some language), not UTF-8 code point length.
    assert.Equal(4, len([]byte("🐎"))) // Not 1
  • Signature should be 65-bytes long.
<<r::binary-size(32), s::binary-size(32), v::binary-size(1)>> = signature_binary
# v should be 0x00 or 0x01 or 0x1B or 0x1C
# 0x00 is equivalent to 0x1B
# 0x01 is equivalent to 0x1C

Here's a test case.

// Psuedo-code
// Given a signature payload
payload := "Test123123!"
// And a secret key
secret_key := "0x9deba3488458c0314e5fef8920d3b117dd76415569cf270db8fd864896c02732"
// The personal sign result should be
personal_sign(secret_key, payload).ToHexString()

Sample code snippets for avatar-generating and signing

libsecp256k1 = "0.7"
hex = "0.4"
sha3 = "0.10" # Keccak256
use libsecp256k1::{SecretKey, Message};
use sha3::{Keccak256, Digest};

const SECRET_KEY: &str = "9DEBA3488458C0314E5FEF8920D3B117DD76415569CF270DB8FD864896C02732";
const SIGN_PAYLOAD: &str = "Test123123!";

fn main() {
// Raw sign message
let sign_payload: String = SIGN_PAYLOAD.to_string();
// SecretKey instance
let secret_key = SecretKey::parse_slice(hex::decode(SECRET_KEY).unwrap().as_slice()).unwrap();
// Sign it
let personal_signature = personal_sign(&sign_payload, &secret_key);
// Verify it
println!("Signature: 0x{}", hex::encode(personal_signature));
// Signature: 0x52f210dadad13c4c8d0656e7380300a367a056631cf26950baa7de4f580187795c76b5fc94de5bd0b8af4d5df432687d900402cba86a12570af56be35ba8d56401

/// `web3.eth.personal.sign()`
fn personal_sign(payload: &String, secret_key: &SecretKey) -> Vec<u8> {
// Wrap personal.sign() required signature struct
let personal_message = format!("\x19Ethereum Signed Message:\n{}{}", payload.len(), payload);
// Keccak256 it into a digest.
let mut hasher = Keccak256::default();
let digest: [u8; 32] = hasher.finalize().into();

// Sign the digest.
let (r_and_s, v) = libsecp256k1::sign(&Message::parse(&digest), secret_key);
// Rebuild the sig into a [u8; 65]
let mut signature: Vec<u8> = vec![];
signature.extend_from_slice(&r_and_s.r.b32()); // r (32 bytes)
signature.extend_from_slice(&r_and_s.s.b32()); // s (32 bytes)
signature.push(v.serialize()); // v (1 byte)
if signature.len() != 65 {
panic!("Signature length is not 65 bytes");

I want to contribute to ProofService, which platforms do you accept?

Well, basiclly every platform can be accepted by us if it meets the following requirement:

  • Identity (on target platform) has the ability to post content specified by user.

  • This content can be read publicly, at anytime, by anyone (human or program), after it is posted.

  • There is a mechanism to "encode" and "extract" signature into / from this content.

    Depends on the shape this content represents, the mechanism to "extract" sig may vary. For example, assuming a platform which only supports posting user-generated pictures, you need to find a way to "encode" the signature into the user-generated pic, and make sure other people / program can "extract" from this pic.

    The most common case is text-based content, which is the easist to process. But if there is a limitation on posting text content on target platform, you should be prepared.