Let's build a better(?) way to verify ownership

It may be time we built a new way to verify token ownership within Discord.

We’ve tried collab.land on the Nouns Discord and it works well for accounts that can sign messages but breaks down for multisig contracts or other accounts that can’t sign messages. Collab.land is attractive for large audiences when you don’t want each participant to have to pay gas for an on-chain transaction verifying ownership.

However, Noun ownership will remain a relatively small audience for the next few years (<365 Nouns created per year) and we’re already asking each Noun owner to periodically perform on chain actions when voting for proposals. I think this means that we can take an on-chain approach to verification. Since this is novel and I believe it could be useful to other organizations I’d like to collect some feedback before making a proof of concept implementation.

I’ll write generally in Projects rather than Nouns as I think this scheme can be made such that other projects could use the same on-chain data for their own attestation.

Overview

The model I’m thinking of has a few main components:

  • On-Chain Attestation Events - EVM Events emitted by a smart contract that allows for token holders to log a piece of data along with a verification that they hold a token
  • Token Transfers - EVM Events that are already emitted by ERC20 and ERC721 token contracts
  • (Optionally) The Graph - Providing more efficient event indexing
  • Role Management Bot - A bot that merges attestation events and Discord server membership to assign roles to token holders

overview

Attestation

I’m thinking of a couple sets of generic Attest events that are emitted by a smart contract. These events would include the token address, the address doing the attesting, and a 256 bit word for a hash of arbitrary data. Events are eventually pruned by Ethereum full nodes making their cost much cheaper but can be kept on archive nodes or otherwise indexed.

event AttestErc20 (
  address indexed token,
  uint256 indexed attestationType,
  address from,
  uint256 hashedData
);

Projects could then prompt their token holders to hash their data using Keccak hashing with an optional salt. A salt would allow token participants to put their data on chain without being as easily brute forced using a lookup table. This could be provided via an API endpoint provided by the Project in order to keep the salt a secret. In our use case this would be a Discord username which could later be recovered by hashing every server member’s username concatenated with a secret salt.

attestation

This salted and hashed data would then be sent to a smart contract that verifies that the transaction sender holds a non-zero amount of the specified ERC20 token or owns a specific ID of a specified ERC721 token. If the balance check passes, the smart contract would emit an EVM Event that can be indexed by consumers.

The contract would look sort of like the following:

/**
 * This is psudo code only, it hasn't been tested,
 * reviewed, or fully developed.
 */

contract TokenAttestation {
  event AttestErc20 (
    address indexed token,
    uint256 indexed attestationType,
    address from,
    uint256 hashedData
  );
  
  event AttestErc721 (
    address indexed token,
    uint256 indexed attestationType,
    address from,
    uint256 id,
    uint256 hashedData
  );

  function erc20Verify (address token, uint256 attestationType, uint256 hashedData) public {
    IERC20 erc20 = IERC20(token);
    if (erc20.balanceOf(msg.sender) > 0) {
        emit AttestErc20(token, attestationType, msg.sender, hashedData);
    }
  }

  function erc721Verify (address token, uint256 attestationType, uint256 id, uint256 hashedData) public {
    IERC721 erc721 = IERC721(token);
    if (erc721.ownerOf(id) == msg.sender) {
        emit AttestErc721(token, attestationType, msg.sender, id, hashedData);
    }
  }

}

Verification

One of the possible consumers - and our use case - would be a Discord bot that periodically fetches all of the on-chain attestations, token transfers, and Discord server members. It would then remove any outdated attestations and transfers (events that predate transfers of NFTs or balances). The set of attestations would then be used to filter the list of server members to find token holders. Any mismatch between the two lists would be where the Discord bot would add/remove Roles.

This is an early draft but I want to share it early to see if anyone has any useful input on how the system could work.

1 Like

I’ve realized I’ve missed a couple points:

Attestation Type

Each event contains an attestationType that is indexed, this would mean that the same contract can be used for different attestation types. This could be other Projects but would also allow us to do different platforms like Discourse in addition to Discord; eg 1 for Nouns Discord, 2 for Nouns Discourse, etc.

Rate Limiting

It’s possible to submit many events that are technically valid but have useless data in an attempt to increase the load on any verification systems. A way to combat this could be to have a rate limit per sender but that would increase gas costs and I’m not sure if the cost of the above would already discourage spam.

Clear Attestation Event

A user may want to forfeit or clear any attestations without having to perform a token transfer, having an event to do this may be useful. Obviously this wouldn’t remove the data from chain but it would be a way for a participant to remove an attestation when the attestation set is projected through.