Non-transferable Badges for Maker Ecosystem Activity (CDIP 18).
We propose an incentivization mechanism to enhance activity on MakerDAO Ecosystem.
A user can activate a Badge for activities on MakerDAO Ecosystem that are checked both on-chain for real-time activities (eg. voting on a Governance Poll) and off-chain via TheGraph for historical data (events) and stored on a merkle tree verified on-chain.
The user gets a Badge, a proof of its activities on Maker, that is Non-transferable, that means that Is the only owner of the Badge, in particular when activating the Badge he mint a new Non-transferable Non Fungible Token that can eventually be burned by himself, the owner.
Smart Contract Building Blocks:
InsigniaDAO
InsigniaDAO to check for activities on MakerDAO ecosystem and keep track of redeemers
To enable InsigniaDAO to check on-chain for activities on MakerDAO ecosystem we are using three interface to map the functions that we'll use:
Pot: to check if a user has accrued 1 or more Dai from DSR, via pie(address guy), chi(), rho() and drip() used in the internal function _dai(address guy) to return the wad or the current accrued Dai interest in DSR. DSChief: to check if a user is voting on a Governance Poll via votes(address) a getter function to check who is currently voting. Flipper: to check for high bidder in the current Bid in Collateral Auctions via bids(id) a getter function of current Bid on Flipper to check for bids(id).guy the high bidder. The function checkRedeemer(uint id) will check on-chain for the previous activities on MakerDAO and will store the hash of the caller address, casted in address type, into the OpenZeppelin EnumerableSet.AddressSet redeemers that will be verified in BadgeFactory via verify(address guy) function linked to it, to allow a redeemer to activate a Non-transferable Badge.
InsigniaDAO, let the owner to set (via setRootHashes(bytes[]) memory rootHashes) an array of root hashes, called roots, ordered by template Id to allow redemeers checked off-chain for activities via TheGraph on the front-end, and stored into a Merkle Tree, to activate Badge. The getter function roots(uint templateId) is then linked to BadgeFactory and checked via OpenZeppelin MerkleProof.sol verify() function.
The contract also inherits OpenZeppelic AccessControl.sol to set the Pauser role to the owner of the contract that can pause(), unpause() functions in case of emergency (Circuit Breaker Design Pattern).
In order to integrate OpenGSN, InsigniaDAO inherits BaseRelayRecipient.sol and do the following changes:
msg.sender is replaced by _msgSender(). trustedForwarder is set in the constructor with the address deployed on Kovan the following function is added to override OpenZeppelin Context _msgSender(): function _msgSender() internal override(Context, BaseRelayRecipient) view returns (address payable) { return BaseRelayRecipient._msgSender(); }
BadgeRoles
BadgeRoles Access Management for Default Admin, Templater and Pauser Role
BadgeRoles inherits the OpenZeppelin AccessControl.sol, allowing the owner of the contract to be set as Default Admin, Pauser and also as Templater and to add a Templater via addTemplater(address guy).
In order to integrate OpenGSN, BadgeRoles inherits OpenGSN BaseRelayRecipient.sol and do the following changes:
msg.sender is replaced by _msgSender(). trustedForwarder is set in the constructor with the address deployed on Kovan the following function is added to override OpenZeppelin Context _msgSender(): function _msgSender() internal override(Context, BaseRelayRecipient) view returns (address payable) { return BaseRelayRecipient._msgSender(); }
BadgeFactory
BadgeFactory to manage Templates and activate Non-transferable Badges for redeemers
To enable BadgeFactory to verify redeemers checked on-chain/off-chain for activities on MakerDAO ecosystem, when they try to redeem their Badge, we're using the interface InsigniaDAO to map the function we'll use.
In particular, we'll use:
verify(address guy) to verify redeemers checked on-chain. roots(uint templateId) a getter function to return root by templated Id to be verified via MerkleProof.sol verify() function, allowing redeemers checked off-chain and stored into a Merkle Tree to be able to redeem Badges. A Merkle Tree is generated for every Template and the root hash is updated by owner of InsigniaDAO daily to allow batches of redeemers to be checked and to redeem Badges.
BadgeFactory inherits BadgeRoles, allowing a Templater to create a new template via createTemplate() specifying name, description and image.
It also inherits ERC721Burnable, where the _transfer() has been overridden to implement Non-transferable feature, allowing redeemers checked on-chain/offchain to redeem a Badge for a specific activity on MakerDAO ecosystem via activateBadge() that will verify if the caller is a redeemer and then will allow the caller to mint a new Non-transferable Badge with tokenURI stored on IPFS (eg. "ipfs.json"). The owner of the Badge can then burn it eventually via burnBadge(uint tokenId) specifying the token Id of the Badge.
During deployment the contract sets the following ERC721 metadata:
name: "InsigniaBadges" symbol: "BADGES" baseURI: "https://badges.makerdao.com/token/" In order to integrate OpenGSN, BadgeFactory inherits BadgeRoles and add the following function to override the _msgSender() in OpenZeppelin Context and in BadgeRoles:
function _msgSender() internal override(Context, BadgeRoles) view returns (address payable) { return BaseRelayRecipient._msgSender(); }
BadgePaymaster
BadgePaymaster to pay for user's meta-transactions
In order to pay for user's meta-transaction BadgePaymaster inherits OpenGSN BasePaymaster.sol and implement the following functions:
acceptRealyedCall() preRelayedCall() postRelayedCall() Once deployed, BadgePaymaster owner need to set RelayHub contract address via setRelayHub(IRelayHub hub) that can be found on Kovan.
Finally the owner just need to fund the contract sending ether to BadgePaymaster contract address and the balanced will be automatically updated in RelayHub contract.
The project is divided into Smart Contracts and Front-end/Back-end
Smart Contracts:
The Project is a Truffle project, that uses Ganache CLI to deploy locally and Infura and Truffle HD Wallet Provider to deploy on Kovan.
The Smart Contracts inherit OpenZeppelin v3.0.1 to extend the behaviour of ERC721Burnable.sol (NFT), to allow to implement Non-transferable Badges by overriding _transfer() function and using a function called _mintWithTokenURI to mint the NFT using a tokenURI (eg. ipfs.json).
The Contracts interact with MakerDAO Ecosystem to check on-chain for real-time activities on Maker Ecosystem, like to check if someone is currently voting on a Governance Poll of if is an high bidder in a Bid in Collateral Auctions or if he has accrued 1 o more Dai interest in DSR.
Also the Smart Contracts inherit OpenGSN v0.9.0 to implement meta-transactions, by using the _msgSender() function and set a trustedForwarder. Also, the project includes a BadgePaymaster that after being funded takes care to pay for gas.
For the HackMoney we launched the fist release on kovan (v0.1.0) and the contracts are monitored via Tenderly and Infura Stats.
Front-end:
The UI starts by forking uniswap-frontend, and adding the badges and design for MakerDAO, integrating Portis for wallet login.
Is deployed on IPFS via Fleek and resolved via ENS subdomain.
To interface with the contracts ABIs it uses ethers.js and OpenGSN.
Back-end:
The Merkle Service on the Back-end keep track of redeemer checked off-chain for historical data (events) via TheGraph and then stores the hashes of addresses into a merkle tree verified on-chain via MerkleProof.sol by OpenZeppelin when a redeemer activate a Badge.