Smart contract

Verification can be done directly from the smart contract using Chainlink Any API. This means that you don't need to setup any backend service.

Installation

Proofi supplies a solidity library for easy integration. It can be installed using NPM

npm install --save @proofi/solidity

Usage

The smart contract must implement the OpenZeppelin Ownable interface in addition to the Proofi Verification interface.

Use the Verification base contract of the Proofi library to implement all required logic, including calls through Chainlink Any API.

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@proofi/solidity/Verification.sol";

// This demo is an NFT that can only be minted by verified wallets.
// Minted NFTs are initially locked and unlocked on verification.
contract MyNFT is ERC721, Ownable, Verification {
  // ...
}

In this example, we're using an NFT contract. But the Verification class can be used for any type of smart contract.

Verification

Before a wallet is allowed to participate and interact with a smart contract (mint, swap, etc) it should be verified with an identity provider.

The website that's connected with Metamask, should have checked that the wallet is known to the identity provider using the isKnown() call, submitting a call to your smart contract.

When using the Verification class, the smart contract will keep a list of approved and declined wallet addresses. If the wallet is in neither of these lists call verify(), passing the message sending.

// When a token is minted verify it using the IdentityProvider.
// The token is reserved until verification is complete.
function mint(uint256 id) public {
    require(isMinted(id) == State.AVAILABLE, "token unavailable");
    require(!isDeclined(msg.sender), "wallet is declined");

    if (isApproved(msg.sender)) {
        _safeMint(msg.sender, id);
    } else {
        verify(msg.sender);
        _reserve(msg.sender, id);
    }
}

Verification is asynchronous. The Chainlink Any API request is an EVM event that's picked up by a Chainlink Oracle. This oracle does an HTTP request to the Proofi API and publishes the yes/no answer on the blockchain.

If the verify() function is called with a different address than the message sender, Proofi will likely refuse to give a response. Proofi checks the blockchain transaction to ensure that the wallet owner is knowingly participating in the project.

Hooks

There are two hooks that should be implemented in your smart contract: onApproved and onDenied. One of these functions will be called based on the response of the oracle.

// Hook from approved verification: unlock the tokens
function onApproved(address owner) internal override {
    mintReserved(owner);
}

// Hook from denied verification
function onDenied(address owner) internal override {
    cancelReservations(owner);
}

There are costs involved in doing ChainLink Any API calls. Fees for these calls need to be paid in LINK tokens. For testnet these tokens can be obtained from the Chainlink faucet.

Costs differ per Oracle and network. You're free to use any Oracle you like, as long as it supports the Get > Bool job. If you're unsure which oracle to use, have a look at Translucent.

To use Chainlink, you must transfer LINK tokens to your smart contract. You will not be able to transfer them back unless you add this functionality to your contract.

Example configuration

{
  "goerli": {
    "url": "https://app.proofi.com/api/verify/eip155:5/",
    "token": "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
    "oracle": "0xCC79157eb46F5624204f47AB42b3906cAA40eaB7",
    "jobId": "0xc1c5e92880894eb6b27d3cae19670aa3"
  }
}

Example

Krazy Kobe is a demo project where NFTs can only be minted by approved wallet owners.

Have a look at the solidity code in the repository or find the project on Etherscan.

Last updated