Skip to main content

Issue a credential

x/identity never stores personal data — only a credential's hash, its type, and its revocation status, anchored by a governance-authorized issuer. This walks through authorizing an issuer, issuing a credential, and watching an account's tier flip to attested — every command below was actually run against a live devnet.

Prerequisites

A running devnet (see Run a node) and two funded accounts: one to act as the issuer, one to receive the credential.

lcvd keys add issuer1 --home ~/.lcv-devnet/validator0 --keyring-backend test
lcvd keys add subject1 --home ~/.lcv-devnet/validator0 --keyring-backend test

lcvd tx bank send validator0 <issuer1-address> 5000000ulcv \
--home ~/.lcv-devnet/validator0 --keyring-backend test --chain-id lcv-devnet \
--gas 300000 --fees 10000ulcv -y

Authorize the issuer

Issuers are added via governance (MsgAddIssuer's authority is x/gov), not by any individual account unilaterally. This devnet runs a fast governance config (30s voting period) specifically so this kind of walkthrough doesn't take an hour:

cat > add-issuer-proposal.json <<'EOF'
{
"messages": [
{
"@type": "/lcv.identity.MsgAddIssuer",
"authority": "<gov-module-account-address>",
"issuer_address": "<issuer1-address>",
"name": "Tutorial Issuer"
}
],
"metadata": "",
"deposit": "10000000ulcv",
"title": "Authorize tutorial credential issuer",
"summary": "Adds issuer1 as an authorized x/identity credential issuer."
}
EOF

Get the gov module account address (it's deterministic, not a key you generate):

lcvd query auth module-account gov --home ~/.lcv-devnet/validator0

Submit and vote:

lcvd tx gov submit-proposal add-issuer-proposal.json \
--from validator0 --home ~/.lcv-devnet/validator0 --keyring-backend test \
--chain-id lcv-devnet --gas 300000 --fees 10000ulcv -y

# vote yes from every validator (this devnet's 4 validators hold all the
# voting power — a real network needs the equivalent quorum/threshold from
# its actual delegator set)
for i in 0 1 2 3; do
lcvd tx gov vote <proposal-id> yes --from validator$i --home ~/.lcv-devnet/validator$i \
--keyring-backend test --chain-id lcv-devnet --gas 300000 --fees 10000ulcv -y
done

After the voting period ends:

curl -s http://localhost:1317/lastcoinvision/lcv/identity/issuers
# {"issuers":[{"address":"lcv19dehj6ta4e82mpehdq6k9nye25r207y38kfklm","name":"Tutorial Issuer","added_at":"..."}],"pagination":null}

Issue the credential

CRED_HASH=$(echo -n "subject1-kyc-credential-doc-v1" | shasum -a 256 | cut -d' ' -f1)

lcvd tx identity issue-credential \
--subject <subject1-address> \
--credential-type kyc \
--credential-hash "0x${CRED_HASH}" \
--from issuer1 --home ~/.lcv-devnet/validator0 --keyring-backend test \
--chain-id lcv-devnet --gas 300000 --fees 10000ulcv -y

Note what's on-chain here: a hash of an off-chain document, a type ("kyc"), and who issued it. Never the document itself, never a name or date of birth.

Check the tier

attested_credential_type is a module param ("kyc" by default) — an account is automatically attested if and only if it holds a valid, unrevoked credential of that type. Nothing sets the tier directly; it's derived:

curl -s http://localhost:1317/lastcoinvision/lcv/identity/tier/<subject1-address>
# {"tier":"ACCOUNT_TIER_ATTESTED","role_metadata":""}

Revoke it and the tier reverts automatically:

lcvd tx identity revoke-credential --credential-id <id> \
--from issuer1 --home ~/.lcv-devnet/validator0 --keyring-backend test \
--chain-id lcv-devnet --gas 300000 --fees 10000ulcv -y

Next

This attested account can now do anything that requires it — for example, register to vote in an election whose eligibility_credential_type is "kyc". See Build a voting portal.