This document is inteded to help people understand the various ways one can mint a badge using the Otterspace protocol.
There are 4 ways to mint an otterspace badge using our protocol
- Mintlist flow (with single issuance)
- Mintlist flow with bulk issuance (via Merkle Trees)
- Request flow
- 2 party consented model
- Single party consented model
- Airdrop flow
In order to help you decide what flow to use for your app, the following questionnaire should help
- Do you plan to issue consented badges?
- If No, then you should use the airdrop flow. We generally recommend establishing consent with the account before sending them a non-transferable token
- If Yes, then you either need to use the Mintlist flow or Request flow. See questions below.
- Do you want to allowlist account(s) before they can claim it?
- If Yes, you should implement it the Mintlist flow. You can allow 1 or many accounts at once. Allowlisting many accounts at once use the merkle tree approach, see below sections for details
- Do you want users to request a badge that you already created?
- If Yes, then the Request flow is the right approach
- Depending on your use case, please see the sections below to determine if a single or 2-party consented model is right for you
- Do you want to just airdrop badge(s) to accounts?
- Use the airdrop flow
Recommended reading
Firstly, familiarize yourself with the quickstart
As a pre-requisite, it is important to understand how Otterspace uses an NFT called RAFT tokens, in otder to represent orgs and capture administrative authorization over their Otterspace account.
Additionally in order to mint a badge, it’s “Badge Spec” must be registered by that admin. A Badge Spec is generally associated to a raft, on-chain.
The follow contract function can be used to create and register a spec. Note that only a Raft Token Holder or a pre-assigned Admin of that Raft is authorized to create a spec for that Raft a.k.a Community.
function createSpec(string calldata _specUri, uint256 _raftTokenId);
Mintlist flow (with single issuance)
This flow leverages with a 2-party consent between the issuer (an admin from the community) and the claimant (a user who wants to mint the badge).
The consent is established via a signature from the issuer authorizing that a registered spec (denoted with the Uri) with the following type hash using EIP-712.
bytes32 constant AGREEMENT_HASH =
keccak256("Agreement(address active,address passive,string tokenURI)");
We refer to this issuance of a signed badge spec a “Voucher”. This is particularly useful in the case of when an issuer wants to issue a single voucher only.
Then the following contract function will need to be called by the Claimant to mint the signed badge into their account
function take(
address _from,
string calldata _uri,
bytes calldata _signature
) external virtual override returns (uint256);
Please note, using this flow, if a Raft token is transferred after already signing the Voucher, the minting will fail as the original issuer is no longer an authorized admin of that Community. Since the Agreement typehash above is a signature capturing both issuer(passive) and claimant(active), it is only valid as long the issuer is an active admin or Raft token holder
With bulk issuance (via Merkle Trees)
This flow leverages with a 2-party consent between the issuer and a list of claimants represented captured as a merkle root.
Similar to the flow above single issuance, the following typehash is signed by the issuer. The list of claimant addresses are captured as a merkle root.
bytes32 constant MERKLE_AGREEMENT_HASH =
keccak256("MerkleAgreement(address passive,string tokenURI,bytes32 root)");
For vouchers issued with this typehash, the following contract function called by the claimant will mint the badge to their account. Note that the proof here is the merkle proof for the claimant address as the leaf node in the merkle tree
function merkleTake(
address _from,
string calldata _uri,
bytes calldata _signature,
bytes32 root,
bytes32[] calldata proof
);
Please note: If a Raft token is transferred after signing the Voucher, the minting will fail since the original issuer is no longer an authorized admin of the Community. The MerkleAgreement typehash is a signature that captures the issuer (passive). It is only valid while the issuer is an active admin or Raft token holder.
Request flow
If a badge already exists, the request flow should be used when a user requests it via an EIP-712 signature. An admin of the community can then approve the request and mint the badge to the requester.
There are 2 ways how this flow can be implemented
- 2-party consented vouchers
- Single party consented vouchers
Using 2-party consented vouchers
The consent is established via a signature from the requester authorizing their intent to own the badge. The following typehash is used where the requester in the “passive” address.
bytes32 constant AGREEMENT_HASH =
keccak256("Agreement(address active,address passive,string tokenURI)");
This approach presumes that the requester is aware of the issuer (active) is and signs their consent expressing that only that issuer may mint this badge to them. Typically, this tends to be the Raft holder or one of the registered admins on the Raft.
The issuer then would need to call the following contract function in order to mint the badge to the requester.
function give(
address _to,
string calldata _uri,
bytes calldata _signature
) external virtual returns (uint256);
Using single party consented vouchers
Sometimes it is necessary to request a badge without caring about who an active admin of that Raft is at the time of mint, as long as they are an active admin at that time. For these use cases, the following typehash is used for signing to estlabish a single party consent
bytes32 constant REQUEST_HASH =
keccak256("Request(address requester,string tokenURI)");
In order to mint a requested badge that was signed using the above typehash, the following contract function must be called.
function giveRequestedBadge(
address _to,
string calldata _uri,
bytes calldata _signature
) external virtual returns (uint256);
Using the airdrop flow
This is a classic way to mint a badge and transfer it to a destination account address. However, it is important to note that the token being transferred is non-consentable and non-transferable. We strongly recommend obtaining consent before transfers, but we understand that sometimes an airdrop is necessary.
function airdrop(address[] calldata _recipients, string calldata _uri);