mirror of https://github.com/nucypher/nucypher.git
96 lines
8.2 KiB
ReStructuredText
96 lines
8.2 KiB
ReStructuredText
NuCypher contracts
|
|
========================
|
|
* `NuCypherToken` contract is the ERC20 token contract with additional function - burn own tokens (only for owners)
|
|
* `MinersEscrow` contract holds Ursula's stake, stores information about Ursulas activity and assigns a reward for participating in NuCypher network. The `Issuer` contract is part of the `MinersEscrow` and uses only to split code
|
|
* `PolicyManager` contract holds policies fee and distributes fee by periods
|
|
* `Upgradeable` is base contract for upgrading (<nucypher.blockchain.eth/project/contracts/proxy/README.MD>)
|
|
* `Dispatcher` contract is used as proxy to other contracts. This provides upgrading of the `MinersEscrow` and `PolicyManager` contracts
|
|
* `UserEscrow` contract locks tokens for some time. Tokens will be unlocked after specified time and all tokens can be used as a stake in the `MinersEscrow` contract
|
|
|
|
Deployment
|
|
========================
|
|
* The first place is the contract `NuCypherToken` with all future supply tokens
|
|
* Next `MinersEscrow` should be deployed with its dispatcher
|
|
* Similarly `PolicyManager` is deployed with own dispatcher
|
|
* Transfer reward tokens to the `MinersEscrow` contract. This tokens is reward for mining. The remaining tokens are initial supply
|
|
* Run the `initialize()` method to initialize the `MinersEscrow` contract
|
|
* Set the address of the `PolicyManager` contract in the `MinersEscrow` by using the `setPolicyManager(address)`
|
|
* Pre-deposit tokens to the `MinersEscrow` if necessary:
|
|
* Approve the transfer tokens for the `MinersEscrow` contract using the `approve(address, uint)` method. The parameters are the address of `MinersEscrow` and the amount of tokens for a miner or group of miner;
|
|
* Deposit tokens to the `MinersEscrow` contract using the `preDeposit(address[], uint[], uint[])` method. The parameters are the addresses of token miner, the amount of token for each miner and the periods during which tokens will be locked for each miner
|
|
* `UserEscrowLibraryLinker`, `UserEscrowProxy` TBD
|
|
* Pre-deposit tokens to the `UserEscrow` if necessary:
|
|
* Create new instance of the `UserEscrow` contract
|
|
* Transfer ownership of the instance of the `UserEscrow` contract to the user
|
|
* Approve the transfer tokens for the `UserEscrow`
|
|
* Deposit tokens by the `initialDeposit(uint256, uint256)` method
|
|
|
|
Miner / Ursula
|
|
========================
|
|
In order to become a participant of the network, a miner should stake tokens in the `MinersEscrow` contract.
|
|
For that, the miner allows the (mining) contract to do a transaction using the `approve(address, uint256)` method in the token contract
|
|
(ERC20 contracts allow to delegrate access to another address).
|
|
After that, the miner transfers some quantity of tokens (method `deposit(uint256, uint256)`), locking them at the same time.
|
|
Another way to do it is using the `approveAndCall(address, uint256, bytes)` method in the token contract.
|
|
The parameters are the address of the `MinersEscrow` contract, the amount of staked tokens and the periods for locking which are serialized into an array of bytes.
|
|
|
|
When staking tokens, the miner sets the number of periods while tokens will be locked, but it should be no less than some minimal locking time (30 periods).
|
|
In order to unlock tokens, the miner should be active during the time of locking (confirm activity).
|
|
Each stake is the amount of tokens and the duration in periods.
|
|
The miner can add new stake by the `deposit(uint256, uint256)` or `lock(uint256, uint256)` methods.
|
|
Also the miner can split stake into two parts: one with the same duration and other with an extended duration.
|
|
For this purpose, the `divideStake(uint256, uint256, uint256, uint256)` method is used.
|
|
The first two parameters are used to identify the stake to divide and the others two for the extended part of the stake.
|
|
When calculating locked tokens (`getLockedTokens(address, uint256)` method), all stakes that are active during the specified period are summed up.
|
|
|
|
In order to confirm activity every period, the miner should call `confirmActivity()` in the process of which activity for the next period is registered.
|
|
Also the method `confirmActivity` is called every time when methods `deposit(uint256, uint256)` or `lock(uint256, uint256)` are called.
|
|
The miner gets a reward for every confirmed period.
|
|
After the period of activity has passed, the miner could call `mint()` method which computes and transfers tokens to the miner's account.
|
|
Also, the `lock(uint256, uint256)` and `confirmActivity()` methods include the `mint()` method.
|
|
|
|
The reward depends on the fraction of locked tokens for the period (only those who confirmed activity are accounted for)
|
|
Also, the reward depends on the number of periods during which the tokens will be locked: if the tokens will be locked in half a year, the coefficient is 1.5.
|
|
Minimal coefficient is 1 (when tokens will get unlocked in the next period), and maximum is 2 (when the time is 1 year or more).
|
|
The reward is calculated separately for each stakes that are active during the mining period and all rewards are summed up.
|
|
The order of calling `mint` by miners (e.g. who calls first, second etc) doesn't matter.
|
|
All reward the miner can get by using the `witdraw(uint256)` method. Only non-locked tokens can be withdrawn.
|
|
|
|
Also the miner gets rewards for policies deployed.
|
|
Computation of the reward happens every time `mint()` is called by the `updateReward(address, uint256)` method.
|
|
In order to take the reward, the miner needs to call method `withdraw()` of the contract `PolicyManager`.
|
|
The miner can set a minimum reward rate for a policy. For that, the miner should call the `setMinRewardRate(uint256)` method.
|
|
|
|
Some users will have locked but not staked tokens.
|
|
In that case, a instance of the `UserEscrow` contract will hold their tokens (method `initialDeposit(uint256, uint256)`).
|
|
All tokens will be unlocked after specified time and the user can get them by method `withdraw(uint256)`.
|
|
When the user wants to become a miner - he uses the `UserEscrow` contract as a proxy for the `MinersEscrow` and `PolicyManager` contracts.
|
|
|
|
Alice
|
|
========================
|
|
Alice uses the net of miners to deploy policies.
|
|
In order to take advantage of network Alice should choose miners and deploy policies with fees for that miners.
|
|
Alice can choose miners by herself or by `findCumSum(uint256, uint256, uint256)` method of the contract `MinersEscrow`.
|
|
The parameters are the start index (if the method is not called the first time), delta of the step and minimum number of periods during which tokens are locked.
|
|
This method will return only active miners.
|
|
|
|
In order to place fee for policy Alice should call method `createPolicy(bytes20, uint256, uint256, address[])` of the contract `PolicyManager`
|
|
by specifying the miners addresses, the policy id (off-chain generation), duration in periods, first period reward.
|
|
Payment should be added in transaction in ETH and the amount is `firstReward * miners.length + rewardRate * periods * miners.length`.
|
|
Reward rate must be equal or more than minimum reward for each miner in the list. First period reward can not be refundable and it can be zero.
|
|
|
|
In case Alice wants to cancel policy then she calls the `revokePolicy(bytes20)` or `revokeArrangement(bytes20, address)` methods of the contract `PolicyManager`.
|
|
While executing those methods Alice get all fee for future periods and for periods when the miners were inactive.
|
|
Also Alice can refund ETH for inactive miners periods without revoking policy by using methods `refund(bytes20)` or `refund(bytes20, address)` of the contract `PolicyManager`.
|
|
|
|
Upgrade
|
|
========================
|
|
Smart contracts in Ethereum are not really changeable.
|
|
So fixing bugs and upgrading logic is to change the contract (address) and save the previous storage values.
|
|
The `Dispatcher` contract is used for this purpose - the fallback function in contract will execute on any request,
|
|
redirect request to the target address (delegatecall) and return result value (using some opcodes).
|
|
A target contract should be inherited from the `Upgradeable` contract in addition to the use of the `Dispatcher`.
|
|
The `Upgradeable` contract include 2 abstract methods that need to be implemented:
|
|
`verifyState(address)` method which checks that new version has correct storage;
|
|
`finishUpgrade(address)` method which should copy initialization data from library storage to the dispatcher storage;
|