Attestation Discord Bot


EthSign's AttestationBot is a gatekeeping bot centered around attestations. Users can attest anything; by simply choosing an attestation's schema ID, Discord server administrators can use attestations from a specific schema to verify users within their Discord community. For a user's Discord account to be verified, the user must create an attestation with the chosen schema ID and sign a message with their wallet address to link their Discord account. Discord administrators can also require that a specific Result field be set to true on the attestations to add another layer of verification. Once these checks pass verification, users will be granted a role in Discord, granting them desired access as defined by the verification role in the server.

Discord Admin Command Reference

/register <testnet> <mode> <schemaId> <verifyAttestation> [attester]

Register attestation data for the server's verification. Requires testnet (true/false), mode (on-chain/off-chain), the schema ID for verification, and whether or not to check the Result field (true/false). Optionally, Discord Server admins can supply an attester address for querying attestations created by a single address (a trusted delegate address).

/generate_role [name]

Generates a pre-defined Discord Role in the server with default permissions. If the name is left blank, the role's name will default to "Verified".

/set_role <role>

Sets the verification role to whichever role the user passes in as an argument.


If set to on, displays the number of verified users in the server.

User Command Reference


DMs user a link for verification. Upon success, a role will be given to the user. Upon failure, tells the user why it failed (no role setup in server, no attestation linked to server, user already verified, etc).

ID Format (Schema + Attestation)

IDs are generated in the following format: {mode}_{chainType}_{chainId}_{id}

Example schema ID: onchain_evm_80001_0x3

Example attestation ID: onchain_evm_80001_0x11

Bot Initialization

1. /register <testnet> <mode> <schemaId> <verifyAttestation> [attester]

Register the Attestation-specific data for a server.

2. /generate_role or /set_role

Set the verified role for a server.

3. /display_count (optional)

Show the number of verified users in a server.

4. /verify

Users can now verify their Discord accounts to be granted special permissions as defined by the verified role.

End User Verification Flow

  1. Run /verify and click the link provided by direct message.

  2. Connect MetaMask to the website.

  3. Click the “Verify” button and sign the relevant message in MetaMask.

  4. Return to Discord.

NOTE: If for any reason the role is removed from a verified Discord account in a Discord server, the user can simply re-run /verify and be granted the role without additional verification.

Backend Endpoint Reference


Validates request body message and signature for the given ID. If successful, the user is granted a role in the relevant Discord server, and the attestation ID is returned for display purposes. Failure will result in an error message.


Determines if the provided interaction ID exists in our backend. If successful, it will return data about the relevant Discord user for displaying on our frontend. Failure will result in an error message.


Discord-specific endpoint used for all Discord bot commands generated through a Discord server.


Used for pinging uptime of the server.

Relevant Sign Protocol Code Examples

Querying for attestations and parsing the response:

const endpoint = `${ATTESTATION_ENDPOINT}/index/attestations`;
// Use fetch() or axios to call the Sign Protocol indexing service:
const res = await AttestationRequest(
    method: "GET",
    // If we have an attester provided in the Discord Server's registration config,
    // we need to use it as the attester when querying. Otherwise, the recipient
    // (the address belonging to the user trying to verify themselves in Discord)
    // will be our attester. Use the recipient as the indexingValue (provided when
    // making an attestation) to query for the recipient when an attester is provided.
    params: attester
      ? {
          mode, // onchain/offchain
          schemaId, // {mode}_{chainType}_{chainId}_{id}
          attester: attester, // an address
          indexingValue: recipient.toLowerCase() // an address
      : {
          mode, // onchain/offchain
          schemaId, // {mode}_{chainType}_{chainId}_{id}
          attester: recipient // an address

// Begin parsing the response from the indexing server.
if (!response.success) {
  return { success: false, message: response?.message ?? "Attestation query failed." };

// Check if there is no data matching our query.
if ( === 0) {
  return { success: false, message: "No attestation for this address found." };

// Do something with the attestations found. We will "return" them in this example.
return {
  success: true,

Parsing the attestation data into a JSON Object:

import { decodeAbiParameters } from "viem";

// List of attestations returned from indexing service.
const attestations = [...]; 

let dataObjects: any = [];
for (const att of attestations) {
  // No data to parse.
  if(! continue;
  // Use viem's decodeAbiParameters() to parse the data.
  // Note that this step may require slight modifications depending on the schema's
  // data type, such as nested tuples or parsing pure strings.
  const data = decodeAbiParameters(
    att.dataLocation === "onchain" ? : [{ type: "string" }],
  // Create a data object from the schema.
  const obj: any = {};
  data.forEach((item: any, i: number) => {
    obj[[i].name] = item;
  // Add the obj to dataObjects.

// Do something with the data objects we have recovered and populated in dataObjects.
// We can use the attestation data to determine if a user should be allowed into a
// Discord server based on some verification criteria, such as requiring an
// attestation's data to contain a field named "Result" with a value of "true".

Last updated