-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Documentation #50
base: develop
Are you sure you want to change the base?
Documentation #50
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
docs | ||
*.md |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Mocks | ||
|
||
This directory contains mocks of contracts used for testing purposes. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Upgrades | ||
|
||
This directory contrains **upgrade test** mock contracts. | ||
|
||
The contracts placed here **must** follow a pattern (in order to be properly picked up by the upgrade test harness): | ||
|
||
- Test contract names **must** match an existing upgradeable contract **exactly** | ||
- Test contract names **must** end in `V2` | ||
- eg. `Foo` -> `FooV2` | ||
- Test contracts **must** imlement a `checkUpgrade()` function that **must** return at least a confitrmation string (default expected value being `"OK"`) | ||
- Test contractss **should** reference all state in the contract, in the order is declared in the contract (including base contracts) | ||
|
||
If properly implemented, the test harness will be able to check if the upgraded contract retains state integrity after the upgrade. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# TokenDistro Contract | ||
|
||
This directory contains the `TokenDistro` contract. The `TokenDistro` contract handles the allocation of rewards by releasing them over time in the **GIVStream**. | ||
|
||
The contract is based on the original `TokenDistro` contracts initially provided by Dappnode. | ||
|
||
## Initialization | ||
|
||
During intialization the TokenDistro defines the follwing: | ||
- The token that is being distributed | ||
- Start time, cliff period and duration of the distribution | ||
- Initial percentage available to claim | ||
- If the admin can cancel an allocation | ||
|
||
The `msg.sender` assumes the inital `DEFAULT_ADMIN_ROLE` role used in the contract. | ||
|
||
## Acess Control | ||
|
||
The Token Distro contract utilizes Openzeppelin `AccessControl` roles. Two roles are recognized: | ||
|
||
1. `DEFAULT_ADMIN_ROLE` is essentally the owner of the TokenDistro. It is assigned to the initializer address. `DEFAULT_ADMIN_ROLE` can add **distributors**, set the start time and cancel allocations. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And assign tokens (add credit) to distributors. |
||
2. `DISTRIBUTOR_ROLE` are the only addresses allowed to allocate tokens for distribution through claims. This should usually be an address of a liquidity mining contract such as eg. `Unipool` or similar. | ||
|
||
## Assigning Distributors | ||
|
||
TokenDistro assumes that there exist distributors (contracts or EOAs) that will later be used to distribute tokens. There are for example: | ||
- The UniV3 staking incentive program | ||
- The `UnipoolDistributor` used for liquidity mining on Honeyswap an Balancer | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- The `GardenUnipoolDistirbutor` used for GIVGarden staking | ||
|
||
The `assign(address account, uint256 amount)` function will assign the given amount of tokens to an address. It must be caled by the `DEFAULT_ADMIN_ROLE`. The `DEFAULT_ADMIN_ROLE` may assign any address to be a Distributor if it has claimed a `DISTRIBUTOR` ROLE. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Allocating/claiming GIV | ||
|
||
When a recipient wants to claim her tokens, she is expected to interact with a deployed distributor contract. The logic of the distributor governshe how much the recipient is owed and at what time. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
In an example, a deployed `MerkleDistributor` checks if an address has a GIV merkle drop available. | ||
|
||
**IMPORTANT:** A distributor cannot allocate GIV to another `DISTRIBUTOR_ROLE`. This is to avoid tokens being trapped in a distribution contract. | ||
|
||
### Allocating GIV | ||
|
||
Before (or in the process of) claiming rewards, a registered distributor should call `allocate(address account, uint256 amount, bool claim)` with the address and the amount of GIV the recipient is entitled to. This will allocate the amount to the recipient where | ||
- One part can be claimed immediately as GIV | ||
- The rest is allocated to the GIVStream and is released over time | ||
|
||
The proportion of GIV that can be claimed is determined by the | ||
- Initial percentage of GIV that is set as claimable | ||
- Current block time (percentage of duration that is elapsed) | ||
- Amount of tokens already claimed | ||
|
||
The globally claimable amount of tokens is: | ||
``` | ||
/** | ||
* Function to get the total claimable tokens at some moment | ||
* @param timestamp Unix time to check the number of tokens claimable | ||
* @return Number of tokens claimable at that timestamp | ||
*/ | ||
function globallyClaimableAt(uint256 timestamp) | ||
public | ||
view | ||
override | ||
returns (uint256) | ||
{ | ||
if (timestamp < startTime) return 0; | ||
if (timestamp < cliffTime) return initialAmount; | ||
if (timestamp > startTime + duration) return totalTokens; | ||
|
||
uint256 deltaTime = timestamp - startTime; | ||
return initialAmount + (deltaTime * lockedAmount) / duration; | ||
} | ||
``` | ||
|
||
This determines the current unlocked amount of allocated tokens (at a given timestamp) as: | ||
``` | ||
uint256 unlockedAmount = (globallyClaimableAt(timestamp) * | ||
balances[recipient].allocatedTokens) / totalTokens; | ||
|
||
return unlockedAmount - balances[recipient].claimed; | ||
``` | ||
|
||
Only a registered distributor can assign tokens to be claimed to an address. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
NOTE: If the claim flag is set to `true` the distributor will also perform a claim (as seen in the *Claiming GIV* section). | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's worth to mention each distributor has a limited balance to allocate. The distributor balance can be increased by the |
||
### SendGIVbacks wrapper | ||
|
||
The `sendGIVbacks(address[] memory recipients, uint256[] memory amounts)` is a wrapper function that will allocate an amount of GIV tokens to a given number of recipients. It uses the underlying `_allocateMany` call. It will emit a `GivBacksPaid(address)` event that logs the function caller. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This is useful for tracking GIVBack payouts that are tracked off-chain and triggered on a regular basis. | ||
|
||
### Claiming GIV | ||
|
||
Anyone can initialte a claim by calling `claim` or `claimTo` functions. This will transfer any tokens that are available to be claimed to the recipient. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This way the recipient can access her GIVStream tokens regardless of the distributor. | ||
|
||
## Changing adress allocations | ||
|
||
The TokenDistro contract supports modifying the recipient of an allocation. This can be very useful if an address is compromised (invalid, lost keys etc.) This can be done by the recipient or (if enabled) by `DEFAULT_ADMIN_ROLE`. | ||
|
||
### Regular mode | ||
|
||
Any recipient can change the address to which she receives GIV allocations by calling `changeAddress(address newAddress)`. The new address **must not** be **in use**, that is it must not have any allocated (or claimed) tokens. | ||
|
||
This call emits a `ChangeAddress(address prevRecipient, address newRecipient)` event. | ||
|
||
**NOTE:** The `DISTRIBUTOR_ROLE` **cannot** change it's recipient address. | ||
|
||
When a recipient changes her address, she will be able to claim the remainder of her tokens from existing allocations. | ||
|
||
### Admin mode | ||
|
||
The `DEFAULT_ADMIN_ROLE` has an option to call the `cancelAllocation(address prevRecipient, address newRecipient)` function to change a recipient by force, buy only if the `cancellable` parameter was set to `true` on TokenDistro initialization. | ||
|
||
This call emits a `ChangeAddress(address prevRecipient, address newRecipient)` event. | ||
|
||
**NOTE:** The `DISTRIBUTOR_ROLE` recipient address **cannot** be changed. | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# Token Contracts | ||
|
||
This directory contains different **token contracts** used by the GIVEconomy. The contracts here are mainly used for reference and testing purposes, with the execption of the `UniswapV3RewardToken`. | ||
|
||
## UniswapV3 Reward Token | ||
|
||
This token is used as a bridge between `UniswapV3Staker` contract which handles the liquidity mining on Uniswap V3 and the `TokenDistro`. | ||
|
||
We must use this bridging token because the UniV3 incentive has no way to call `TokenDistro.allocate` when rewards are claimed. If the rewards in GIV are claimed directly from UniV3, there would be no way to enforce that the correct proportion of the claim is placed in the GIVStream. | ||
|
||
To accomplish this, the reward token contract maintains a fake balance that represent GIV that should be assigned and claimed from the TokenDistro. When a recipient withdraws rewards from the UniV3 incentive contract, the appropriate amount of reward tokens is "burned" and real GIV is allocated to the recipient via `TokenDistro.allocate`. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The token implements the ERC-20 standard in such a way to **only** allow interactions between the TokenDistro, GIV token and the UniV3 staker contract. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The contract implements `ownable` and is owned by the deployer address. | ||
|
||
### The `approve` and `allowance` functions | ||
|
||
The UniV3 incentive contract is the only account that is allowed to call transfer from, and it has `MAX_UINT256` allowance. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Approve is unused, but for interface compatibility always returns `true`. | ||
|
||
### The `transferFrom` function | ||
|
||
This function is called by the `UniV3Staker` contract when creating a new incentive: | ||
``` | ||
function createIncentive(IncentiveKey memory key, uint256 reward) external override { | ||
... | ||
|
||
TransferHelper.safeTransferFrom( | ||
address(key.rewardToken), | ||
msg.sender, | ||
address(this), | ||
reward | ||
); | ||
|
||
... | ||
} | ||
``` | ||
|
||
The `transferFrom` call will only succeed if: | ||
- Is called by the UniV3 staker contract | ||
- Is `from` the contract `owner` | ||
- Is `to` the the UniV3 staker contract | ||
|
||
For the `createIncentive` to succeed, the caller **must** be the `owner` of the contract. | ||
|
||
The transferFrom will "mint" the tokens that can later be allocated in the TokenDistro by calling transfer. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
### The `transfer` function | ||
|
||
This function is called by the `UniV3Staker` when a recipient claims rewards: | ||
``` | ||
function claimReward( | ||
IERC20Minimal rewardToken, | ||
address to, | ||
uint256 amountRequested | ||
) external override returns (uint256 reward) { | ||
... | ||
|
||
TransferHelper.safeTransfer(address(rewardToken), to, reward); | ||
|
||
... | ||
} | ||
``` | ||
To succeed, `transfer` **must** be called by the UniV3 staker contract. | ||
|
||
When called, this function will "burn" the claimed amount of reward tokens and then call `TokenDistro.allocate`. The `allocate` call will allocate actual GIV tokens and perform a claim. | ||
paxthemax marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This function emits a `RewardPaid(uint256 amount, address to)` event. | ||
|
||
## GIV Token | ||
|
||
This is the GIV token contract that is deployed to both Ethereum mainnet and Gnosis chain. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the GIV token only deployed on Mainnet. The Genosis chain one is created by bridge! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! I almost missed that part. |
||
|
||
GIV is a lightweight ERC-20 token modeled after the [uniswap implementation](https://github.com/Uniswap/v2-core/blob/v1.0.1/contracts/UniswapV2ERC20.sol) with some modifications: | ||
|
||
- It has exposed `mint` and `burn` functions | ||
- With associated `minter` role | ||
- Total supply is uncapped | ||
- [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) `transferWithAuthorization` | ||
- `DOMAIN_SEPARATOR` is computed inside `_validateSignedData` to avoid reply-attacks due to Hardforks | ||
- Forbids transfers to the contract and address(0) | ||
- This is to avoid losing tokens in the contract and to enforce burn events | ||
|
||
**NOTE:** The actual GIV token is not meant to be deployed from the GIVEconomy repo, and was deployed separately before mainnet launch. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The GIV Token on mainnet is deployed by the current version ofGIV.sol. @paxthemax is it what you mean? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Uniswap V3 Staker Contracts | ||
|
||
This is the cannonical Uniswap V3 Staker staker contract, sourced directly from the [uniswap repo](https://github.com/Uniswap/v3-staker/tree/main/contracts). | ||
|
||
The contract source is not modified in any way, and is only included | ||
- To provide bytecode and ABI to reference during deployment | ||
- To use in testing | ||
|
||
Uniswap V3Staker is deployed to Ethereum **mainnet**. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Documentation | ||
|
||
This directory will contain **documentation** for the GivEconomy contracts. | ||
|
||
See `NOTES.md` for a general overview of contrats. | ||
|
||
TODO: add diagrams |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
@startuml "Merkle Distro claim" | ||
|
||
actor Recipient as caller | ||
participant MerkleDistro as merkle | ||
participant TokenDistro as distro | ||
participant GIVToken as token | ||
|
||
caller -> merkle : isClaimed(index) | ||
activate merkle | ||
|
||
merkle --> caller : bool | ||
deactivate merkle | ||
|
||
caller -> merkle : claim(index, amount, proof) | ||
activate merkle | ||
|
||
alt proof is invalid | ||
merkle --> caller : revert | ||
destroy merkle | ||
else | ||
merkle -> distro: allocate() | ||
|
||
activate distro | ||
note right: set allocation | ||
|
||
alt claim | ||
distro -> distro : claim() | ||
activate distro | ||
|
||
distro -> token: safeTransfer() | ||
activate token | ||
|
||
token --> caller: transfer tokens | ||
|
||
token --> distro: OK | ||
deactivate token | ||
deactivate distro | ||
end | ||
|
||
distro --> merkle: OK | ||
deactivate distro | ||
|
||
merkle -->caller : OK | ||
deactivate merkle | ||
end | ||
|
||
@enduml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be rephrased.