-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
4,426 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
{ | ||
"solidity.compileUsingRemoteVersion": "v0.5.2+commit.1df8f40c" | ||
"solidity.compileUsingRemoteVersion": "v0.5.2+commit.1df8f40c", | ||
"solidity.enabledAsYouTypeCompilationErrorCheck": false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,63 @@ | ||
# The Relevant Token | ||
# The Relevant Token Smart Contracts | ||
|
||
This repository contains the code for REL, an ERC20 token used by [Relevant](https://relevant.community) to reward content curation. | ||
This repository contains smart contracts used by [Relevant APP](https://relevant.community) to reward content curation. | ||
|
||
## Key Contract Parameters | ||
## RelevantTokenV3.sol | ||
|
||
Users of the Relevant App are able to earn rewards for curating tokens. These rewards are generated by minting new REL tokens. REL token `admin` distributes the tokens as balances in the web2 database of the Relevant App. Users can then `claim` these tokens by requesting an attestation by the `admin` account and submitting it the REL smart contract. | ||
|
||
There is currently a limit to how many tokens can be claimed this way by any individual account. Balances over the limit can be claimed as vested `sRel` tokens. These tokens are not transferrable (for the most part) but can otherwise be used for governance and within the app. | ||
|
||
### Key Contract Parameters | ||
|
||
- `inflation` is the yearly APR in basis points. | ||
- `allocatedRewards` are the tokens that still belong to the smart contract but are allocated to users as Curation rewards. These tokens can either be claimed or vested by the users. | ||
- `admin` is a designated hot wallet address that signs user's token claims. Security note: if this account is compromised the attacker can drain the `allocatedRewards` but not more. This address should be rotate periodically by 'owner'. | ||
|
||
## Key Methods | ||
### Key Methods | ||
|
||
- `releaseTokens` can be called by anyone to either mint or allocate existing tokens. If there are enough tokens in the smart contract that are not part of the `allocatedRewards`, they will be used, otherwise new tokens will be minted. | ||
- `claimTokens` can be used by users to claim REL from `allocatedRewards` | ||
|
||
## Owner Methods | ||
### Owner Methods | ||
|
||
- `vestAllocatedTokens` sends a portion of the `allocatedTokens` to a vesting smart contract. This method should be called bafore initializing any specific vesting account. | ||
- `updateAllocatedRewards` updates `allocatedRewards` - we may need to do this to ensure app rewards match up with allocated rewards. | ||
- `setInflation` sets yearly inflation rate in basis points. | ||
- `setAdmin` updates the token claim signer. | ||
- `burn` can burn an amount of tokens that are not part of the `allocatedRewards`. We might want to do this in the future to simplify accounting. Note: yearly inflation rate should be adjusted if a significant number of tokens is burned. | ||
|
||
# Relevant Protocol Governance Smart Contracts | ||
|
||
## sREL token | ||
|
||
sREL is governance wrapper for REL tokens and allows staking and vesting. | ||
|
||
### Roles | ||
|
||
- `owner` (this will eventually be a DAO) | ||
- can initialize initial vesting accounts | ||
- can set `vestAdmin` role | ||
- `vestAdmin` is designed to be a hotwallet that allows automated vesting initialization via the Relevant App | ||
|
||
### Staking | ||
|
||
- REL tokens can be staked via the contract in exchange for sRel | ||
- sRel cannot be transferred or exchanged back to REL unless they are 'unlocked' (unstaked) and unvested | ||
- `lockPeriod` deterimines the time it takes to unstake the tokens | ||
|
||
### Vesting | ||
|
||
- Vested tokens can be added by the `owner` of the contract or via a signature from the `vestAdmin' | ||
- There are two vesting schedules - short and long, exact params TBD, likely 4 and 16 years respectively | ||
- The params are global - meant to distribute a set amount of tokens to users | ||
- Vested tokens can be used to cast governance votes | ||
- The full amount of vested tokens can be transferred to a new account | ||
|
||
## Governor | ||
|
||
Openzeppelin Governor contract that can self-modify the `votingPeriod`, `proposalThreshhold` and `votingDelay`. | ||
|
||
## Timelock | ||
|
||
Openzeppelin Timlock that will add a delay to all governance decisions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const { network } = require('hardhat') | ||
const { setupAccount } = require('../test/utils') | ||
const { upgradeRel, initV3 } = require('../scripts/upgradeRel') | ||
|
||
module.exports = async ({ getNamedAccounts, deployments }) => { | ||
const { proxyAdmin, relOwner, relAdmin } = await getNamedAccounts() | ||
|
||
// this step should be done manually on the mainnet via upgrade script | ||
if (network.name == 'hardhat') { | ||
const proxyAdminSigner = await setupAccount(proxyAdmin) | ||
await upgradeRel(proxyAdminSigner) | ||
|
||
const relOwnerSigner = await setupAccount(relOwner) | ||
await initV3(relOwnerSigner, relAdmin) | ||
} | ||
} | ||
module.exports.tags = ['Rel'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module.exports = async ({ getNamedAccounts, deployments }) => { | ||
const { deploy } = deployments | ||
const { deployer } = await getNamedAccounts() | ||
|
||
await deploy('Utils', { | ||
from: deployer, | ||
}) | ||
} | ||
|
||
module.exports.tags = ['Utils'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
const OZ_SDK_EXPORT = require('../openzeppelin-cli-export.json') | ||
|
||
const getVestingParams = () => { | ||
const vestBegin = Math.round(new Date('10.01.2021').getTime() / 1000) | ||
const vestShort = vestBegin + 4 * 365 * 24 * 60 * 60 | ||
const vestLong = vestBegin + 16 * 365 * 24 * 60 * 60 | ||
return [vestBegin, vestShort, vestLong] | ||
} | ||
|
||
module.exports = async ({ getNamedAccounts, deployments }) => { | ||
const [RelevantToken] = OZ_SDK_EXPORT.networks.mainnet.proxies[ | ||
'REL/RelevantToken' | ||
] | ||
|
||
const utils = await deployments.get('Utils') | ||
const { deploy } = deployments | ||
const { deployer, vestAdmin } = await getNamedAccounts() | ||
|
||
await deploy('sRel', { | ||
from: deployer, | ||
args: [RelevantToken.address, vestAdmin, ...getVestingParams()], | ||
libraries: { Utils: utils.address }, | ||
log: true, | ||
}) | ||
} | ||
|
||
module.exports.tags = ['sRel'] | ||
module.exports.dependencies = ['Utils'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const { constants } = require('ethers') | ||
const { AddressZero } = constants | ||
|
||
module.exports = async ({ getNamedAccounts, deployments }) => { | ||
const { deploy } = deployments | ||
const { deployer } = await getNamedAccounts() | ||
|
||
const minDelay = 60 * 60 * 24 * 4 | ||
|
||
await deploy('RelTimelock', { | ||
from: deployer, | ||
args: [minDelay, [], [AddressZero]], | ||
log: true, | ||
}) | ||
} | ||
|
||
module.exports.tags = ['RelTimelock'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module.exports = async ({ getNamedAccounts, deployments }) => { | ||
const { deploy } = deployments | ||
const { deployer } = await getNamedAccounts() | ||
|
||
const timelock = await deployments.get('RelTimelock') | ||
const sRel = await deployments.get('sRel') | ||
|
||
await deploy('RelGovernor', { | ||
from: deployer, | ||
args: [sRel.address, timelock.address], | ||
log: true, | ||
}) | ||
} | ||
|
||
module.exports.tags = ['Governance'] | ||
module.exports.dependencies = ['sRel', 'RelTimelock'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
const { ethers } = require('hardhat') | ||
const OZ_SDK_EXPORT = require('../openzeppelin-cli-export.json') | ||
|
||
const TIMELOCK_ADMIN_ROLE = | ||
'0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5' | ||
const PROPOSER_ROLE = | ||
'0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1' | ||
const EXECUTOR_ROLE = | ||
'0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63' | ||
|
||
module.exports = async ({ getNamedAccounts, deployments }) => { | ||
const [RelevantToken] = OZ_SDK_EXPORT.networks.mainnet.proxies[ | ||
'REL/RelevantToken' | ||
] | ||
|
||
const { deployer, relOwner } = await getNamedAccounts() | ||
const RelevantTokenV3 = await ethers.getContractFactory( | ||
'RelevantTokenV3', | ||
await ethers.getSigner(relOwner), | ||
) | ||
const rel = RelevantTokenV3.attach(RelevantToken.address) | ||
|
||
const timelock = await ethers.getContract('RelTimelock', deployer) | ||
const relGov = await ethers.getContract('RelGovernor', deployer) | ||
const sRel = await ethers.getContract('sRel', deployer) | ||
|
||
// REL OWNER | ||
const relTokenOwner = await rel.owner() | ||
if (relTokenOwner !== timelock.addres) { | ||
console.log('setting Timelock as owner of Rel', timelock.address) | ||
await rel.transferOwnership(timelock.address) | ||
} else { | ||
console.log('Timelock is owner of Rel', timelock.address) | ||
} | ||
|
||
// sREL OWNER | ||
const sRelOwner = await sRel.owner() | ||
if (sRelOwner !== timelock.addres) { | ||
console.log('setting Timelock as owner of sRel', timelock.address) | ||
await sRel.transferOwnership(timelock.address) | ||
} else { | ||
console.log('Timelock is owner of sRel', timelock.address) | ||
} | ||
|
||
// Timelock Proposer TODO - do it via constructor with deterministic deploys | ||
const govIsProposer = await timelock.hasRole(PROPOSER_ROLE, relGov.address) | ||
if (!govIsProposer) { | ||
console.log('Setting RelGovernor as timelock proposer') | ||
await timelock.grantRole(PROPOSER_ROLE, relGov.address) | ||
} else { | ||
console.log('RelGovernor is timelock proposer') | ||
} | ||
|
||
// Revoke timelock admin (only self-admin) | ||
const deployerIsAdmin = await timelock.hasRole(TIMELOCK_ADMIN_ROLE, deployer) | ||
if (deployerIsAdmin) { | ||
console.log('Renounce Deployer Admin Role') | ||
await timelock.renounceRole(TIMELOCK_ADMIN_ROLE, deployer) | ||
} else { | ||
console.log('Deployer is not Admin') | ||
} | ||
} | ||
|
||
module.exports.tags = ['Governance'] | ||
module.exports.dependencies = ['RelGovernor', 'Rel'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
31337 |
Oops, something went wrong.