Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 793 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Make Cover Using... | 12315101 | 1777 days ago | IN | 0 ETH | 0.0259656 | ||||
| Make Cover Using... | 12315067 | 1777 days ago | IN | 0 ETH | 0.0227199 | ||||
| Make Cover Using... | 12315031 | 1777 days ago | IN | 0 ETH | 0.02380127 | ||||
| Expire Cover | 12313952 | 1777 days ago | IN | 0 ETH | 0.0072679 | ||||
| Expire Cover | 12313949 | 1777 days ago | IN | 0 ETH | 0.00938769 | ||||
| Expire Cover | 12313949 | 1777 days ago | IN | 0 ETH | 0.00854572 | ||||
| Expire Cover | 12311992 | 1777 days ago | IN | 0 ETH | 0.01133502 | ||||
| Make Cover Using... | 12311457 | 1777 days ago | IN | 0 ETH | 0.02442633 | ||||
| Expire Cover | 12311325 | 1777 days ago | IN | 0 ETH | 0.008894 | ||||
| Expire Cover | 12311322 | 1777 days ago | IN | 0 ETH | 0.00900784 | ||||
| Expire Cover | 12311321 | 1777 days ago | IN | 0 ETH | 0.0345062 | ||||
| Expire Cover | 12311313 | 1777 days ago | IN | 0 ETH | 0.04067915 | ||||
| Expire Cover | 12311310 | 1777 days ago | IN | 0 ETH | 0.04054451 | ||||
| Expire Cover | 12311309 | 1777 days ago | IN | 0 ETH | 0.00912713 | ||||
| Expire Cover | 12311308 | 1777 days ago | IN | 0 ETH | 0.03416471 | ||||
| Expire Cover | 12311305 | 1777 days ago | IN | 0 ETH | 0.03405088 | ||||
| Expire Cover | 12311304 | 1777 days ago | IN | 0 ETH | 0.0110936 | ||||
| Expire Cover | 12311303 | 1777 days ago | IN | 0 ETH | 0.03359555 | ||||
| Expire Cover | 12311301 | 1777 days ago | IN | 0 ETH | 0.03348172 | ||||
| Expire Cover | 12311299 | 1777 days ago | IN | 0 ETH | 0.0387498 | ||||
| Expire Cover | 12311297 | 1777 days ago | IN | 0 ETH | 0.03325405 | ||||
| Expire Cover | 12311289 | 1777 days ago | IN | 0 ETH | 0.03314022 | ||||
| Expire Cover | 12311285 | 1777 days ago | IN | 0 ETH | 0.03609764 | ||||
| Expire Cover | 12311284 | 1777 days ago | IN | 0 ETH | 0.03562012 | ||||
| Expire Cover | 12311282 | 1777 days ago | IN | 0 ETH | 0.03514505 |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Quotation
Compiler Version
v0.5.17+commit.d19bba13
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../capital/MCR.sol";
import "../capital/PoolData.sol";
import "../claims/ClaimsReward.sol";
import "../governance/MemberRoles.sol";
import "../token/TokenController.sol";
import "../token/TokenData.sol";
import "../token/TokenFunctions.sol";
import "./QuotationData.sol";
contract Quotation is Iupgradable {
using SafeMath for uint;
TokenFunctions public tf;
TokenController public tc;
TokenData public td;
Pool public pool;
PoolData public pd;
QuotationData public qd;
MCR public m1;
MemberRoles public mr;
ClaimsReward public cr;
bool internal locked;
event RefundEvent(address indexed user, bool indexed status, uint holdedCoverID, bytes32 reason);
modifier noReentrancy() {
require(!locked, "Reentrant call.");
locked = true;
_;
locked = false;
}
/**
* @dev Iupgradable Interface to update dependent contract address
*/
function changeDependentContractAddress() public onlyInternal {
m1 = MCR(ms.getLatestAddress("MC"));
tf = TokenFunctions(ms.getLatestAddress("TF"));
tc = TokenController(ms.getLatestAddress("TC"));
td = TokenData(ms.getLatestAddress("TD"));
qd = QuotationData(ms.getLatestAddress("QD"));
pd = PoolData(ms.getLatestAddress("PD"));
mr = MemberRoles(ms.getLatestAddress("MR"));
cr = ClaimsReward(ms.getLatestAddress("CR"));
pool = Pool(ms.getLatestAddress("P1"));
}
function sendEther() public payable {
}
/**
* @dev Expires a cover after a set period of time.
* Changes the status of the Cover and reduces the current
* sum assured of all areas in which the quotation lies
* Unlocks the CN tokens of the cover. Updates the Total Sum Assured value.
* @param _cid Cover Id.
*/
function expireCover(uint _cid) public {
require(checkCoverExpired(_cid) && qd.getCoverStatusNo(_cid) != uint(QuotationData.CoverStatus.CoverExpired));
tf.unlockCN(_cid);
bytes4 curr;
address scAddress;
uint sumAssured;
(,, scAddress, curr, sumAssured,) = qd.getCoverDetailsByCoverID1(_cid);
if (qd.getCoverStatusNo(_cid) != uint(QuotationData.CoverStatus.ClaimAccepted))
_removeSAFromCSA(_cid, sumAssured);
qd.changeCoverStatusNo(_cid, uint8(QuotationData.CoverStatus.CoverExpired));
}
/**
* @dev Checks if a cover should get expired/closed or not.
* @param _cid Cover Index.
* @return expire true if the Cover's time has expired, false otherwise.
*/
function checkCoverExpired(uint _cid) public view returns (bool expire) {
expire = qd.getValidityOfCover(_cid) < uint64(now);
}
/**
* @dev Updates the Sum Assured Amount of all the quotation.
* @param _cid Cover id
* @param _amount that will get subtracted Current Sum Assured
* amount that comes under a quotation.
*/
function removeSAFromCSA(uint _cid, uint _amount) public onlyInternal {
_removeSAFromCSA(_cid, _amount);
}
/**
* @dev Makes Cover funded via NXM tokens.
* @param smartCAdd Smart Contract Address
*/
function makeCoverUsingNXMTokens(
uint[] memory coverDetails,
uint16 coverPeriod,
bytes4 coverCurr,
address smartCAdd,
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
isMemberAndcheckPause
{
tc.burnFrom(msg.sender, coverDetails[2]); // need burn allowance
_verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s, true);
}
/**
* @dev Verifies cover details signed off chain.
* @param from address of funder.
* @param scAddress Smart Contract Address
*/
function verifyCoverDetails(
address payable from,
address scAddress,
bytes4 coverCurr,
uint[] memory coverDetails,
uint16 coverPeriod,
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
onlyInternal
{
_verifyCoverDetails(
from,
scAddress,
coverCurr,
coverDetails,
coverPeriod,
_v,
_r,
_s,
false
);
}
/**
* @dev Verifies signature.
* @param coverDetails details related to cover.
* @param coverPeriod validity of cover.
* @param smaratCA smarat contract address.
* @param _v argument from vrs hash.
* @param _r argument from vrs hash.
* @param _s argument from vrs hash.
*/
function verifySign(
uint[] memory coverDetails,
uint16 coverPeriod,
bytes4 curr,
address smaratCA,
uint8 _v,
bytes32 _r,
bytes32 _s
)
public
view
returns (bool)
{
require(smaratCA != address(0));
require(pd.capReached() == 1, "Can not buy cover until cap reached for 1st time");
bytes32 hash = getOrderHash(coverDetails, coverPeriod, curr, smaratCA);
return isValidSignature(hash, _v, _r, _s);
}
/**
* @dev Gets order hash for given cover details.
* @param coverDetails details realted to cover.
* @param coverPeriod validity of cover.
* @param smaratCA smarat contract address.
*/
function getOrderHash(
uint[] memory coverDetails,
uint16 coverPeriod,
bytes4 curr,
address smaratCA
)
public
view
returns (bytes32)
{
return keccak256(
abi.encodePacked(
coverDetails[0],
curr, coverPeriod,
smaratCA,
coverDetails[1],
coverDetails[2],
coverDetails[3],
coverDetails[4],
address(this)
)
);
}
/**
* @dev Verifies signature.
* @param hash order hash
* @param v argument from vrs hash.
* @param r argument from vrs hash.
* @param s argument from vrs hash.
*/
function isValidSignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public view returns (bool) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, hash));
address a = ecrecover(prefixedHash, v, r, s);
return (a == qd.getAuthQuoteEngine());
}
/**
* @dev to get the status of recently holded coverID
* @param userAdd is the user address in concern
* @return the status of the concerned coverId
*/
function getRecentHoldedCoverIdStatus(address userAdd) public view returns (int) {
uint holdedCoverLen = qd.getUserHoldedCoverLength(userAdd);
if (holdedCoverLen == 0) {
return - 1;
} else {
uint holdedCoverID = qd.getUserHoldedCoverByIndex(userAdd, holdedCoverLen.sub(1));
return int(qd.holdedCoverIDStatus(holdedCoverID));
}
}
/**
* @dev to get the verdict of kyc process
* @param status is the kyc status
* @param _add is the address of member
*/
function kycVerdict(address _add, bool status) public checkPause noReentrancy {
require(msg.sender == qd.kycAuthAddress());
_kycTrigger(status, _add);
}
/**
* @dev transfering Ethers to newly created quotation contract.
*/
function transferAssetsToNewContract(address newAdd) public onlyInternal noReentrancy {
uint amount = address(this).balance;
IERC20 erc20;
if (amount > 0) {
// newAdd.transfer(amount);
Quotation newQT = Quotation(newAdd);
newQT.sendEther.value(amount)();
}
uint currAssetLen = pd.getAllCurrenciesLen();
for (uint64 i = 1; i < currAssetLen; i++) {
bytes4 currName = pd.getCurrenciesByIndex(i);
address currAddr = pd.getCurrencyAssetAddress(currName);
erc20 = IERC20(currAddr); // solhint-disable-line
if (erc20.balanceOf(address(this)) > 0) {
require(erc20.transfer(newAdd, erc20.balanceOf(address(this))));
}
}
}
/**
* @dev Creates cover of the quotation, changes the status of the quotation ,
* updates the total sum assured and locks the tokens of the cover against a quote.
* @param from Quote member Ethereum address.
*/
function _makeCover(//solhint-disable-line
address payable from,
address scAddress,
bytes4 coverCurr,
uint[] memory coverDetails,
uint16 coverPeriod
)
internal
{
uint cid = qd.getCoverLength();
qd.addCover(
coverPeriod,
coverDetails[0],
from,
coverCurr,
scAddress,
coverDetails[1],
coverDetails[2]
);
uint coverNoteAmount = (coverDetails[2].mul(qd.tokensRetained())).div(100);
tc.mint(from, coverNoteAmount);
tf.lockCN(coverNoteAmount, coverPeriod, cid, from);
qd.addInTotalSumAssured(coverCurr, coverDetails[0]);
qd.addInTotalSumAssuredSC(scAddress, coverCurr, coverDetails[0]);
tf.pushStakerRewards(scAddress, coverDetails[2]);
}
/**
* @dev Makes a cover.
* @param from address of funder.
* @param scAddress Smart Contract Address
*/
function _verifyCoverDetails(
address payable from,
address scAddress,
bytes4 coverCurr,
uint[] memory coverDetails,
uint16 coverPeriod,
uint8 _v,
bytes32 _r,
bytes32 _s,
bool isNXM
) internal {
require(coverDetails[3] > now);
require(!qd.timestampRepeated(coverDetails[4]));
qd.setTimestampRepeated(coverDetails[4]);
require(coverPeriod >= 30 && coverPeriod <= 365, "Quotation: Cover period out of bounds");
address asset = cr.getCurrencyAssetAddress(coverCurr);
if (coverCurr != "ETH" && !isNXM) {
pool.transferAssetFrom(asset, from, coverDetails[1]);
}
require(verifySign(coverDetails, coverPeriod, coverCurr, scAddress, _v, _r, _s));
_makeCover(from, scAddress, coverCurr, coverDetails, coverPeriod);
}
/**
* @dev Updates the Sum Assured Amount of all the quotation.
* @param _cid Cover id
* @param _amount that will get subtracted Current Sum Assured
* amount that comes under a quotation.
*/
function _removeSAFromCSA(uint _cid, uint _amount) internal checkPause {
address _add;
bytes4 coverCurr;
(,, _add, coverCurr,,) = qd.getCoverDetailsByCoverID1(_cid);
qd.subFromTotalSumAssured(coverCurr, _amount);
qd.subFromTotalSumAssuredSC(_add, coverCurr, _amount);
}
/**
* @dev to trigger the kyc process
* @param status is the kyc status
* @param _add is the address of member
*/
function _kycTrigger(bool status, address _add) internal {
uint holdedCoverLen = qd.getUserHoldedCoverLength(_add).sub(1);
uint holdedCoverID = qd.getUserHoldedCoverByIndex(_add, holdedCoverLen);
address payable userAdd;
address scAddress;
bytes4 coverCurr;
uint16 coverPeriod;
uint[] memory coverDetails = new uint[](4);
IERC20 erc20;
(, userAdd, coverDetails) = qd.getHoldedCoverDetailsByID2(holdedCoverID);
(, scAddress, coverCurr, coverPeriod) = qd.getHoldedCoverDetailsByID1(holdedCoverID);
require(qd.refundEligible(userAdd));
qd.setRefundEligible(userAdd, false);
require(qd.holdedCoverIDStatus(holdedCoverID) == uint(QuotationData.HCIDStatus.kycPending));
uint joinFee = td.joiningFee();
if (status) {
mr.payJoiningFee.value(joinFee)(userAdd);
if (coverDetails[3] > now) {
qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycPass));
if (coverCurr == "ETH") {
(bool ok,) = address(pool).call.value(coverDetails[1])("");
require(ok, "Quotation: ether transfer to pool failed");
} else {
erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line
require(erc20.transfer(address(pool), coverDetails[1]));
}
emit RefundEvent(userAdd, status, holdedCoverID, "KYC Passed");
_makeCover(userAdd, scAddress, coverCurr, coverDetails, coverPeriod);
} else {
qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycPassNoCover));
if (coverCurr == "ETH") {
userAdd.transfer(coverDetails[1]);
} else {
erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line
require(erc20.transfer(userAdd, coverDetails[1]));
}
emit RefundEvent(userAdd, status, holdedCoverID, "Cover Failed");
}
} else {
qd.setHoldedCoverIDStatus(holdedCoverID, uint(QuotationData.HCIDStatus.kycFailedOrRefunded));
uint totalRefund = joinFee;
if (coverCurr == "ETH") {
totalRefund = coverDetails[1].add(joinFee);
} else {
erc20 = IERC20(pd.getCurrencyAssetAddress(coverCurr)); // solhint-disable-line
require(erc20.transfer(userAdd, coverDetails[1]));
}
userAdd.transfer(totalRefund);
emit RefundEvent(userAdd, status, holdedCoverID, "KYC Failed");
}
}
}pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../capital/Pool.sol";
import "../cover/QuotationData.sol";
import "../governance/MemberRoles.sol";
import "../governance/ProposalCategory.sol";
import "../oracles/PriceFeedOracle.sol";
import "../token/NXMToken.sol";
import "../token/TokenData.sol";
import "./PoolData.sol";
contract MCR is Iupgradable {
using SafeMath for uint;
Pool public pool;
PoolData public pd;
NXMToken public tk;
QuotationData public qd;
MemberRoles public mr;
TokenData public td;
ProposalCategory public proposalCategory;
uint private constant minCapFactor = uint(10) ** 21;
uint public variableMincap;
uint public dynamicMincapThresholdx100 = 13000;
uint public dynamicMincapIncrementx100 = 100;
event MCREvent(
uint indexed date,
uint blockNumber,
bytes4[] allCurr,
uint[] allCurrRates,
uint mcrEtherx100,
uint mcrPercx100,
uint vFull
);
constructor (address masterAddress) public {
changeMasterAddress(masterAddress);
// we'll pass the zero address on the first deploy
// due to missing previous MCR contract
if (masterAddress == address(0)) {
return;
}
address mcrAddress = ms.getLatestAddress("MC");
MCR previousMCR = MCR(mcrAddress);
// fetch MCR parameters from previous contract
variableMincap = previousMCR.variableMincap();
dynamicMincapThresholdx100 = previousMCR.dynamicMincapThresholdx100();
dynamicMincapIncrementx100 = previousMCR.dynamicMincapIncrementx100();
}
/**
* @dev Adds new MCR data.
* @param mcrP Minimum Capital Requirement in percentage.
* @param vF Pool fund value in Ether used in the last full daily calculation of the Capital model.
* @param onlyDate Date(yyyymmdd) at which MCR details are getting added.
*/
function addMCRData(
uint mcrP,
uint mcrE,
uint vF,
bytes4[] calldata curr,
uint[] calldata _threeDayAvg,
uint64 onlyDate
)
external
checkPause
{
require(proposalCategory.constructorCheck());
require(pd.isnotarise(msg.sender));
if (mr.launched() && pd.capReached() != 1) {
if (mcrP >= 10000)
pd.setCapReached(1);
}
uint len = pd.getMCRDataLength();
_addMCRData(len, onlyDate, curr, mcrE, mcrP, vF, _threeDayAvg);
}
// proxying this call through mcr contract to get rid of pd from pool
function getLastMCREther() external view returns (uint) {
return pd.getLastMCREther();
}
/**
* @dev Iupgradable Interface to update dependent contract address
*/
function changeDependentContractAddress() public {
qd = QuotationData(ms.getLatestAddress("QD"));
pool = Pool(ms.getLatestAddress("P1"));
pd = PoolData(ms.getLatestAddress("PD"));
tk = NXMToken(ms.tokenAddress());
mr = MemberRoles(ms.getLatestAddress("MR"));
td = TokenData(ms.getLatestAddress("TD"));
proposalCategory = ProposalCategory(ms.getLatestAddress("PC"));
}
/**
* @dev Gets total sum assured (in ETH).
* @return amount of sum assured
*/
function getAllSumAssurance() public view returns (uint) {
PriceFeedOracle priceFeed = pool.priceFeedOracle();
address daiAddress = priceFeed.daiAddress();
uint ethAmount = qd.getTotalSumAssured("ETH").mul(1e18);
uint daiAmount = qd.getTotalSumAssured("DAI").mul(1e18);
uint daiRate = priceFeed.getAssetToEthRate(daiAddress);
uint daiAmountInEth = daiAmount.mul(daiRate).div(1e18);
return ethAmount.add(daiAmountInEth);
}
function getThresholdValues(uint vtp, uint vF, uint totalSA, uint minCap) public view returns (uint lowerThreshold, uint upperThreshold)
{
minCap = (minCap.mul(minCapFactor)).add(variableMincap);
uint lower = 0;
if (vtp >= vF) {
// Max Threshold = [MAX(Vtp, Vfull) x 120] / mcrMinCap
upperThreshold = vtp.mul(120).mul(100).div((minCap));
} else {
upperThreshold = vF.mul(120).mul(100).div((minCap));
}
if (vtp > 0) {
lower = totalSA.mul(pd.shockParameter()).div(100);
if (lower < minCap.mul(11).div(10))
lower = minCap.mul(11).div(10);
}
if (lower > 0) {
// Min Threshold = [Vtp / MAX(TotalActiveSA x ShockParameter, mcrMinCap x 1.1)] x 100
lowerThreshold = vtp.mul(100).mul(100).div(lower);
}
}
/**
* @dev Gets Uint Parameters of a code
* @param code whose details we want
* @return string value of the code
* @return associated amount (time or perc or value) to the code
*/
function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
codeVal = code;
if (code == "DMCT") {
val = dynamicMincapThresholdx100;
} else if (code == "DMCI") {
val = dynamicMincapIncrementx100;
}
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "DMCT") {
dynamicMincapThresholdx100 = val;
} else if (code == "DMCI") {
dynamicMincapIncrementx100 = val;
}
else {
revert("Invalid param code");
}
}
/**
* @dev Adds MCR Data. Checks if MCR is within valid
* thresholds in order to rule out any incorrect calculations
*/
function _addMCRData(
uint len,
uint64 newMCRDate,
bytes4[] memory curr,
uint mcrE,
uint mcrP,
uint vF,
uint[] memory _threeDayAvg
)
internal
{
uint lowerThreshold = 0;
uint upperThreshold = 0;
if (len > 1) {
uint vtp = pool.getPoolValueInEth();
(lowerThreshold, upperThreshold) = getThresholdValues(vtp, vF, getAllSumAssurance(), pd.minCap());
}
if (mcrP > dynamicMincapThresholdx100) {
variableMincap = (variableMincap.mul(dynamicMincapIncrementx100.add(10000)).add(minCapFactor.mul(pd.minCap().mul(dynamicMincapIncrementx100)))).div(10000);
}
// Explanation for above formula :-
// actual formula -> variableMinCap = variableMinCap + (variableMinCap+minCap)*dynamicMincapIncrement/100
// Implemented formula is simplified form of actual formula.
// Let consider above formula as b = b + (a+b)*c/100
// here, dynamicMincapIncrement is in x100 format.
// so b+(a+b)*cx100/10000 can be written as => (10000.b + b.cx100 + a.cx100)/10000.
// It can further simplify to (b.(10000+cx100) + a.cx100)/10000.
if (len == 1 || (mcrP) >= lowerThreshold && (mcrP) <= upperThreshold) {
pd.pushMCRData(mcrP, mcrE, vF, newMCRDate);
for (uint i = 0; i < curr.length; i++) {
pd.updateCAAvgRate(curr[i], _threeDayAvg[i]);
}
emit MCREvent(newMCRDate, block.number, curr, _threeDayAvg, mcrE, mcrP, vF);
return;
}
revert("MCR: Failed");
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";
contract DSValue {
function peek() public view returns (bytes32, bool);
function read() public view returns (bytes32);
}
contract PoolData is Iupgradable {
using SafeMath for uint;
struct ApiId {
bytes4 typeOf;
bytes4 currency;
uint id;
uint64 dateAdd;
uint64 dateUpd;
}
struct CurrencyAssets {
address currAddress;
uint baseMin;
uint varMin;
}
struct InvestmentAssets {
address currAddress;
bool status;
uint64 minHoldingPercX100;
uint64 maxHoldingPercX100;
uint8 decimals;
}
struct IARankDetails {
bytes4 maxIACurr;
uint64 maxRate;
bytes4 minIACurr;
uint64 minRate;
}
struct McrData {
uint mcrPercx100;
uint mcrEther;
uint vFull; //Pool funds
uint64 date;
}
IARankDetails[] internal allIARankDetails;
McrData[] public allMCRData;
bytes4[] internal allInvestmentCurrencies;
bytes4[] internal allCurrencies;
bytes32[] public allAPIcall;
mapping(bytes32 => ApiId) public allAPIid;
mapping(uint64 => uint) internal datewiseId;
mapping(bytes16 => uint) internal currencyLastIndex;
mapping(bytes4 => CurrencyAssets) internal allCurrencyAssets;
mapping(bytes4 => InvestmentAssets) internal allInvestmentAssets;
mapping(bytes4 => uint) internal caAvgRate;
mapping(bytes4 => uint) internal iaAvgRate;
address public notariseMCR;
address public daiFeedAddress;
uint private constant DECIMAL1E18 = uint(10) ** 18;
uint public uniswapDeadline;
uint public liquidityTradeCallbackTime;
uint public lastLiquidityTradeTrigger;
uint64 internal lastDate;
uint public variationPercX100;
uint public iaRatesTime;
uint public minCap;
uint public mcrTime;
uint public a;
uint public shockParameter;
uint public c;
uint public mcrFailTime;
uint public ethVolumeLimit;
uint public capReached;
uint public capacityLimit;
constructor(address _notariseAdd, address _daiFeedAdd, address _daiAdd) public {
notariseMCR = _notariseAdd;
daiFeedAddress = _daiFeedAdd;
c = 5800000;
a = 1028;
mcrTime = 24 hours;
mcrFailTime = 6 hours;
allMCRData.push(McrData(0, 0, 0, 0));
minCap = 12000 * DECIMAL1E18;
shockParameter = 50;
variationPercX100 = 100; // 1%
iaRatesTime = 24 hours; // 24 hours in seconds
uniswapDeadline = 20 minutes;
liquidityTradeCallbackTime = 4 hours;
ethVolumeLimit = 4;
capacityLimit = 10;
allCurrencies.push("ETH");
allCurrencyAssets["ETH"] = CurrencyAssets(address(0), 1000 * DECIMAL1E18, 0);
allCurrencies.push("DAI");
allCurrencyAssets["DAI"] = CurrencyAssets(_daiAdd, 50000 * DECIMAL1E18, 0);
allInvestmentCurrencies.push("ETH");
allInvestmentAssets["ETH"] = InvestmentAssets(address(0), true, 2500, 10000, 18);
allInvestmentCurrencies.push("DAI");
allInvestmentAssets["DAI"] = InvestmentAssets(_daiAdd, true, 250, 1500, 18);
}
/**
* @dev to set the maximum cap allowed
* @param val is the new value
*/
function setCapReached(uint val) external onlyInternal {
capReached = val;
}
/// @dev Updates the 3 day average rate of a IA currency.
/// To be replaced by MakerDao's on chain rates
/// @param curr IA Currency Name.
/// @param rate Average exchange rate X 100 (of last 3 days).
function updateIAAvgRate(bytes4 curr, uint rate) external onlyInternal {
iaAvgRate[curr] = rate;
}
/// @dev Updates the 3 day average rate of a CA currency.
/// To be replaced by MakerDao's on chain rates
/// @param curr Currency Name.
/// @param rate Average exchange rate X 100 (of last 3 days).
function updateCAAvgRate(bytes4 curr, uint rate) external onlyInternal {
caAvgRate[curr] = rate;
}
/// @dev Adds details of (Minimum Capital Requirement)MCR.
/// @param mcrp Minimum Capital Requirement percentage (MCR% * 100 ,Ex:for 54.56% ,given 5456)
/// @param vf Pool fund value in Ether used in the last full daily calculation from the Capital model.
function pushMCRData(uint mcrp, uint mcre, uint vf, uint64 time) external onlyInternal {
allMCRData.push(McrData(mcrp, mcre, vf, time));
}
/**
* @dev Updates the Timestamp at which result of oracalize call is received.
*/
function updateDateUpdOfAPI(bytes32 myid) external onlyInternal {
allAPIid[myid].dateUpd = uint64(now);
}
/**
* @dev Saves the details of the Oraclize API.
* @param myid Id return by the oraclize query.
* @param _typeof type of the query for which oraclize call is made.
* @param id ID of the proposal,quote,cover etc. for which oraclize call is made
*/
function saveApiDetails(bytes32 myid, bytes4 _typeof, uint id) external onlyInternal {
allAPIid[myid] = ApiId(_typeof, "", id, uint64(now), uint64(now));
}
/**
* @dev Stores the id return by the oraclize query.
* Maintains record of all the Ids return by oraclize query.
* @param myid Id return by the oraclize query.
*/
function addInAllApiCall(bytes32 myid) external onlyInternal {
allAPIcall.push(myid);
}
/**
* @dev Saves investment asset rank details.
* @param maxIACurr Maximum ranked investment asset currency.
* @param maxRate Maximum ranked investment asset rate.
* @param minIACurr Minimum ranked investment asset currency.
* @param minRate Minimum ranked investment asset rate.
* @param date in yyyymmdd.
*/
function saveIARankDetails(
bytes4 maxIACurr,
uint64 maxRate,
bytes4 minIACurr,
uint64 minRate,
uint64 date
)
external
onlyInternal
{
allIARankDetails.push(IARankDetails(maxIACurr, maxRate, minIACurr, minRate));
datewiseId[date] = allIARankDetails.length.sub(1);
}
/**
* @dev to get the time for the laste liquidity trade trigger
*/
function setLastLiquidityTradeTrigger() external onlyInternal {
lastLiquidityTradeTrigger = now;
}
/**
* @dev Updates Last Date.
*/
function updatelastDate(uint64 newDate) external onlyInternal {
lastDate = newDate;
}
/**
* @dev Adds currency asset currency.
* @param curr currency of the asset
* @param currAddress address of the currency
* @param baseMin base minimum in 10^18.
*/
function addCurrencyAssetCurrency(
bytes4 curr,
address currAddress,
uint baseMin
)
external
{
require(ms.checkIsAuthToGoverned(msg.sender));
allCurrencies.push(curr);
allCurrencyAssets[curr] = CurrencyAssets(currAddress, baseMin, 0);
}
/**
* @dev Adds investment asset.
*/
function addInvestmentAssetCurrency(
bytes4 curr,
address currAddress,
bool status,
uint64 minHoldingPercX100,
uint64 maxHoldingPercX100,
uint8 decimals
)
external
{
require(ms.checkIsAuthToGoverned(msg.sender));
allInvestmentCurrencies.push(curr);
allInvestmentAssets[curr] = InvestmentAssets(currAddress, status,
minHoldingPercX100, maxHoldingPercX100, decimals);
}
/**
* @dev Changes base minimum of a given currency asset.
*/
function changeCurrencyAssetBaseMin(bytes4 curr, uint baseMin) external {
require(ms.checkIsAuthToGoverned(msg.sender));
allCurrencyAssets[curr].baseMin = baseMin;
}
/**
* @dev changes variable minimum of a given currency asset.
*/
function changeCurrencyAssetVarMin(bytes4 curr, uint varMin) external onlyInternal {
allCurrencyAssets[curr].varMin = varMin;
}
/**
* @dev Changes the investment asset status.
*/
function changeInvestmentAssetStatus(bytes4 curr, bool status) external {
require(ms.checkIsAuthToGoverned(msg.sender));
allInvestmentAssets[curr].status = status;
}
/**
* @dev Changes the investment asset Holding percentage of a given currency.
*/
function changeInvestmentAssetHoldingPerc(
bytes4 curr,
uint64 minPercX100,
uint64 maxPercX100
)
external
{
require(ms.checkIsAuthToGoverned(msg.sender));
allInvestmentAssets[curr].minHoldingPercX100 = minPercX100;
allInvestmentAssets[curr].maxHoldingPercX100 = maxPercX100;
}
/**
* @dev Gets Currency asset token address.
*/
function changeCurrencyAssetAddress(bytes4 curr, address currAdd) external {
require(ms.checkIsAuthToGoverned(msg.sender));
allCurrencyAssets[curr].currAddress = currAdd;
}
/**
* @dev Changes Investment asset token address.
*/
function changeInvestmentAssetAddressAndDecimal(
bytes4 curr,
address currAdd,
uint8 newDecimal
)
external
{
require(ms.checkIsAuthToGoverned(msg.sender));
allInvestmentAssets[curr].currAddress = currAdd;
allInvestmentAssets[curr].decimals = newDecimal;
}
/// @dev Changes address allowed to post MCR.
function changeNotariseAddress(address _add) external onlyInternal {
notariseMCR = _add;
}
/// @dev updates daiFeedAddress address.
/// @param _add address of DAI feed.
function changeDAIfeedAddress(address _add) external onlyInternal {
daiFeedAddress = _add;
}
/**
* @dev Gets Uint Parameters of a code
* @param code whose details we want
* @return string value of the code
* @return associated amount (time or perc or value) to the code
*/
function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
codeVal = code;
if (code == "MCRTIM") {
val = mcrTime / (1 hours);
} else if (code == "MCRFTIM") {
val = mcrFailTime / (1 hours);
} else if (code == "MCRMIN") {
val = minCap;
} else if (code == "MCRSHOCK") {
val = shockParameter;
} else if (code == "MCRCAPL") {
val = capacityLimit;
} else if (code == "IMZ") {
val = variationPercX100;
} else if (code == "IMRATET") {
val = iaRatesTime / (1 hours);
} else if (code == "IMUNIDL") {
val = uniswapDeadline / (1 minutes);
} else if (code == "IMLIQT") {
val = liquidityTradeCallbackTime / (1 hours);
} else if (code == "IMETHVL") {
val = ethVolumeLimit;
} else if (code == "C") {
val = c;
} else if (code == "A") {
val = a;
}
}
/// @dev Checks whether a given address can notaise MCR data or not.
/// @param _add Address.
/// @return res Returns 0 if address is not authorized, else 1.
function isnotarise(address _add) external view returns (bool res) {
res = false;
if (_add == notariseMCR)
res = true;
}
/// @dev Gets the details of last added MCR.
/// @return mcrPercx100 Total Minimum Capital Requirement percentage of that month of year(multiplied by 100).
/// @return vFull Total Pool fund value in Ether used in the last full daily calculation.
function getLastMCR() external view returns (uint mcrPercx100, uint mcrEtherx1E18, uint vFull, uint64 date) {
uint index = allMCRData.length.sub(1);
return (
allMCRData[index].mcrPercx100,
allMCRData[index].mcrEther,
allMCRData[index].vFull,
allMCRData[index].date
);
}
/// @dev Gets last Minimum Capital Requirement percentage of Capital Model
/// @return val MCR% value,multiplied by 100.
function getLastMCRPerc() external view returns (uint) {
return allMCRData[allMCRData.length.sub(1)].mcrPercx100;
}
/// @dev Gets last Ether price of Capital Model
/// @return val ether value,multiplied by 100.
function getLastMCREther() external view returns (uint) {
return allMCRData[allMCRData.length.sub(1)].mcrEther;
}
/// @dev Gets Pool fund value in Ether used in the last full daily calculation from the Capital model.
function getLastVfull() external view returns (uint) {
return allMCRData[allMCRData.length.sub(1)].vFull;
}
/// @dev Gets last Minimum Capital Requirement in Ether.
/// @return date of MCR.
function getLastMCRDate() external view returns (uint64 date) {
date = allMCRData[allMCRData.length.sub(1)].date;
}
/// @dev Gets details for token price calculation.
function getTokenPriceDetails(bytes4 curr) external view returns (uint _a, uint _c, uint rate) {
_a = a;
_c = c;
rate = _getAvgRate(curr, false);
}
/// @dev Gets the total number of times MCR calculation has been made.
function getMCRDataLength() external view returns (uint len) {
len = allMCRData.length;
}
/**
* @dev Gets investment asset rank details by given date.
*/
function getIARankDetailsByDate(
uint64 date
)
external
view
returns (
bytes4 maxIACurr,
uint64 maxRate,
bytes4 minIACurr,
uint64 minRate
)
{
uint index = datewiseId[date];
return (
allIARankDetails[index].maxIACurr,
allIARankDetails[index].maxRate,
allIARankDetails[index].minIACurr,
allIARankDetails[index].minRate
);
}
/**
* @dev Gets Last Date.
*/
function getLastDate() external view returns (uint64 date) {
return lastDate;
}
/**
* @dev Gets investment currency for a given index.
*/
function getInvestmentCurrencyByIndex(uint index) external view returns (bytes4 currName) {
return allInvestmentCurrencies[index];
}
/**
* @dev Gets count of investment currency.
*/
function getInvestmentCurrencyLen() external view returns (uint len) {
return allInvestmentCurrencies.length;
}
/**
* @dev Gets all the investment currencies.
*/
function getAllInvestmentCurrencies() external view returns (bytes4[] memory currencies) {
return allInvestmentCurrencies;
}
/**
* @dev Gets All currency for a given index.
*/
function getCurrenciesByIndex(uint index) external view returns (bytes4 currName) {
return allCurrencies[index];
}
/**
* @dev Gets count of All currency.
*/
function getAllCurrenciesLen() external view returns (uint len) {
return allCurrencies.length;
}
/**
* @dev Gets all currencies
*/
function getAllCurrencies() external view returns (bytes4[] memory currencies) {
return allCurrencies;
}
/**
* @dev Gets currency asset details for a given currency.
*/
function getCurrencyAssetVarBase(
bytes4 curr
)
external
view
returns (
bytes4 currency,
uint baseMin,
uint varMin
)
{
return (
curr,
allCurrencyAssets[curr].baseMin,
allCurrencyAssets[curr].varMin
);
}
/**
* @dev Gets minimum variable value for currency asset.
*/
function getCurrencyAssetVarMin(bytes4 curr) external view returns (uint varMin) {
return allCurrencyAssets[curr].varMin;
}
/**
* @dev Gets base minimum of a given currency asset.
*/
function getCurrencyAssetBaseMin(bytes4 curr) external view returns (uint baseMin) {
return allCurrencyAssets[curr].baseMin;
}
/**
* @dev Gets investment asset maximum and minimum holding percentage of a given currency.
*/
function getInvestmentAssetHoldingPerc(
bytes4 curr
)
external
view
returns (
uint64 minHoldingPercX100,
uint64 maxHoldingPercX100
)
{
return (
allInvestmentAssets[curr].minHoldingPercX100,
allInvestmentAssets[curr].maxHoldingPercX100
);
}
/**
* @dev Gets investment asset decimals.
*/
function getInvestmentAssetDecimals(bytes4 curr) external view returns (uint8 decimal) {
return allInvestmentAssets[curr].decimals;
}
/**
* @dev Gets investment asset maximum holding percentage of a given currency.
*/
function getInvestmentAssetMaxHoldingPerc(bytes4 curr) external view returns (uint64 maxHoldingPercX100) {
return allInvestmentAssets[curr].maxHoldingPercX100;
}
/**
* @dev Gets investment asset minimum holding percentage of a given currency.
*/
function getInvestmentAssetMinHoldingPerc(bytes4 curr) external view returns (uint64 minHoldingPercX100) {
return allInvestmentAssets[curr].minHoldingPercX100;
}
/**
* @dev Gets investment asset details of a given currency
*/
function getInvestmentAssetDetails(
bytes4 curr
)
external
view
returns (
bytes4 currency,
address currAddress,
bool status,
uint64 minHoldingPerc,
uint64 maxHoldingPerc,
uint8 decimals
)
{
return (
curr,
allInvestmentAssets[curr].currAddress,
allInvestmentAssets[curr].status,
allInvestmentAssets[curr].minHoldingPercX100,
allInvestmentAssets[curr].maxHoldingPercX100,
allInvestmentAssets[curr].decimals
);
}
/**
* @dev Gets Currency asset token address.
*/
function getCurrencyAssetAddress(bytes4 curr) external view returns (address) {
return allCurrencyAssets[curr].currAddress;
}
/**
* @dev Gets investment asset token address.
*/
function getInvestmentAssetAddress(bytes4 curr) external view returns (address) {
return allInvestmentAssets[curr].currAddress;
}
/**
* @dev Gets investment asset active Status of a given currency.
*/
function getInvestmentAssetStatus(bytes4 curr) external view returns (bool status) {
return allInvestmentAssets[curr].status;
}
/**
* @dev Gets type of oraclize query for a given Oraclize Query ID.
* @param myid Oraclize Query ID identifying the query for which the result is being received.
* @return _typeof It could be of type "quote","quotation","cover","claim" etc.
*/
function getApiIdTypeOf(bytes32 myid) external view returns (bytes4) {
return allAPIid[myid].typeOf;
}
/**
* @dev Gets ID associated to oraclize query for a given Oraclize Query ID.
* @param myid Oraclize Query ID identifying the query for which the result is being received.
* @return id1 It could be the ID of "proposal","quotation","cover","claim" etc.
*/
function getIdOfApiId(bytes32 myid) external view returns (uint) {
return allAPIid[myid].id;
}
/**
* @dev Gets the Timestamp of a oracalize call.
*/
function getDateAddOfAPI(bytes32 myid) external view returns (uint64) {
return allAPIid[myid].dateAdd;
}
/**
* @dev Gets the Timestamp at which result of oracalize call is received.
*/
function getDateUpdOfAPI(bytes32 myid) external view returns (uint64) {
return allAPIid[myid].dateUpd;
}
/**
* @dev Gets currency by oracalize id.
*/
function getCurrOfApiId(bytes32 myid) external view returns (bytes4) {
return allAPIid[myid].currency;
}
/**
* @dev Gets ID return by the oraclize query of a given index.
* @param index Index.
* @return myid ID return by the oraclize query.
*/
function getApiCallIndex(uint index) external view returns (bytes32 myid) {
myid = allAPIcall[index];
}
/**
* @dev Gets Length of API call.
*/
function getApilCallLength() external view returns (uint) {
return allAPIcall.length;
}
/**
* @dev Get Details of Oraclize API when given Oraclize Id.
* @param myid ID return by the oraclize query.
* @return _typeof ype of the query for which oraclize
* call is made.("proposal","quote","quotation" etc.)
*/
function getApiCallDetails(
bytes32 myid
)
external
view
returns (
bytes4 _typeof,
bytes4 curr,
uint id,
uint64 dateAdd,
uint64 dateUpd
)
{
return (
allAPIid[myid].typeOf,
allAPIid[myid].currency,
allAPIid[myid].id,
allAPIid[myid].dateAdd,
allAPIid[myid].dateUpd
);
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "MCRTIM") {
_changeMCRTime(val * 1 hours);
} else if (code == "MCRFTIM") {
_changeMCRFailTime(val * 1 hours);
} else if (code == "MCRMIN") {
_changeMinCap(val);
} else if (code == "MCRSHOCK") {
_changeShockParameter(val);
} else if (code == "MCRCAPL") {
_changeCapacityLimit(val);
} else if (code == "IMZ") {
_changeVariationPercX100(val);
} else if (code == "IMRATET") {
_changeIARatesTime(val * 1 hours);
} else if (code == "IMUNIDL") {
_changeUniswapDeadlineTime(val * 1 minutes);
} else if (code == "IMLIQT") {
_changeliquidityTradeCallbackTime(val * 1 hours);
} else if (code == "IMETHVL") {
_setEthVolumeLimit(val);
} else if (code == "C") {
_changeC(val);
} else if (code == "A") {
_changeA(val);
} else {
revert("Invalid param code");
}
}
/**
* @dev to get the average rate of currency rate
* @param curr is the currency in concern
* @return required rate
*/
function getCAAvgRate(bytes4 curr) public view returns (uint rate) {
return _getAvgRate(curr, false);
}
/**
* @dev to get the average rate of investment rate
* @param curr is the investment in concern
* @return required rate
*/
function getIAAvgRate(bytes4 curr) public view returns (uint rate) {
return _getAvgRate(curr, true);
}
function changeDependentContractAddress() public onlyInternal {}
/// @dev Gets the average rate of a CA currency.
/// @param curr Currency Name.
/// @return rate Average rate X 100(of last 3 days).
function _getAvgRate(bytes4 curr, bool isIA) internal view returns (uint rate) {
if (curr == "DAI") {
DSValue ds = DSValue(daiFeedAddress);
rate = uint(ds.read()).div(uint(10) ** 16);
} else if (isIA) {
rate = iaAvgRate[curr];
} else {
rate = caAvgRate[curr];
}
}
/**
* @dev to set the ethereum volume limit
* @param val is the new limit value
*/
function _setEthVolumeLimit(uint val) internal {
ethVolumeLimit = val;
}
/// @dev Sets minimum Cap.
function _changeMinCap(uint newCap) internal {
minCap = newCap;
}
/// @dev Sets Shock Parameter.
function _changeShockParameter(uint newParam) internal {
shockParameter = newParam;
}
/// @dev Changes time period for obtaining new MCR data from external oracle query.
function _changeMCRTime(uint _time) internal {
mcrTime = _time;
}
/// @dev Sets MCR Fail time.
function _changeMCRFailTime(uint _time) internal {
mcrFailTime = _time;
}
/**
* @dev to change the uniswap deadline time
* @param newDeadline is the value
*/
function _changeUniswapDeadlineTime(uint newDeadline) internal {
uniswapDeadline = newDeadline;
}
/**
* @dev to change the liquidity trade call back time
* @param newTime is the new value to be set
*/
function _changeliquidityTradeCallbackTime(uint newTime) internal {
liquidityTradeCallbackTime = newTime;
}
/**
* @dev Changes time after which investment asset rates need to be fed.
*/
function _changeIARatesTime(uint _newTime) internal {
iaRatesTime = _newTime;
}
/**
* @dev Changes the variation range percentage.
*/
function _changeVariationPercX100(uint newPercX100) internal {
variationPercX100 = newPercX100;
}
/// @dev Changes Growth Step
function _changeC(uint newC) internal {
c = newC;
}
/// @dev Changes scaling factor.
function _changeA(uint val) internal {
a = val;
}
/**
* @dev to change the capacity limit
* @param val is the new value
*/
function _changeCapacityLimit(uint val) internal {
capacityLimit = val;
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
//Claims Reward Contract contains the functions for calculating number of tokens
// that will get rewarded, unlocked or burned depending upon the status of claim.
pragma solidity ^0.5.0;
import "../../interfaces/IPooledStaking.sol";
import "../capital/Pool.sol";
import "../cover/QuotationData.sol";
import "../governance/Governance.sol";
import "../token/TokenData.sol";
import "../token/TokenFunctions.sol";
import "./Claims.sol";
import "./ClaimsData.sol";
contract ClaimsReward is Iupgradable {
using SafeMath for uint;
NXMToken internal tk;
TokenController internal tc;
TokenFunctions internal tf;
TokenData internal td;
QuotationData internal qd;
Claims internal c1;
ClaimsData internal cd;
Pool internal pool;
Governance internal gv;
IPooledStaking internal pooledStaking;
MemberRoles internal memberRoles;
// assigned in constructor
address public DAI;
// constants
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint private constant DECIMAL1E18 = uint(10) ** 18;
constructor (address masterAddress, address _daiAddress) public {
changeMasterAddress(masterAddress);
DAI = _daiAddress;
}
function changeDependentContractAddress() public onlyInternal {
c1 = Claims(ms.getLatestAddress("CL"));
cd = ClaimsData(ms.getLatestAddress("CD"));
tk = NXMToken(ms.tokenAddress());
tc = TokenController(ms.getLatestAddress("TC"));
td = TokenData(ms.getLatestAddress("TD"));
tf = TokenFunctions(ms.getLatestAddress("TF"));
qd = QuotationData(ms.getLatestAddress("QD"));
gv = Governance(ms.getLatestAddress("GV"));
pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
memberRoles = MemberRoles(ms.getLatestAddress("MR"));
pool = Pool(ms.getLatestAddress("P1"));
}
/// @dev Decides the next course of action for a given claim.
function changeClaimStatus(uint claimid) public checkPause onlyInternal {
(, uint coverid) = cd.getClaimCoverId(claimid);
(, uint status) = cd.getClaimStatusNumber(claimid);
// when current status is "Pending-Claim Assessor Vote"
if (status == 0) {
_changeClaimStatusCA(claimid, coverid, status);
} else if (status >= 1 && status <= 5) {
_changeClaimStatusMV(claimid, coverid, status);
} else if (status == 12) {// when current status is "Claim Accepted Payout Pending"
bool payoutSucceeded = attemptClaimPayout(coverid);
if (payoutSucceeded) {
c1.setClaimStatus(claimid, 14);
} else {
c1.setClaimStatus(claimid, 12);
}
}
c1.changePendingClaimStart();
}
function getCurrencyAssetAddress(bytes4 currency) public view returns (address) {
if (currency == "ETH") {
return ETH;
}
if (currency == "DAI") {
return DAI;
}
revert("ClaimsReward: unknown asset");
}
function attemptClaimPayout(uint coverId) internal returns (bool success) {
uint sumAssured = qd.getCoverSumAssured(coverId);
// TODO: when adding new cover currencies, fetch the correct decimals for this multiplication
uint sumAssuredWei = sumAssured.mul(1e18);
// get asset address
bytes4 coverCurrency = qd.getCurrencyOfCover(coverId);
address asset = getCurrencyAssetAddress(coverCurrency);
// get payout address
address payable coverHolder = qd.getCoverMemberAddress(coverId);
address payable payoutAddress = memberRoles.getClaimPayoutAddress(coverHolder);
// execute the payout
bool payoutSucceeded = pool.sendClaimPayout(asset, payoutAddress, sumAssuredWei);
if (payoutSucceeded) {
// burn staked tokens
(, address scAddress) = qd.getscAddressOfCover(coverId);
uint tokenPrice = pool.getTokenPrice(asset);
// note: for new assets "18" needs to be replaced with target asset decimals
uint burnNXMAmount = sumAssuredWei.mul(1e18).div(tokenPrice);
pooledStaking.pushBurn(scAddress, burnNXMAmount);
// adjust total sum assured
(, address coverContract) = qd.getscAddressOfCover(coverId);
qd.subFromTotalSumAssured(coverCurrency, sumAssured);
qd.subFromTotalSumAssuredSC(coverContract, coverCurrency, sumAssured);
return true;
}
return false;
}
/// @dev Amount of tokens to be rewarded to a user for a particular vote id.
/// @param check 1 -> CA vote, else member vote
/// @param voteid vote id for which reward has to be Calculated
/// @param flag if 1 calculate even if claimed,else don't calculate if already claimed
/// @return tokenCalculated reward to be given for vote id
/// @return lastClaimedCheck true if final verdict is still pending for that voteid
/// @return tokens number of tokens locked under that voteid
/// @return perc percentage of reward to be given.
function getRewardToBeGiven(
uint check,
uint voteid,
uint flag
)
public
view
returns (
uint tokenCalculated,
bool lastClaimedCheck,
uint tokens,
uint perc
)
{
uint claimId;
int8 verdict;
bool claimed;
uint tokensToBeDist;
uint totalTokens;
(tokens, claimId, verdict, claimed) = cd.getVoteDetails(voteid);
lastClaimedCheck = false;
int8 claimVerdict = cd.getFinalVerdict(claimId);
if (claimVerdict == 0) {
lastClaimedCheck = true;
}
if (claimVerdict == verdict && (claimed == false || flag == 1)) {
if (check == 1) {
(perc, , tokensToBeDist) = cd.getClaimRewardDetail(claimId);
} else {
(, perc, tokensToBeDist) = cd.getClaimRewardDetail(claimId);
}
if (perc > 0) {
if (check == 1) {
if (verdict == 1) {
(, totalTokens,) = cd.getClaimsTokenCA(claimId);
} else {
(,, totalTokens) = cd.getClaimsTokenCA(claimId);
}
} else {
if (verdict == 1) {
(, totalTokens,) = cd.getClaimsTokenMV(claimId);
} else {
(,, totalTokens) = cd.getClaimsTokenMV(claimId);
}
}
tokenCalculated = (perc.mul(tokens).mul(tokensToBeDist)).div(totalTokens.mul(100));
}
}
}
/// @dev Transfers all tokens held by contract to a new contract in case of upgrade.
function upgrade(address _newAdd) public onlyInternal {
uint amount = tk.balanceOf(address(this));
if (amount > 0) {
require(tk.transfer(_newAdd, amount));
}
}
/// @dev Total reward in token due for claim by a user.
/// @return total total number of tokens
function getRewardToBeDistributedByUser(address _add) public view returns (uint total) {
uint lengthVote = cd.getVoteAddressCALength(_add);
uint lastIndexCA;
uint lastIndexMV;
uint tokenForVoteId;
uint voteId;
(lastIndexCA, lastIndexMV) = cd.getRewardDistributedIndex(_add);
for (uint i = lastIndexCA; i < lengthVote; i++) {
voteId = cd.getVoteAddressCA(_add, i);
(tokenForVoteId,,,) = getRewardToBeGiven(1, voteId, 0);
total = total.add(tokenForVoteId);
}
lengthVote = cd.getVoteAddressMemberLength(_add);
for (uint j = lastIndexMV; j < lengthVote; j++) {
voteId = cd.getVoteAddressMember(_add, j);
(tokenForVoteId,,,) = getRewardToBeGiven(0, voteId, 0);
total = total.add(tokenForVoteId);
}
return (total);
}
/// @dev Gets reward amount and claiming status for a given claim id.
/// @return reward amount of tokens to user.
/// @return claimed true if already claimed false if yet to be claimed.
function getRewardAndClaimedStatus(uint check, uint claimId) public view returns (uint reward, bool claimed) {
uint voteId;
uint claimid;
uint lengthVote;
if (check == 1) {
lengthVote = cd.getVoteAddressCALength(msg.sender);
for (uint i = 0; i < lengthVote; i++) {
voteId = cd.getVoteAddressCA(msg.sender, i);
(, claimid, , claimed) = cd.getVoteDetails(voteId);
if (claimid == claimId) {break;}
}
} else {
lengthVote = cd.getVoteAddressMemberLength(msg.sender);
for (uint j = 0; j < lengthVote; j++) {
voteId = cd.getVoteAddressMember(msg.sender, j);
(, claimid, , claimed) = cd.getVoteDetails(voteId);
if (claimid == claimId) {break;}
}
}
(reward,,,) = getRewardToBeGiven(check, voteId, 1);
}
/**
* @dev Function used to claim all pending rewards : Claims Assessment + Risk Assessment + Governance
* Claim assesment, Risk assesment, Governance rewards
*/
function claimAllPendingReward(uint records) public isMemberAndcheckPause {
_claimRewardToBeDistributed(records);
pooledStaking.withdrawReward(msg.sender);
uint governanceRewards = gv.claimReward(msg.sender, records);
if (governanceRewards > 0) {
require(tk.transfer(msg.sender, governanceRewards));
}
}
/**
* @dev Function used to get pending rewards of a particular user address.
* @param _add user address.
* @return total reward amount of the user
*/
function getAllPendingRewardOfUser(address _add) public view returns (uint) {
uint caReward = getRewardToBeDistributedByUser(_add);
uint pooledStakingReward = pooledStaking.stakerReward(_add);
uint governanceReward = gv.getPendingReward(_add);
return caReward.add(pooledStakingReward).add(governanceReward);
}
/// @dev Rewards/Punishes users who participated in Claims assessment.
// Unlocking and burning of the tokens will also depend upon the status of claim.
/// @param claimid Claim Id.
function _rewardAgainstClaim(uint claimid, uint coverid, uint status) internal {
uint premiumNXM = qd.getCoverPremiumNXM(coverid);
uint distributableTokens = premiumNXM.mul(cd.claimRewardPerc()).div(100); // 20% of premium
uint percCA;
uint percMV;
(percCA, percMV) = cd.getRewardStatus(status);
cd.setClaimRewardDetail(claimid, percCA, percMV, distributableTokens);
if (percCA > 0 || percMV > 0) {
tc.mint(address(this), distributableTokens);
}
// denied
if (status == 6 || status == 9 || status == 11) {
cd.changeFinalVerdict(claimid, - 1);
td.setDepositCN(coverid, false); // Unset flag
tf.burnDepositCN(coverid); // burn Deposited CN
// accepted
} else if (status == 7 || status == 8 || status == 10) {
cd.changeFinalVerdict(claimid, 1);
td.setDepositCN(coverid, false); // Unset flag
tf.unlockCN(coverid);
bool payoutSucceeded = attemptClaimPayout(coverid);
// 12 = payout pending, 14 = payout succeeded
uint nextStatus = payoutSucceeded ? 14 : 12;
c1.setClaimStatus(claimid, nextStatus);
}
}
/// @dev Computes the result of Claim Assessors Voting for a given claim id.
function _changeClaimStatusCA(uint claimid, uint coverid, uint status) internal {
// Check if voting should be closed or not
if (c1.checkVoteClosing(claimid) == 1) {
uint caTokens = c1.getCATokens(claimid, 0); // converted in cover currency.
uint accept;
uint deny;
uint acceptAndDeny;
bool rewardOrPunish;
uint sumAssured;
(, accept) = cd.getClaimVote(claimid, 1);
(, deny) = cd.getClaimVote(claimid, - 1);
acceptAndDeny = accept.add(deny);
accept = accept.mul(100);
deny = deny.mul(100);
if (caTokens == 0) {
status = 3;
} else {
sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18);
// Min threshold reached tokens used for voting > 5* sum assured
if (caTokens > sumAssured.mul(5)) {
if (accept.div(acceptAndDeny) > 70) {
status = 7;
qd.changeCoverStatusNo(coverid, uint8(QuotationData.CoverStatus.ClaimAccepted));
rewardOrPunish = true;
} else if (deny.div(acceptAndDeny) > 70) {
status = 6;
qd.changeCoverStatusNo(coverid, uint8(QuotationData.CoverStatus.ClaimDenied));
rewardOrPunish = true;
} else if (accept.div(acceptAndDeny) > deny.div(acceptAndDeny)) {
status = 4;
} else {
status = 5;
}
} else {
if (accept.div(acceptAndDeny) > deny.div(acceptAndDeny)) {
status = 2;
} else {
status = 3;
}
}
}
c1.setClaimStatus(claimid, status);
if (rewardOrPunish) {
_rewardAgainstClaim(claimid, coverid, status);
}
}
}
/// @dev Computes the result of Member Voting for a given claim id.
function _changeClaimStatusMV(uint claimid, uint coverid, uint status) internal {
// Check if voting should be closed or not
if (c1.checkVoteClosing(claimid) == 1) {
uint8 coverStatus;
uint statusOrig = status;
uint mvTokens = c1.getCATokens(claimid, 1); // converted in cover currency.
// If tokens used for acceptance >50%, claim is accepted
uint sumAssured = qd.getCoverSumAssured(coverid).mul(DECIMAL1E18);
uint thresholdUnreached = 0;
// Minimum threshold for member voting is reached only when
// value of tokens used for voting > 5* sum assured of claim id
if (mvTokens < sumAssured.mul(5)) {
thresholdUnreached = 1;
}
uint accept;
(, accept) = cd.getClaimMVote(claimid, 1);
uint deny;
(, deny) = cd.getClaimMVote(claimid, - 1);
if (accept.add(deny) > 0) {
if (accept.mul(100).div(accept.add(deny)) >= 50 && statusOrig > 1 &&
statusOrig <= 5 && thresholdUnreached == 0) {
status = 8;
coverStatus = uint8(QuotationData.CoverStatus.ClaimAccepted);
} else if (deny.mul(100).div(accept.add(deny)) >= 50 && statusOrig > 1 &&
statusOrig <= 5 && thresholdUnreached == 0) {
status = 9;
coverStatus = uint8(QuotationData.CoverStatus.ClaimDenied);
}
}
if (thresholdUnreached == 1 && (statusOrig == 2 || statusOrig == 4)) {
status = 10;
coverStatus = uint8(QuotationData.CoverStatus.ClaimAccepted);
} else if (thresholdUnreached == 1 && (statusOrig == 5 || statusOrig == 3 || statusOrig == 1)) {
status = 11;
coverStatus = uint8(QuotationData.CoverStatus.ClaimDenied);
}
c1.setClaimStatus(claimid, status);
qd.changeCoverStatusNo(coverid, uint8(coverStatus));
// Reward/Punish Claim Assessors and Members who participated in Claims assessment
_rewardAgainstClaim(claimid, coverid, status);
}
}
/// @dev Allows a user to claim all pending Claims assessment rewards.
function _claimRewardToBeDistributed(uint _records) internal {
uint lengthVote = cd.getVoteAddressCALength(msg.sender);
uint voteid;
uint lastIndex;
(lastIndex,) = cd.getRewardDistributedIndex(msg.sender);
uint total = 0;
uint tokenForVoteId = 0;
bool lastClaimedCheck;
uint _days = td.lockCADays();
bool claimed;
uint counter = 0;
uint claimId;
uint perc;
uint i;
uint lastClaimed = lengthVote;
for (i = lastIndex; i < lengthVote && counter < _records; i++) {
voteid = cd.getVoteAddressCA(msg.sender, i);
(tokenForVoteId, lastClaimedCheck, , perc) = getRewardToBeGiven(1, voteid, 0);
if (lastClaimed == lengthVote && lastClaimedCheck == true) {
lastClaimed = i;
}
(, claimId, , claimed) = cd.getVoteDetails(voteid);
if (perc > 0 && !claimed) {
counter++;
cd.setRewardClaimed(voteid, true);
} else if (perc == 0 && cd.getFinalVerdict(claimId) != 0 && !claimed) {
(perc,,) = cd.getClaimRewardDetail(claimId);
if (perc == 0) {
counter++;
}
cd.setRewardClaimed(voteid, true);
}
if (tokenForVoteId > 0) {
total = tokenForVoteId.add(total);
}
}
if (lastClaimed == lengthVote) {
cd.setRewardDistributedIndexCA(msg.sender, i);
}
else {
cd.setRewardDistributedIndexCA(msg.sender, lastClaimed);
}
lengthVote = cd.getVoteAddressMemberLength(msg.sender);
lastClaimed = lengthVote;
_days = _days.mul(counter);
if (tc.tokensLockedAtTime(msg.sender, "CLA", now) > 0) {
tc.reduceLock(msg.sender, "CLA", _days);
}
(, lastIndex) = cd.getRewardDistributedIndex(msg.sender);
lastClaimed = lengthVote;
counter = 0;
for (i = lastIndex; i < lengthVote && counter < _records; i++) {
voteid = cd.getVoteAddressMember(msg.sender, i);
(tokenForVoteId, lastClaimedCheck,,) = getRewardToBeGiven(0, voteid, 0);
if (lastClaimed == lengthVote && lastClaimedCheck == true) {
lastClaimed = i;
}
(, claimId, , claimed) = cd.getVoteDetails(voteid);
if (claimed == false && cd.getFinalVerdict(claimId) != 0) {
cd.setRewardClaimed(voteid, true);
counter++;
}
if (tokenForVoteId > 0) {
total = tokenForVoteId.add(total);
}
}
if (total > 0) {
require(tk.transfer(msg.sender, total));
}
if (lastClaimed == lengthVote) {
cd.setRewardDistributedIndexMV(msg.sender, i);
}
else {
cd.setRewardDistributedIndexMV(msg.sender, lastClaimed);
}
}
/**
* @dev Function used to claim the commission earned by the staker.
*/
function _claimStakeCommission(uint _records, address _user) external onlyInternal {
uint total = 0;
uint len = td.getStakerStakedContractLength(_user);
uint lastCompletedStakeCommission = td.lastCompletedStakeCommission(_user);
uint commissionEarned;
uint commissionRedeemed;
uint maxCommission;
uint lastCommisionRedeemed = len;
uint counter;
uint i;
for (i = lastCompletedStakeCommission; i < len && counter < _records; i++) {
commissionRedeemed = td.getStakerRedeemedStakeCommission(_user, i);
commissionEarned = td.getStakerEarnedStakeCommission(_user, i);
maxCommission = td.getStakerInitialStakedAmountOnContract(
_user, i).mul(td.stakerMaxCommissionPer()).div(100);
if (lastCommisionRedeemed == len && maxCommission != commissionEarned)
lastCommisionRedeemed = i;
td.pushRedeemedStakeCommissions(_user, i, commissionEarned.sub(commissionRedeemed));
total = total.add(commissionEarned.sub(commissionRedeemed));
counter++;
}
if (lastCommisionRedeemed == len) {
td.setLastCompletedStakeCommissionIndex(_user, i);
} else {
td.setLastCompletedStakeCommissionIndex(_user, lastCommisionRedeemed);
}
if (total > 0)
require(tk.transfer(_user, total)); // solhint-disable-line
}
}/* Copyright (C) 2017 GovBlocks.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "../claims/ClaimsReward.sol";
import "../cover/QuotationData.sol";
import "../token/TokenController.sol";
import "../token/TokenData.sol";
import "../token/TokenFunctions.sol";
import "./Governance.sol";
import "./external/Governed.sol";
contract MemberRoles is Governed, Iupgradable {
TokenController public dAppToken;
TokenData internal td;
QuotationData internal qd;
ClaimsReward internal cr;
Governance internal gv;
TokenFunctions internal tf;
NXMToken public tk;
struct MemberRoleDetails {
uint memberCounter;
mapping(address => bool) memberActive;
address[] memberAddress;
address authorized;
}
enum Role {UnAssigned, AdvisoryBoard, Member, Owner}
event MemberRole(uint256 indexed roleId, bytes32 roleName, string roleDescription);
event switchedMembership(address indexed previousMember, address indexed newMember, uint timeStamp);
event ClaimPayoutAddressSet(address indexed member, address indexed payoutAddress);
MemberRoleDetails[] internal memberRoleData;
bool internal constructorCheck;
uint public maxABCount;
bool public launched;
uint public launchedOn;
mapping (address => address payable) internal claimPayoutAddress;
modifier checkRoleAuthority(uint _memberRoleId) {
if (memberRoleData[_memberRoleId].authorized != address(0))
require(msg.sender == memberRoleData[_memberRoleId].authorized);
else
require(isAuthorizedToGovern(msg.sender), "Not Authorized");
_;
}
/**
* @dev to swap advisory board member
* @param _newABAddress is address of new AB member
* @param _removeAB is advisory board member to be removed
*/
function swapABMember(
address _newABAddress,
address _removeAB
)
external
checkRoleAuthority(uint(Role.AdvisoryBoard)) {
_updateRole(_newABAddress, uint(Role.AdvisoryBoard), true);
_updateRole(_removeAB, uint(Role.AdvisoryBoard), false);
}
/**
* @dev to swap the owner address
* @param _newOwnerAddress is the new owner address
*/
function swapOwner(
address _newOwnerAddress
)
external {
require(msg.sender == address(ms));
_updateRole(ms.owner(), uint(Role.Owner), false);
_updateRole(_newOwnerAddress, uint(Role.Owner), true);
}
/**
* @dev is used to add initital advisory board members
* @param abArray is the list of initial advisory board members
*/
function addInitialABMembers(address[] calldata abArray) external onlyOwner {
//Ensure that NXMaster has initialized.
require(ms.masterInitialized());
require(maxABCount >=
SafeMath.add(numberOfMembers(uint(Role.AdvisoryBoard)), abArray.length)
);
//AB count can't exceed maxABCount
for (uint i = 0; i < abArray.length; i++) {
require(checkRole(abArray[i], uint(MemberRoles.Role.Member)));
_updateRole(abArray[i], uint(Role.AdvisoryBoard), true);
}
}
/**
* @dev to change max number of AB members allowed
* @param _val is the new value to be set
*/
function changeMaxABCount(uint _val) external onlyInternal {
maxABCount = _val;
}
/**
* @dev Iupgradable Interface to update dependent contract address
*/
function changeDependentContractAddress() public {
td = TokenData(ms.getLatestAddress("TD"));
cr = ClaimsReward(ms.getLatestAddress("CR"));
qd = QuotationData(ms.getLatestAddress("QD"));
gv = Governance(ms.getLatestAddress("GV"));
tf = TokenFunctions(ms.getLatestAddress("TF"));
tk = NXMToken(ms.tokenAddress());
dAppToken = TokenController(ms.getLatestAddress("TC"));
}
/**
* @dev to change the master address
* @param _masterAddress is the new master address
*/
function changeMasterAddress(address _masterAddress) public {
if (masterAddress != address(0))
require(masterAddress == msg.sender);
masterAddress = _masterAddress;
ms = INXMMaster(_masterAddress);
nxMasterAddress = _masterAddress;
}
/**
* @dev to initiate the member roles
* @param _firstAB is the address of the first AB member
* @param memberAuthority is the authority (role) of the member
*/
function memberRolesInitiate(address _firstAB, address memberAuthority) public {
require(!constructorCheck);
_addInitialMemberRoles(_firstAB, memberAuthority);
constructorCheck = true;
}
/// @dev Adds new member role
/// @param _roleName New role name
/// @param _roleDescription New description hash
/// @param _authorized Authorized member against every role id
function addRole(//solhint-disable-line
bytes32 _roleName,
string memory _roleDescription,
address _authorized
)
public
onlyAuthorizedToGovern {
_addRole(_roleName, _roleDescription, _authorized);
}
/// @dev Assign or Delete a member from specific role.
/// @param _memberAddress Address of Member
/// @param _roleId RoleId to update
/// @param _active active is set to be True if we want to assign this role to member, False otherwise!
function updateRole(//solhint-disable-line
address _memberAddress,
uint _roleId,
bool _active
)
public
checkRoleAuthority(_roleId) {
_updateRole(_memberAddress, _roleId, _active);
}
/**
* @dev to add members before launch
* @param userArray is list of addresses of members
* @param tokens is list of tokens minted for each array element
*/
function addMembersBeforeLaunch(address[] memory userArray, uint[] memory tokens) public onlyOwner {
require(!launched);
for (uint i = 0; i < userArray.length; i++) {
require(!ms.isMember(userArray[i]));
dAppToken.addToWhitelist(userArray[i]);
_updateRole(userArray[i], uint(Role.Member), true);
dAppToken.mint(userArray[i], tokens[i]);
}
launched = true;
launchedOn = now;
}
/**
* @dev Called by user to pay joining membership fee
*/
function payJoiningFee(address _userAddress) public payable {
require(_userAddress != address(0));
require(!ms.isPause(), "Emergency Pause Applied");
if (msg.sender == address(ms.getLatestAddress("QT"))) {
require(td.walletAddress() != address(0), "No walletAddress present");
dAppToken.addToWhitelist(_userAddress);
_updateRole(_userAddress, uint(Role.Member), true);
td.walletAddress().transfer(msg.value);
} else {
require(!qd.refundEligible(_userAddress));
require(!ms.isMember(_userAddress));
require(msg.value == td.joiningFee());
qd.setRefundEligible(_userAddress, true);
}
}
/**
* @dev to perform kyc verdict
* @param _userAddress whose kyc is being performed
* @param verdict of kyc process
*/
function kycVerdict(address payable _userAddress, bool verdict) public {
require(msg.sender == qd.kycAuthAddress());
require(!ms.isPause());
require(_userAddress != address(0));
require(!ms.isMember(_userAddress));
require(qd.refundEligible(_userAddress));
if (verdict) {
qd.setRefundEligible(_userAddress, false);
uint fee = td.joiningFee();
dAppToken.addToWhitelist(_userAddress);
_updateRole(_userAddress, uint(Role.Member), true);
td.walletAddress().transfer(fee); // solhint-disable-line
} else {
qd.setRefundEligible(_userAddress, false);
_userAddress.transfer(td.joiningFee()); // solhint-disable-line
}
}
/**
* @dev Called by existed member if wish to Withdraw membership.
*/
function withdrawMembership() public {
require(!ms.isPause() && ms.isMember(msg.sender));
require(dAppToken.totalLockedBalance(msg.sender, now) == 0); // solhint-disable-line
require(!tf.isLockedForMemberVote(msg.sender)); // No locked tokens for Member/Governance voting
require(cr.getAllPendingRewardOfUser(msg.sender) == 0); // No pending reward to be claimed(claim assesment).
require(dAppToken.tokensUnlockable(msg.sender, "CLA") == 0, "Member should have no CLA unlockable tokens");
gv.removeDelegation(msg.sender);
dAppToken.burnFrom(msg.sender, tk.balanceOf(msg.sender));
_updateRole(msg.sender, uint(Role.Member), false);
dAppToken.removeFromWhitelist(msg.sender); // need clarification on whitelist
if (claimPayoutAddress[msg.sender] != address(0)) {
claimPayoutAddress[msg.sender] = address(0);
emit ClaimPayoutAddressSet(msg.sender, address(0));
}
}
/**
* @dev Called by existed member if wish to switch membership to other address.
* @param _add address of user to forward membership.
*/
function switchMembership(address _add) external {
require(!ms.isPause() && ms.isMember(msg.sender) && !ms.isMember(_add));
require(dAppToken.totalLockedBalance(msg.sender, now) == 0); // solhint-disable-line
require(!tf.isLockedForMemberVote(msg.sender)); // No locked tokens for Member/Governance voting
require(cr.getAllPendingRewardOfUser(msg.sender) == 0); // No pending reward to be claimed(claim assesment).
require(dAppToken.tokensUnlockable(msg.sender, "CLA") == 0, "Member should have no CLA unlockable tokens");
gv.removeDelegation(msg.sender);
dAppToken.addToWhitelist(_add);
_updateRole(_add, uint(Role.Member), true);
tk.transferFrom(msg.sender, _add, tk.balanceOf(msg.sender));
_updateRole(msg.sender, uint(Role.Member), false);
dAppToken.removeFromWhitelist(msg.sender);
address payable previousPayoutAddress = claimPayoutAddress[msg.sender];
if (previousPayoutAddress != address(0)) {
address payable storedAddress = previousPayoutAddress == _add ? address(0) : previousPayoutAddress;
claimPayoutAddress[msg.sender] = address(0);
claimPayoutAddress[_add] = storedAddress;
// emit event for old address reset
emit ClaimPayoutAddressSet(msg.sender, address(0));
if (storedAddress != address(0)) {
// emit event for setting the payout address on the new member address if it's non zero
emit ClaimPayoutAddressSet(_add, storedAddress);
}
}
emit switchedMembership(msg.sender, _add, now);
}
function getClaimPayoutAddress(address payable _member) external view returns (address payable) {
address payable payoutAddress = claimPayoutAddress[_member];
return payoutAddress != address(0) ? payoutAddress : _member;
}
function setClaimPayoutAddress(address payable _address) external {
require(!ms.isPause(), "system is paused");
require(ms.isMember(msg.sender), "sender is not a member");
require(_address != msg.sender, "should be different than the member address");
claimPayoutAddress[msg.sender] = _address;
emit ClaimPayoutAddressSet(msg.sender, _address);
}
/// @dev Return number of member roles
function totalRoles() public view returns (uint256) {//solhint-disable-line
return memberRoleData.length;
}
/// @dev Change Member Address who holds the authority to Add/Delete any member from specific role.
/// @param _roleId roleId to update its Authorized Address
/// @param _newAuthorized New authorized address against role id
function changeAuthorized(uint _roleId, address _newAuthorized) public checkRoleAuthority(_roleId) {//solhint-disable-line
memberRoleData[_roleId].authorized = _newAuthorized;
}
/// @dev Gets the member addresses assigned by a specific role
/// @param _memberRoleId Member role id
/// @return roleId Role id
/// @return allMemberAddress Member addresses of specified role id
function members(uint _memberRoleId) public view returns (uint, address[] memory memberArray) {//solhint-disable-line
uint length = memberRoleData[_memberRoleId].memberAddress.length;
uint i;
uint j = 0;
memberArray = new address[](memberRoleData[_memberRoleId].memberCounter);
for (i = 0; i < length; i++) {
address member = memberRoleData[_memberRoleId].memberAddress[i];
if (memberRoleData[_memberRoleId].memberActive[member] && !_checkMemberInArray(member, memberArray)) {//solhint-disable-line
memberArray[j] = member;
j++;
}
}
return (_memberRoleId, memberArray);
}
/// @dev Gets all members' length
/// @param _memberRoleId Member role id
/// @return memberRoleData[_memberRoleId].memberCounter Member length
function numberOfMembers(uint _memberRoleId) public view returns (uint) {//solhint-disable-line
return memberRoleData[_memberRoleId].memberCounter;
}
/// @dev Return member address who holds the right to add/remove any member from specific role.
function authorized(uint _memberRoleId) public view returns (address) {//solhint-disable-line
return memberRoleData[_memberRoleId].authorized;
}
/// @dev Get All role ids array that has been assigned to a member so far.
function roles(address _memberAddress) public view returns (uint[] memory) {//solhint-disable-line
uint length = memberRoleData.length;
uint[] memory assignedRoles = new uint[](length);
uint counter = 0;
for (uint i = 1; i < length; i++) {
if (memberRoleData[i].memberActive[_memberAddress]) {
assignedRoles[counter] = i;
counter++;
}
}
return assignedRoles;
}
/// @dev Returns true if the given role id is assigned to a member.
/// @param _memberAddress Address of member
/// @param _roleId Checks member's authenticity with the roleId.
/// i.e. Returns true if this roleId is assigned to member
function checkRole(address _memberAddress, uint _roleId) public view returns (bool) {//solhint-disable-line
if (_roleId == uint(Role.UnAssigned))
return true;
else
if (memberRoleData[_roleId].memberActive[_memberAddress]) //solhint-disable-line
return true;
else
return false;
}
/// @dev Return total number of members assigned against each role id.
/// @return totalMembers Total members in particular role id
function getMemberLengthForAllRoles() public view returns (uint[] memory totalMembers) {//solhint-disable-line
totalMembers = new uint[](memberRoleData.length);
for (uint i = 0; i < memberRoleData.length; i++) {
totalMembers[i] = numberOfMembers(i);
}
}
/**
* @dev to update the member roles
* @param _memberAddress in concern
* @param _roleId the id of role
* @param _active if active is true, add the member, else remove it
*/
function _updateRole(address _memberAddress,
uint _roleId,
bool _active) internal {
// require(_roleId != uint(Role.TokenHolder), "Membership to Token holder is detected automatically");
if (_active) {
require(!memberRoleData[_roleId].memberActive[_memberAddress]);
memberRoleData[_roleId].memberCounter = SafeMath.add(memberRoleData[_roleId].memberCounter, 1);
memberRoleData[_roleId].memberActive[_memberAddress] = true;
memberRoleData[_roleId].memberAddress.push(_memberAddress);
} else {
require(memberRoleData[_roleId].memberActive[_memberAddress]);
memberRoleData[_roleId].memberCounter = SafeMath.sub(memberRoleData[_roleId].memberCounter, 1);
delete memberRoleData[_roleId].memberActive[_memberAddress];
}
}
/// @dev Adds new member role
/// @param _roleName New role name
/// @param _roleDescription New description hash
/// @param _authorized Authorized member against every role id
function _addRole(
bytes32 _roleName,
string memory _roleDescription,
address _authorized
) internal {
emit MemberRole(memberRoleData.length, _roleName, _roleDescription);
memberRoleData.push(MemberRoleDetails(0, new address[](0), _authorized));
}
/**
* @dev to check if member is in the given member array
* @param _memberAddress in concern
* @param memberArray in concern
* @return boolean to represent the presence
*/
function _checkMemberInArray(
address _memberAddress,
address[] memory memberArray
)
internal
pure
returns (bool memberExists)
{
uint i;
for (i = 0; i < memberArray.length; i++) {
if (memberArray[i] == _memberAddress) {
memberExists = true;
break;
}
}
}
/**
* @dev to add initial member roles
* @param _firstAB is the member address to be added
* @param memberAuthority is the member authority(role) to be added for
*/
function _addInitialMemberRoles(address _firstAB, address memberAuthority) internal {
maxABCount = 5;
_addRole("Unassigned", "Unassigned", address(0));
_addRole(
"Advisory Board",
"Selected few members that are deeply entrusted by the dApp. An ideal advisory board should be a mix of skills of domain, governance, research, technology, consulting etc to improve the performance of the dApp.", //solhint-disable-line
address(0)
);
_addRole(
"Member",
"Represents all users of Mutual.", //solhint-disable-line
memberAuthority
);
_addRole(
"Owner",
"Represents Owner of Mutual.", //solhint-disable-line
address(0)
);
// _updateRole(_firstAB, uint(Role.AdvisoryBoard), true);
_updateRole(_firstAB, uint(Role.Owner), true);
// _updateRole(_firstAB, uint(Role.Member), true);
launchedOn = 0;
}
function memberAtIndex(uint _memberRoleId, uint index) external view returns (address, bool) {
address memberAddress = memberRoleData[_memberRoleId].memberAddress[index];
return (memberAddress, memberRoleData[_memberRoleId].memberActive[memberAddress]);
}
function membersLength(uint _memberRoleId) external view returns (uint) {
return memberRoleData[_memberRoleId].memberAddress.length;
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";
import "../../interfaces/IPooledStaking.sol";
import "./NXMToken.sol";
import "./external/IERC1132.sol";
contract TokenController is IERC1132, Iupgradable {
using SafeMath for uint256;
event Burned(address indexed member, bytes32 lockedUnder, uint256 amount);
NXMToken public token;
IPooledStaking public pooledStaking;
uint public minCALockTime = uint(30).mul(1 days);
bytes32 private constant CLA = bytes32("CLA");
/**
* @dev Just for interface
*/
function changeDependentContractAddress() public {
token = NXMToken(ms.tokenAddress());
pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
}
/**
* @dev to change the operator address
* @param _newOperator is the new address of operator
*/
function changeOperator(address _newOperator) public onlyInternal {
token.changeOperator(_newOperator);
}
/**
* @dev Proxies token transfer through this contract to allow staking when members are locked for voting
* @param _from Source address
* @param _to Destination address
* @param _value Amount to transfer
*/
function operatorTransfer(address _from, address _to, uint _value) onlyInternal external returns (bool) {
require(msg.sender == address(pooledStaking), "Call is only allowed from PooledStaking address");
require(token.operatorTransfer(_from, _value), "Operator transfer failed");
require(token.transfer(_to, _value), "Internal transfer failed");
return true;
}
/**
* @dev Locks a specified amount of tokens,
* for CLA reason and for a specified time
* @param _reason The reason to lock tokens, currently restricted to CLA
* @param _amount Number of tokens to be locked
* @param _time Lock time in seconds
*/
function lock(bytes32 _reason, uint256 _amount, uint256 _time) public checkPause returns (bool)
{
require(_reason == CLA, "Restricted to reason CLA");
require(minCALockTime <= _time, "Should lock for minimum time");
// If tokens are already locked, then functions extendLock or
// increaseLockAmount should be used to make any changes
_lock(msg.sender, _reason, _amount, _time);
return true;
}
/**
* @dev Locks a specified amount of tokens against an address,
* for a specified reason and time
* @param _reason The reason to lock tokens
* @param _amount Number of tokens to be locked
* @param _time Lock time in seconds
* @param _of address whose tokens are to be locked
*/
function lockOf(address _of, bytes32 _reason, uint256 _amount, uint256 _time)
public
onlyInternal
returns (bool)
{
// If tokens are already locked, then functions extendLock or
// increaseLockAmount should be used to make any changes
_lock(_of, _reason, _amount, _time);
return true;
}
/**
* @dev Extends lock for reason CLA for a specified time
* @param _reason The reason to lock tokens, currently restricted to CLA
* @param _time Lock extension time in seconds
*/
function extendLock(bytes32 _reason, uint256 _time)
public
checkPause
returns (bool)
{
require(_reason == CLA, "Restricted to reason CLA");
_extendLock(msg.sender, _reason, _time);
return true;
}
/**
* @dev Extends lock for a specified reason and time
* @param _reason The reason to lock tokens
* @param _time Lock extension time in seconds
*/
function extendLockOf(address _of, bytes32 _reason, uint256 _time)
public
onlyInternal
returns (bool)
{
_extendLock(_of, _reason, _time);
return true;
}
/**
* @dev Increase number of tokens locked for a CLA reason
* @param _reason The reason to lock tokens, currently restricted to CLA
* @param _amount Number of tokens to be increased
*/
function increaseLockAmount(bytes32 _reason, uint256 _amount)
public
checkPause
returns (bool)
{
require(_reason == CLA, "Restricted to reason CLA");
require(_tokensLocked(msg.sender, _reason) > 0);
token.operatorTransfer(msg.sender, _amount);
locked[msg.sender][_reason].amount = locked[msg.sender][_reason].amount.add(_amount);
emit Locked(msg.sender, _reason, _amount, locked[msg.sender][_reason].validity);
return true;
}
/**
* @dev burns tokens of an address
* @param _of is the address to burn tokens of
* @param amount is the amount to burn
* @return the boolean status of the burning process
*/
function burnFrom(address _of, uint amount) public onlyInternal returns (bool) {
return token.burnFrom(_of, amount);
}
/**
* @dev Burns locked tokens of a user
* @param _of address whose tokens are to be burned
* @param _reason lock reason for which tokens are to be burned
* @param _amount amount of tokens to burn
*/
function burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) public onlyInternal {
_burnLockedTokens(_of, _reason, _amount);
}
/**
* @dev reduce lock duration for a specified reason and time
* @param _of The address whose tokens are locked
* @param _reason The reason to lock tokens
* @param _time Lock reduction time in seconds
*/
function reduceLock(address _of, bytes32 _reason, uint256 _time) public onlyInternal {
_reduceLock(_of, _reason, _time);
}
/**
* @dev Released locked tokens of an address locked for a specific reason
* @param _of address whose tokens are to be released from lock
* @param _reason reason of the lock
* @param _amount amount of tokens to release
*/
function releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount)
public
onlyInternal
{
_releaseLockedTokens(_of, _reason, _amount);
}
/**
* @dev Adds an address to whitelist maintained in the contract
* @param _member address to add to whitelist
*/
function addToWhitelist(address _member) public onlyInternal {
token.addToWhiteList(_member);
}
/**
* @dev Removes an address from the whitelist in the token
* @param _member address to remove
*/
function removeFromWhitelist(address _member) public onlyInternal {
token.removeFromWhiteList(_member);
}
/**
* @dev Mints new token for an address
* @param _member address to reward the minted tokens
* @param _amount number of tokens to mint
*/
function mint(address _member, uint _amount) public onlyInternal {
token.mint(_member, _amount);
}
/**
* @dev Lock the user's tokens
* @param _of user's address.
*/
function lockForMemberVote(address _of, uint _days) public onlyInternal {
token.lockForMemberVote(_of, _days);
}
/**
* @dev Unlocks the unlockable tokens against CLA of a specified address
* @param _of Address of user, claiming back unlockable tokens against CLA
*/
function unlock(address _of)
public
checkPause
returns (uint256 unlockableTokens)
{
unlockableTokens = _tokensUnlockable(_of, CLA);
if (unlockableTokens > 0) {
locked[_of][CLA].claimed = true;
emit Unlocked(_of, CLA, unlockableTokens);
require(token.transfer(_of, unlockableTokens));
}
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "MNCLT") {
minCALockTime = val.mul(1 days);
} else {
revert("Invalid param code");
}
}
/**
* @dev Gets the validity of locked tokens of a specified address
* @param _of The address to query the validity
* @param reason reason for which tokens were locked
*/
function getLockedTokensValidity(address _of, bytes32 reason)
public
view
returns (uint256 validity)
{
validity = locked[_of][reason].validity;
}
/**
* @dev Gets the unlockable tokens of a specified address
* @param _of The address to query the the unlockable token count of
*/
function getUnlockableTokens(address _of)
public
view
returns (uint256 unlockableTokens)
{
for (uint256 i = 0; i < lockReason[_of].length; i++) {
unlockableTokens = unlockableTokens.add(_tokensUnlockable(_of, lockReason[_of][i]));
}
}
/**
* @dev Returns tokens locked for a specified address for a
* specified reason
*
* @param _of The address whose tokens are locked
* @param _reason The reason to query the lock tokens for
*/
function tokensLocked(address _of, bytes32 _reason)
public
view
returns (uint256 amount)
{
return _tokensLocked(_of, _reason);
}
/**
* @dev Returns unlockable tokens for a specified address for a specified reason
* @param _of The address to query the the unlockable token count of
* @param _reason The reason to query the unlockable tokens for
*/
function tokensUnlockable(address _of, bytes32 _reason)
public
view
returns (uint256 amount)
{
return _tokensUnlockable(_of, _reason);
}
function totalSupply() public view returns (uint256)
{
return token.totalSupply();
}
/**
* @dev Returns tokens locked for a specified address for a
* specified reason at a specific time
*
* @param _of The address whose tokens are locked
* @param _reason The reason to query the lock tokens for
* @param _time The timestamp to query the lock tokens for
*/
function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time)
public
view
returns (uint256 amount)
{
return _tokensLockedAtTime(_of, _reason, _time);
}
/**
* @dev Returns the total amount of tokens held by an address:
* transferable + locked + staked for pooled staking - pending burns.
* Used by Claims and Governance in member voting to calculate the user's vote weight.
*
* @param _of The address to query the total balance of
* @param _of The address to query the total balance of
*/
function totalBalanceOf(address _of) public view returns (uint256 amount) {
amount = token.balanceOf(_of);
for (uint256 i = 0; i < lockReason[_of].length; i++) {
amount = amount.add(_tokensLocked(_of, lockReason[_of][i]));
}
uint stakerReward = pooledStaking.stakerReward(_of);
uint stakerDeposit = pooledStaking.stakerDeposit(_of);
amount = amount.add(stakerDeposit).add(stakerReward);
}
/**
* @dev Returns the total locked tokens at time
* Returns the total amount of locked and staked tokens at a given time. Used by MemberRoles to check eligibility
* for withdraw / switch membership. Includes tokens locked for Claim Assessment and staked for Risk Assessment.
* Does not take into account pending burns.
*
* @param _of member whose locked tokens are to be calculate
* @param _time timestamp when the tokens should be locked
*/
function totalLockedBalance(address _of, uint256 _time) public view returns (uint256 amount) {
for (uint256 i = 0; i < lockReason[_of].length; i++) {
amount = amount.add(_tokensLockedAtTime(_of, lockReason[_of][i], _time));
}
amount = amount.add(pooledStaking.stakerDeposit(_of));
}
/**
* @dev Locks a specified amount of tokens against an address,
* for a specified reason and time
* @param _of address whose tokens are to be locked
* @param _reason The reason to lock tokens
* @param _amount Number of tokens to be locked
* @param _time Lock time in seconds
*/
function _lock(address _of, bytes32 _reason, uint256 _amount, uint256 _time) internal {
require(_tokensLocked(_of, _reason) == 0);
require(_amount != 0);
if (locked[_of][_reason].amount == 0) {
lockReason[_of].push(_reason);
}
require(token.operatorTransfer(_of, _amount));
uint256 validUntil = now.add(_time); // solhint-disable-line
locked[_of][_reason] = LockToken(_amount, validUntil, false);
emit Locked(_of, _reason, _amount, validUntil);
}
/**
* @dev Returns tokens locked for a specified address for a
* specified reason
*
* @param _of The address whose tokens are locked
* @param _reason The reason to query the lock tokens for
*/
function _tokensLocked(address _of, bytes32 _reason)
internal
view
returns (uint256 amount)
{
if (!locked[_of][_reason].claimed) {
amount = locked[_of][_reason].amount;
}
}
/**
* @dev Returns tokens locked for a specified address for a
* specified reason at a specific time
*
* @param _of The address whose tokens are locked
* @param _reason The reason to query the lock tokens for
* @param _time The timestamp to query the lock tokens for
*/
function _tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time)
internal
view
returns (uint256 amount)
{
if (locked[_of][_reason].validity > _time) {
amount = locked[_of][_reason].amount;
}
}
/**
* @dev Extends lock for a specified reason and time
* @param _of The address whose tokens are locked
* @param _reason The reason to lock tokens
* @param _time Lock extension time in seconds
*/
function _extendLock(address _of, bytes32 _reason, uint256 _time) internal {
require(_tokensLocked(_of, _reason) > 0);
emit Unlocked(_of, _reason, locked[_of][_reason].amount);
locked[_of][_reason].validity = locked[_of][_reason].validity.add(_time);
emit Locked(_of, _reason, locked[_of][_reason].amount, locked[_of][_reason].validity);
}
/**
* @dev reduce lock duration for a specified reason and time
* @param _of The address whose tokens are locked
* @param _reason The reason to lock tokens
* @param _time Lock reduction time in seconds
*/
function _reduceLock(address _of, bytes32 _reason, uint256 _time) internal {
require(_tokensLocked(_of, _reason) > 0);
emit Unlocked(_of, _reason, locked[_of][_reason].amount);
locked[_of][_reason].validity = locked[_of][_reason].validity.sub(_time);
emit Locked(_of, _reason, locked[_of][_reason].amount, locked[_of][_reason].validity);
}
/**
* @dev Returns unlockable tokens for a specified address for a specified reason
* @param _of The address to query the the unlockable token count of
* @param _reason The reason to query the unlockable tokens for
*/
function _tokensUnlockable(address _of, bytes32 _reason) internal view returns (uint256 amount)
{
if (locked[_of][_reason].validity <= now && !locked[_of][_reason].claimed) {
amount = locked[_of][_reason].amount;
}
}
/**
* @dev Burns locked tokens of a user
* @param _of address whose tokens are to be burned
* @param _reason lock reason for which tokens are to be burned
* @param _amount amount of tokens to burn
*/
function _burnLockedTokens(address _of, bytes32 _reason, uint256 _amount) internal {
uint256 amount = _tokensLocked(_of, _reason);
require(amount >= _amount);
if (amount == _amount) {
locked[_of][_reason].claimed = true;
}
locked[_of][_reason].amount = locked[_of][_reason].amount.sub(_amount);
if (locked[_of][_reason].amount == 0) {
_removeReason(_of, _reason);
}
token.burn(_amount);
emit Burned(_of, _reason, _amount);
}
/**
* @dev Released locked tokens of an address locked for a specific reason
* @param _of address whose tokens are to be released from lock
* @param _reason reason of the lock
* @param _amount amount of tokens to release
*/
function _releaseLockedTokens(address _of, bytes32 _reason, uint256 _amount) internal
{
uint256 amount = _tokensLocked(_of, _reason);
require(amount >= _amount);
if (amount == _amount) {
locked[_of][_reason].claimed = true;
}
locked[_of][_reason].amount = locked[_of][_reason].amount.sub(_amount);
if (locked[_of][_reason].amount == 0) {
_removeReason(_of, _reason);
}
require(token.transfer(_of, _amount));
emit Unlocked(_of, _reason, _amount);
}
function _removeReason(address _of, bytes32 _reason) internal {
uint len = lockReason[_of].length;
for (uint i = 0; i < len; i++) {
if (lockReason[_of][i] == _reason) {
lockReason[_of][i] = lockReason[_of][len.sub(1)];
lockReason[_of].pop();
break;
}
}
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";
contract TokenData is Iupgradable {
using SafeMath for uint;
address payable public walletAddress;
uint public lockTokenTimeAfterCoverExp;
uint public bookTime;
uint public lockCADays;
uint public lockMVDays;
uint public scValidDays;
uint public joiningFee;
uint public stakerCommissionPer;
uint public stakerMaxCommissionPer;
uint public tokenExponent;
uint public priceStep;
struct StakeCommission {
uint commissionEarned;
uint commissionRedeemed;
}
struct Stake {
address stakedContractAddress;
uint stakedContractIndex;
uint dateAdd;
uint stakeAmount;
uint unlockedAmount;
uint burnedAmount;
uint unLockableBeforeLastBurn;
}
struct Staker {
address stakerAddress;
uint stakerIndex;
}
struct CoverNote {
uint amount;
bool isDeposited;
}
/**
* @dev mapping of uw address to array of sc address to fetch
* all staked contract address of underwriter, pushing
* data into this array of Stake returns stakerIndex
*/
mapping(address => Stake[]) public stakerStakedContracts;
/**
* @dev mapping of sc address to array of UW address to fetch
* all underwritters of the staked smart contract
* pushing data into this mapped array returns scIndex
*/
mapping(address => Staker[]) public stakedContractStakers;
/**
* @dev mapping of staked contract Address to the array of StakeCommission
* here index of this array is stakedContractIndex
*/
mapping(address => mapping(uint => StakeCommission)) public stakedContractStakeCommission;
mapping(address => uint) public lastCompletedStakeCommission;
/**
* @dev mapping of the staked contract address to the current
* staker index who will receive commission.
*/
mapping(address => uint) public stakedContractCurrentCommissionIndex;
/**
* @dev mapping of the staked contract address to the
* current staker index to burn token from.
*/
mapping(address => uint) public stakedContractCurrentBurnIndex;
/**
* @dev mapping to return true if Cover Note deposited against coverId
*/
mapping(uint => CoverNote) public depositedCN;
mapping(address => uint) internal isBookedTokens;
event Commission(
address indexed stakedContractAddress,
address indexed stakerAddress,
uint indexed scIndex,
uint commissionAmount
);
constructor(address payable _walletAdd) public {
walletAddress = _walletAdd;
bookTime = 12 hours;
joiningFee = 2000000000000000; // 0.002 Ether
lockTokenTimeAfterCoverExp = 35 days;
scValidDays = 250;
lockCADays = 7 days;
lockMVDays = 2 days;
stakerCommissionPer = 20;
stakerMaxCommissionPer = 50;
tokenExponent = 4;
priceStep = 1000;
}
/**
* @dev Change the wallet address which receive Joining Fee
*/
function changeWalletAddress(address payable _address) external onlyInternal {
walletAddress = _address;
}
/**
* @dev Gets Uint Parameters of a code
* @param code whose details we want
* @return string value of the code
* @return associated amount (time or perc or value) to the code
*/
function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
codeVal = code;
if (code == "TOKEXP") {
val = tokenExponent;
} else if (code == "TOKSTEP") {
val = priceStep;
} else if (code == "RALOCKT") {
val = scValidDays;
} else if (code == "RACOMM") {
val = stakerCommissionPer;
} else if (code == "RAMAXC") {
val = stakerMaxCommissionPer;
} else if (code == "CABOOKT") {
val = bookTime / (1 hours);
} else if (code == "CALOCKT") {
val = lockCADays / (1 days);
} else if (code == "MVLOCKT") {
val = lockMVDays / (1 days);
} else if (code == "QUOLOCKT") {
val = lockTokenTimeAfterCoverExp / (1 days);
} else if (code == "JOINFEE") {
val = joiningFee;
}
}
/**
* @dev Just for interface
*/
function changeDependentContractAddress() public {//solhint-disable-line
}
/**
* @dev to get the contract staked by a staker
* @param _stakerAddress is the address of the staker
* @param _stakerIndex is the index of staker
* @return the address of staked contract
*/
function getStakerStakedContractByIndex(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (address stakedContractAddress)
{
stakedContractAddress = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractAddress;
}
/**
* @dev to get the staker's staked burned
* @param _stakerAddress is the address of the staker
* @param _stakerIndex is the index of staker
* @return amount burned
*/
function getStakerStakedBurnedByIndex(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint burnedAmount)
{
burnedAmount = stakerStakedContracts[
_stakerAddress][_stakerIndex].burnedAmount;
}
/**
* @dev to get the staker's staked unlockable before the last burn
* @param _stakerAddress is the address of the staker
* @param _stakerIndex is the index of staker
* @return unlockable staked tokens
*/
function getStakerStakedUnlockableBeforeLastBurnByIndex(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint unlockable)
{
unlockable = stakerStakedContracts[
_stakerAddress][_stakerIndex].unLockableBeforeLastBurn;
}
/**
* @dev to get the staker's staked contract index
* @param _stakerAddress is the address of the staker
* @param _stakerIndex is the index of staker
* @return is the index of the smart contract address
*/
function getStakerStakedContractIndex(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint scIndex)
{
scIndex = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractIndex;
}
/**
* @dev to get the staker index of the staked contract
* @param _stakedContractAddress is the address of the staked contract
* @param _stakedContractIndex is the index of staked contract
* @return is the index of the staker
*/
function getStakedContractStakerIndex(
address _stakedContractAddress,
uint _stakedContractIndex
)
public
view
returns (uint sIndex)
{
sIndex = stakedContractStakers[
_stakedContractAddress][_stakedContractIndex].stakerIndex;
}
/**
* @dev to get the staker's initial staked amount on the contract
* @param _stakerAddress is the address of the staker
* @param _stakerIndex is the index of staker
* @return staked amount
*/
function getStakerInitialStakedAmountOnContract(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint amount)
{
amount = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakeAmount;
}
/**
* @dev to get the staker's staked contract length
* @param _stakerAddress is the address of the staker
* @return length of staked contract
*/
function getStakerStakedContractLength(
address _stakerAddress
)
public
view
returns (uint length)
{
length = stakerStakedContracts[_stakerAddress].length;
}
/**
* @dev to get the staker's unlocked tokens which were staked
* @param _stakerAddress is the address of the staker
* @param _stakerIndex is the index of staker
* @return amount
*/
function getStakerUnlockedStakedTokens(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint amount)
{
amount = stakerStakedContracts[
_stakerAddress][_stakerIndex].unlockedAmount;
}
/**
* @dev pushes the unlocked staked tokens by a staker.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker to distribute commission.
* @param _amount amount to be given as commission.
*/
function pushUnlockedStakedTokens(
address _stakerAddress,
uint _stakerIndex,
uint _amount
)
public
onlyInternal
{
stakerStakedContracts[_stakerAddress][
_stakerIndex].unlockedAmount = stakerStakedContracts[_stakerAddress][
_stakerIndex].unlockedAmount.add(_amount);
}
/**
* @dev pushes the Burned tokens for a staker.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker.
* @param _amount amount to be burned.
*/
function pushBurnedTokens(
address _stakerAddress,
uint _stakerIndex,
uint _amount
)
public
onlyInternal
{
stakerStakedContracts[_stakerAddress][
_stakerIndex].burnedAmount = stakerStakedContracts[_stakerAddress][
_stakerIndex].burnedAmount.add(_amount);
}
/**
* @dev pushes the unLockable tokens for a staker before last burn.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker.
* @param _amount amount to be added to unlockable.
*/
function pushUnlockableBeforeLastBurnTokens(
address _stakerAddress,
uint _stakerIndex,
uint _amount
)
public
onlyInternal
{
stakerStakedContracts[_stakerAddress][
_stakerIndex].unLockableBeforeLastBurn = stakerStakedContracts[_stakerAddress][
_stakerIndex].unLockableBeforeLastBurn.add(_amount);
}
/**
* @dev sets the unLockable tokens for a staker before last burn.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker.
* @param _amount amount to be added to unlockable.
*/
function setUnlockableBeforeLastBurnTokens(
address _stakerAddress,
uint _stakerIndex,
uint _amount
)
public
onlyInternal
{
stakerStakedContracts[_stakerAddress][
_stakerIndex].unLockableBeforeLastBurn = _amount;
}
/**
* @dev pushes the earned commission earned by a staker.
* @param _stakerAddress address of staker.
* @param _stakedContractAddress address of smart contract.
* @param _stakedContractIndex index of the staker to distribute commission.
* @param _commissionAmount amount to be given as commission.
*/
function pushEarnedStakeCommissions(
address _stakerAddress,
address _stakedContractAddress,
uint _stakedContractIndex,
uint _commissionAmount
)
public
onlyInternal
{
stakedContractStakeCommission[_stakedContractAddress][_stakedContractIndex].
commissionEarned = stakedContractStakeCommission[_stakedContractAddress][
_stakedContractIndex].commissionEarned.add(_commissionAmount);
emit Commission(
_stakerAddress,
_stakedContractAddress,
_stakedContractIndex,
_commissionAmount
);
}
/**
* @dev pushes the redeemed commission redeemed by a staker.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker to distribute commission.
* @param _amount amount to be given as commission.
*/
function pushRedeemedStakeCommissions(
address _stakerAddress,
uint _stakerIndex,
uint _amount
)
public
onlyInternal
{
uint stakedContractIndex = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractIndex;
address stakedContractAddress = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractAddress;
stakedContractStakeCommission[stakedContractAddress][stakedContractIndex].
commissionRedeemed = stakedContractStakeCommission[
stakedContractAddress][stakedContractIndex].commissionRedeemed.add(_amount);
}
/**
* @dev Gets stake commission given to an underwriter
* for particular stakedcontract on given index.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker commission.
*/
function getStakerEarnedStakeCommission(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint)
{
return _getStakerEarnedStakeCommission(_stakerAddress, _stakerIndex);
}
/**
* @dev Gets stake commission redeemed by an underwriter
* for particular staked contract on given index.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker commission.
* @return commissionEarned total amount given to staker.
*/
function getStakerRedeemedStakeCommission(
address _stakerAddress,
uint _stakerIndex
)
public
view
returns (uint)
{
return _getStakerRedeemedStakeCommission(_stakerAddress, _stakerIndex);
}
/**
* @dev Gets total stake commission given to an underwriter
* @param _stakerAddress address of staker.
* @return totalCommissionEarned total commission earned by staker.
*/
function getStakerTotalEarnedStakeCommission(
address _stakerAddress
)
public
view
returns (uint totalCommissionEarned)
{
totalCommissionEarned = 0;
for (uint i = 0; i < stakerStakedContracts[_stakerAddress].length; i++) {
totalCommissionEarned = totalCommissionEarned.
add(_getStakerEarnedStakeCommission(_stakerAddress, i));
}
}
/**
* @dev Gets total stake commission given to an underwriter
* @param _stakerAddress address of staker.
* @return totalCommissionEarned total commission earned by staker.
*/
function getStakerTotalReedmedStakeCommission(
address _stakerAddress
)
public
view
returns (uint totalCommissionRedeemed)
{
totalCommissionRedeemed = 0;
for (uint i = 0; i < stakerStakedContracts[_stakerAddress].length; i++) {
totalCommissionRedeemed = totalCommissionRedeemed.add(
_getStakerRedeemedStakeCommission(_stakerAddress, i));
}
}
/**
* @dev set flag to deposit/ undeposit cover note
* against a cover Id
* @param coverId coverId of Cover
* @param flag true/false for deposit/undeposit
*/
function setDepositCN(uint coverId, bool flag) public onlyInternal {
if (flag == true) {
require(!depositedCN[coverId].isDeposited, "Cover note already deposited");
}
depositedCN[coverId].isDeposited = flag;
}
/**
* @dev set locked cover note amount
* against a cover Id
* @param coverId coverId of Cover
* @param amount amount of nxm to be locked
*/
function setDepositCNAmount(uint coverId, uint amount) public onlyInternal {
depositedCN[coverId].amount = amount;
}
/**
* @dev to get the staker address on a staked contract
* @param _stakedContractAddress is the address of the staked contract in concern
* @param _stakedContractIndex is the index of staked contract's index
* @return address of staker
*/
function getStakedContractStakerByIndex(
address _stakedContractAddress,
uint _stakedContractIndex
)
public
view
returns (address stakerAddress)
{
stakerAddress = stakedContractStakers[
_stakedContractAddress][_stakedContractIndex].stakerAddress;
}
/**
* @dev to get the length of stakers on a staked contract
* @param _stakedContractAddress is the address of the staked contract in concern
* @return length in concern
*/
function getStakedContractStakersLength(
address _stakedContractAddress
)
public
view
returns (uint length)
{
length = stakedContractStakers[_stakedContractAddress].length;
}
/**
* @dev Adds a new stake record.
* @param _stakerAddress staker address.
* @param _stakedContractAddress smart contract address.
* @param _amount amountof NXM to be staked.
*/
function addStake(
address _stakerAddress,
address _stakedContractAddress,
uint _amount
)
public
onlyInternal
returns (uint scIndex)
{
scIndex = (stakedContractStakers[_stakedContractAddress].push(
Staker(_stakerAddress, stakerStakedContracts[_stakerAddress].length))).sub(1);
stakerStakedContracts[_stakerAddress].push(
Stake(_stakedContractAddress, scIndex, now, _amount, 0, 0, 0));
}
/**
* @dev books the user's tokens for maintaining Assessor Velocity,
* i.e. once a token is used to cast a vote as a Claims assessor,
* @param _of user's address.
*/
function bookCATokens(address _of) public onlyInternal {
require(!isCATokensBooked(_of), "Tokens already booked");
isBookedTokens[_of] = now.add(bookTime);
}
/**
* @dev to know if claim assessor's tokens are booked or not
* @param _of is the claim assessor's address in concern
* @return boolean representing the status of tokens booked
*/
function isCATokensBooked(address _of) public view returns (bool res) {
if (now < isBookedTokens[_of])
res = true;
}
/**
* @dev Sets the index which will receive commission.
* @param _stakedContractAddress smart contract address.
* @param _index current index.
*/
function setStakedContractCurrentCommissionIndex(
address _stakedContractAddress,
uint _index
)
public
onlyInternal
{
stakedContractCurrentCommissionIndex[_stakedContractAddress] = _index;
}
/**
* @dev Sets the last complete commission index
* @param _stakerAddress smart contract address.
* @param _index current index.
*/
function setLastCompletedStakeCommissionIndex(
address _stakerAddress,
uint _index
)
public
onlyInternal
{
lastCompletedStakeCommission[_stakerAddress] = _index;
}
/**
* @dev Sets the index till which commission is distrubuted.
* @param _stakedContractAddress smart contract address.
* @param _index current index.
*/
function setStakedContractCurrentBurnIndex(
address _stakedContractAddress,
uint _index
)
public
onlyInternal
{
stakedContractCurrentBurnIndex[_stakedContractAddress] = _index;
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "TOKEXP") {
_setTokenExponent(val);
} else if (code == "TOKSTEP") {
_setPriceStep(val);
} else if (code == "RALOCKT") {
_changeSCValidDays(val);
} else if (code == "RACOMM") {
_setStakerCommissionPer(val);
} else if (code == "RAMAXC") {
_setStakerMaxCommissionPer(val);
} else if (code == "CABOOKT") {
_changeBookTime(val * 1 hours);
} else if (code == "CALOCKT") {
_changelockCADays(val * 1 days);
} else if (code == "MVLOCKT") {
_changelockMVDays(val * 1 days);
} else if (code == "QUOLOCKT") {
_setLockTokenTimeAfterCoverExp(val * 1 days);
} else if (code == "JOINFEE") {
_setJoiningFee(val);
} else {
revert("Invalid param code");
}
}
/**
* @dev Internal function to get stake commission given to an
* underwriter for particular stakedcontract on given index.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker commission.
*/
function _getStakerEarnedStakeCommission(
address _stakerAddress,
uint _stakerIndex
)
internal
view
returns (uint amount)
{
uint _stakedContractIndex;
address _stakedContractAddress;
_stakedContractAddress = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractAddress;
_stakedContractIndex = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractIndex;
amount = stakedContractStakeCommission[
_stakedContractAddress][_stakedContractIndex].commissionEarned;
}
/**
* @dev Internal function to get stake commission redeemed by an
* underwriter for particular stakedcontract on given index.
* @param _stakerAddress address of staker.
* @param _stakerIndex index of the staker commission.
*/
function _getStakerRedeemedStakeCommission(
address _stakerAddress,
uint _stakerIndex
)
internal
view
returns (uint amount)
{
uint _stakedContractIndex;
address _stakedContractAddress;
_stakedContractAddress = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractAddress;
_stakedContractIndex = stakerStakedContracts[
_stakerAddress][_stakerIndex].stakedContractIndex;
amount = stakedContractStakeCommission[
_stakedContractAddress][_stakedContractIndex].commissionRedeemed;
}
/**
* @dev to set the percentage of staker commission
* @param _val is new percentage value
*/
function _setStakerCommissionPer(uint _val) internal {
stakerCommissionPer = _val;
}
/**
* @dev to set the max percentage of staker commission
* @param _val is new percentage value
*/
function _setStakerMaxCommissionPer(uint _val) internal {
stakerMaxCommissionPer = _val;
}
/**
* @dev to set the token exponent value
* @param _val is new value
*/
function _setTokenExponent(uint _val) internal {
tokenExponent = _val;
}
/**
* @dev to set the price step
* @param _val is new value
*/
function _setPriceStep(uint _val) internal {
priceStep = _val;
}
/**
* @dev Changes number of days for which NXM needs to staked in case of underwriting
*/
function _changeSCValidDays(uint _days) internal {
scValidDays = _days;
}
/**
* @dev Changes the time period up to which tokens will be locked.
* Used to generate the validity period of tokens booked by
* a user for participating in claim's assessment/claim's voting.
*/
function _changeBookTime(uint _time) internal {
bookTime = _time;
}
/**
* @dev Changes lock CA days - number of days for which tokens
* are locked while submitting a vote.
*/
function _changelockCADays(uint _val) internal {
lockCADays = _val;
}
/**
* @dev Changes lock MV days - number of days for which tokens are locked
* while submitting a vote.
*/
function _changelockMVDays(uint _val) internal {
lockMVDays = _val;
}
/**
* @dev Changes extra lock period for a cover, post its expiry.
*/
function _setLockTokenTimeAfterCoverExp(uint time) internal {
lockTokenTimeAfterCoverExp = time;
}
/**
* @dev Set the joining fee for membership
*/
function _setJoiningFee(uint _amount) internal {
joiningFee = _amount;
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../interfaces/IPooledStaking.sol";
import "../cover/QuotationData.sol";
import "./NXMToken.sol";
import "./TokenController.sol";
import "./TokenData.sol";
contract TokenFunctions is Iupgradable {
using SafeMath for uint;
NXMToken public tk;
TokenController public tc;
TokenData public td;
QuotationData public qd;
IPooledStaking public pooledStaking;
event BurnCATokens(uint claimId, address addr, uint amount);
/**
* @dev Rewards stakers on purchase of cover on smart contract.
* @param _contractAddress smart contract address.
* @param _coverPriceNXM cover price in NXM.
*/
function pushStakerRewards(address _contractAddress, uint _coverPriceNXM) external onlyInternal {
uint rewardValue = _coverPriceNXM.mul(td.stakerCommissionPer()).div(100);
pooledStaking.accumulateReward(_contractAddress, rewardValue);
}
/**
* @dev Returns amount of NXM Tokens locked as Cover Note for given coverId.
* @param _of address of the coverHolder.
* @param _coverId coverId of the cover.
*/
function getUserLockedCNTokens(address _of, uint _coverId) external view returns (uint) {
return _getUserLockedCNTokens(_of, _coverId);
}
/**
* @dev to get the all the cover locked tokens of a user
* @param _of is the user address in concern
* @return amount locked
*/
function getUserAllLockedCNTokens(address _of) external view returns (uint amount) {
for (uint i = 0; i < qd.getUserCoverLength(_of); i++) {
amount = amount.add(_getUserLockedCNTokens(_of, qd.getAllCoversOfUser(_of)[i]));
}
}
/**
* @dev Returns amount of NXM Tokens locked as Cover Note against given coverId.
* @param _coverId coverId of the cover.
*/
function getLockedCNAgainstCover(uint _coverId) external view returns (uint) {
return _getLockedCNAgainstCover(_coverId);
}
/**
* @dev Change Dependent Contract Address
*/
function changeDependentContractAddress() public {
tk = NXMToken(ms.tokenAddress());
td = TokenData(ms.getLatestAddress("TD"));
tc = TokenController(ms.getLatestAddress("TC"));
qd = QuotationData(ms.getLatestAddress("QD"));
pooledStaking = IPooledStaking(ms.getLatestAddress("PS"));
}
/**
* @dev Set the flag to check if cover note is deposited against the cover id
* @param coverId Cover Id.
*/
function depositCN(uint coverId) public onlyInternal returns (bool success) {
require(_getLockedCNAgainstCover(coverId) > 0, "No cover note available");
td.setDepositCN(coverId, true);
success = true;
}
/**
* @param _of address of Member
* @param _coverId Cover Id
* @param _lockTime Pending Time + Cover Period 7*1 days
*/
function extendCNEPOff(address _of, uint _coverId, uint _lockTime) public onlyInternal {
uint timeStamp = now.add(_lockTime);
uint coverValidUntil = qd.getValidityOfCover(_coverId);
if (timeStamp >= coverValidUntil) {
bytes32 reason = keccak256(abi.encodePacked("CN", _of, _coverId));
tc.extendLockOf(_of, reason, timeStamp);
}
}
/**
* @dev to burn the deposited cover tokens
* @param coverId is id of cover whose tokens have to be burned
* @return the status of the successful burning
*/
function burnDepositCN(uint coverId) public onlyInternal returns (bool success) {
address _of = qd.getCoverMemberAddress(coverId);
uint amount;
(amount,) = td.depositedCN(coverId);
amount = (amount.mul(50)).div(100);
bytes32 reason = keccak256(abi.encodePacked("CN", _of, coverId));
tc.burnLockedTokens(_of, reason, amount);
success = true;
}
/**
* @dev Unlocks covernote locked against a given cover
* @param coverId id of cover
*/
function unlockCN(uint coverId) public onlyInternal {
(, bool isDeposited) = td.depositedCN(coverId);
require(!isDeposited, "Cover note is deposited and can not be released");
uint lockedCN = _getLockedCNAgainstCover(coverId);
if (lockedCN != 0) {
address coverHolder = qd.getCoverMemberAddress(coverId);
bytes32 reason = keccak256(abi.encodePacked("CN", coverHolder, coverId));
tc.releaseLockedTokens(coverHolder, reason, lockedCN);
}
}
/**
* @dev Burns tokens used for fraudulent voting against a claim
* @param claimid Claim Id.
* @param _value number of tokens to be burned
* @param _of Claim Assessor's address.
*/
function burnCAToken(uint claimid, uint _value, address _of) public {
require(ms.checkIsAuthToGoverned(msg.sender));
tc.burnLockedTokens(_of, "CLA", _value);
emit BurnCATokens(claimid, _of, _value);
}
/**
* @dev to lock cover note tokens
* @param coverNoteAmount is number of tokens to be locked
* @param coverPeriod is cover period in concern
* @param coverId is the cover id of cover in concern
* @param _of address whose tokens are to be locked
*/
function lockCN(
uint coverNoteAmount,
uint coverPeriod,
uint coverId,
address _of
)
public
onlyInternal
{
uint validity = (coverPeriod * 1 days).add(td.lockTokenTimeAfterCoverExp());
bytes32 reason = keccak256(abi.encodePacked("CN", _of, coverId));
td.setDepositCNAmount(coverId, coverNoteAmount);
tc.lockOf(_of, reason, coverNoteAmount, validity);
}
/**
* @dev to check if a member is locked for member vote
* @param _of is the member address in concern
* @return the boolean status
*/
function isLockedForMemberVote(address _of) public view returns (bool) {
return now < tk.isLockedForMV(_of);
}
/**
* @dev Returns amount of NXM Tokens locked as Cover Note for given coverId.
* @param _coverId coverId of the cover.
*/
function _getLockedCNAgainstCover(uint _coverId) internal view returns (uint) {
address coverHolder = qd.getCoverMemberAddress(_coverId);
bytes32 reason = keccak256(abi.encodePacked("CN", coverHolder, _coverId));
return tc.tokensLockedAtTime(coverHolder, reason, now);
}
/**
* @dev Returns amount of NXM Tokens locked as Cover Note for given coverId.
* @param _of address of the coverHolder.
* @param _coverId coverId of the cover.
*/
function _getUserLockedCNTokens(address _of, uint _coverId) internal view returns (uint) {
bytes32 reason = keccak256(abi.encodePacked("CN", _of, _coverId));
return tc.tokensLockedAtTime(_of, reason, now);
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";
contract QuotationData is Iupgradable {
using SafeMath for uint;
enum HCIDStatus {NA, kycPending, kycPass, kycFailedOrRefunded, kycPassNoCover}
enum CoverStatus {Active, ClaimAccepted, ClaimDenied, CoverExpired, ClaimSubmitted, Requested}
struct Cover {
address payable memberAddress;
bytes4 currencyCode;
uint sumAssured;
uint16 coverPeriod;
uint validUntil;
address scAddress;
uint premiumNXM;
}
struct HoldCover {
uint holdCoverId;
address payable userAddress;
address scAddress;
bytes4 coverCurr;
uint[] coverDetails;
uint16 coverPeriod;
}
address public authQuoteEngine;
mapping(bytes4 => uint) internal currencyCSA;
mapping(address => uint[]) internal userCover;
mapping(address => uint[]) public userHoldedCover;
mapping(address => bool) public refundEligible;
mapping(address => mapping(bytes4 => uint)) internal currencyCSAOfSCAdd;
mapping(uint => uint8) public coverStatus;
mapping(uint => uint) public holdedCoverIDStatus;
mapping(uint => bool) public timestampRepeated;
Cover[] internal allCovers;
HoldCover[] internal allCoverHolded;
uint public stlp;
uint public stl;
uint public pm;
uint public minDays;
uint public tokensRetained;
address public kycAuthAddress;
event CoverDetailsEvent(
uint indexed cid,
address scAdd,
uint sumAssured,
uint expiry,
uint premium,
uint premiumNXM,
bytes4 curr
);
event CoverStatusEvent(uint indexed cid, uint8 statusNum);
constructor(address _authQuoteAdd, address _kycAuthAdd) public {
authQuoteEngine = _authQuoteAdd;
kycAuthAddress = _kycAuthAdd;
stlp = 90;
stl = 100;
pm = 30;
minDays = 30;
tokensRetained = 10;
allCovers.push(Cover(address(0), "0x00", 0, 0, 0, address(0), 0));
uint[] memory arr = new uint[](1);
allCoverHolded.push(HoldCover(0, address(0), address(0), 0x00, arr, 0));
}
/// @dev Adds the amount in Total Sum Assured of a given currency of a given smart contract address.
/// @param _add Smart Contract Address.
/// @param _amount Amount to be added.
function addInTotalSumAssuredSC(address _add, bytes4 _curr, uint _amount) external onlyInternal {
currencyCSAOfSCAdd[_add][_curr] = currencyCSAOfSCAdd[_add][_curr].add(_amount);
}
/// @dev Subtracts the amount from Total Sum Assured of a given currency and smart contract address.
/// @param _add Smart Contract Address.
/// @param _amount Amount to be subtracted.
function subFromTotalSumAssuredSC(address _add, bytes4 _curr, uint _amount) external onlyInternal {
currencyCSAOfSCAdd[_add][_curr] = currencyCSAOfSCAdd[_add][_curr].sub(_amount);
}
/// @dev Subtracts the amount from Total Sum Assured of a given currency.
/// @param _curr Currency Name.
/// @param _amount Amount to be subtracted.
function subFromTotalSumAssured(bytes4 _curr, uint _amount) external onlyInternal {
currencyCSA[_curr] = currencyCSA[_curr].sub(_amount);
}
/// @dev Adds the amount in Total Sum Assured of a given currency.
/// @param _curr Currency Name.
/// @param _amount Amount to be added.
function addInTotalSumAssured(bytes4 _curr, uint _amount) external onlyInternal {
currencyCSA[_curr] = currencyCSA[_curr].add(_amount);
}
/// @dev sets bit for timestamp to avoid replay attacks.
function setTimestampRepeated(uint _timestamp) external onlyInternal {
timestampRepeated[_timestamp] = true;
}
/// @dev Creates a blank new cover.
function addCover(
uint16 _coverPeriod,
uint _sumAssured,
address payable _userAddress,
bytes4 _currencyCode,
address _scAddress,
uint premium,
uint premiumNXM
)
external
onlyInternal
{
uint expiryDate = now.add(uint(_coverPeriod).mul(1 days));
allCovers.push(Cover(_userAddress, _currencyCode,
_sumAssured, _coverPeriod, expiryDate, _scAddress, premiumNXM));
uint cid = allCovers.length.sub(1);
userCover[_userAddress].push(cid);
emit CoverDetailsEvent(cid, _scAddress, _sumAssured, expiryDate, premium, premiumNXM, _currencyCode);
}
/// @dev create holded cover which will process after verdict of KYC.
function addHoldCover(
address payable from,
address scAddress,
bytes4 coverCurr,
uint[] calldata coverDetails,
uint16 coverPeriod
)
external
onlyInternal
{
uint holdedCoverLen = allCoverHolded.length;
holdedCoverIDStatus[holdedCoverLen] = uint(HCIDStatus.kycPending);
allCoverHolded.push(HoldCover(holdedCoverLen, from, scAddress,
coverCurr, coverDetails, coverPeriod));
userHoldedCover[from].push(allCoverHolded.length.sub(1));
}
///@dev sets refund eligible bit.
///@param _add user address.
///@param status indicates if user have pending kyc.
function setRefundEligible(address _add, bool status) external onlyInternal {
refundEligible[_add] = status;
}
/// @dev to set current status of particular holded coverID (1 for not completed KYC,
/// 2 for KYC passed, 3 for failed KYC or full refunded,
/// 4 for KYC completed but cover not processed)
function setHoldedCoverIDStatus(uint holdedCoverID, uint status) external onlyInternal {
holdedCoverIDStatus[holdedCoverID] = status;
}
/**
* @dev to set address of kyc authentication
* @param _add is the new address
*/
function setKycAuthAddress(address _add) external onlyInternal {
kycAuthAddress = _add;
}
/// @dev Changes authorised address for generating quote off chain.
function changeAuthQuoteEngine(address _add) external onlyInternal {
authQuoteEngine = _add;
}
/**
* @dev Gets Uint Parameters of a code
* @param code whose details we want
* @return string value of the code
* @return associated amount (time or perc or value) to the code
*/
function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
codeVal = code;
if (code == "STLP") {
val = stlp;
} else if (code == "STL") {
val = stl;
} else if (code == "PM") {
val = pm;
} else if (code == "QUOMIND") {
val = minDays;
} else if (code == "QUOTOK") {
val = tokensRetained;
}
}
/// @dev Gets Product details.
/// @return _minDays minimum cover period.
/// @return _PM Profit margin.
/// @return _STL short term Load.
/// @return _STLP short term load period.
function getProductDetails()
external
view
returns (
uint _minDays,
uint _pm,
uint _stl,
uint _stlp
)
{
_minDays = minDays;
_pm = pm;
_stl = stl;
_stlp = stlp;
}
/// @dev Gets total number covers created till date.
function getCoverLength() external view returns (uint len) {
return (allCovers.length);
}
/// @dev Gets Authorised Engine address.
function getAuthQuoteEngine() external view returns (address _add) {
_add = authQuoteEngine;
}
/// @dev Gets the Total Sum Assured amount of a given currency.
function getTotalSumAssured(bytes4 _curr) external view returns (uint amount) {
amount = currencyCSA[_curr];
}
/// @dev Gets all the Cover ids generated by a given address.
/// @param _add User's address.
/// @return allCover array of covers.
function getAllCoversOfUser(address _add) external view returns (uint[] memory allCover) {
return (userCover[_add]);
}
/// @dev Gets total number of covers generated by a given address
function getUserCoverLength(address _add) external view returns (uint len) {
len = userCover[_add].length;
}
/// @dev Gets the status of a given cover.
function getCoverStatusNo(uint _cid) external view returns (uint8) {
return coverStatus[_cid];
}
/// @dev Gets the Cover Period (in days) of a given cover.
function getCoverPeriod(uint _cid) external view returns (uint32 cp) {
cp = allCovers[_cid].coverPeriod;
}
/// @dev Gets the Sum Assured Amount of a given cover.
function getCoverSumAssured(uint _cid) external view returns (uint sa) {
sa = allCovers[_cid].sumAssured;
}
/// @dev Gets the Currency Name in which a given cover is assured.
function getCurrencyOfCover(uint _cid) external view returns (bytes4 curr) {
curr = allCovers[_cid].currencyCode;
}
/// @dev Gets the validity date (timestamp) of a given cover.
function getValidityOfCover(uint _cid) external view returns (uint date) {
date = allCovers[_cid].validUntil;
}
/// @dev Gets Smart contract address of cover.
function getscAddressOfCover(uint _cid) external view returns (uint, address) {
return (_cid, allCovers[_cid].scAddress);
}
/// @dev Gets the owner address of a given cover.
function getCoverMemberAddress(uint _cid) external view returns (address payable _add) {
_add = allCovers[_cid].memberAddress;
}
/// @dev Gets the premium amount of a given cover in NXM.
function getCoverPremiumNXM(uint _cid) external view returns (uint _premiumNXM) {
_premiumNXM = allCovers[_cid].premiumNXM;
}
/// @dev Provides the details of a cover Id
/// @param _cid cover Id
/// @return memberAddress cover user address.
/// @return scAddress smart contract Address
/// @return currencyCode currency of cover
/// @return sumAssured sum assured of cover
/// @return premiumNXM premium in NXM
function getCoverDetailsByCoverID1(
uint _cid
)
external
view
returns (
uint cid,
address _memberAddress,
address _scAddress,
bytes4 _currencyCode,
uint _sumAssured,
uint premiumNXM
)
{
return (
_cid,
allCovers[_cid].memberAddress,
allCovers[_cid].scAddress,
allCovers[_cid].currencyCode,
allCovers[_cid].sumAssured,
allCovers[_cid].premiumNXM
);
}
/// @dev Provides details of a cover Id
/// @param _cid cover Id
/// @return status status of cover.
/// @return sumAssured Sum assurance of cover.
/// @return coverPeriod Cover Period of cover (in days).
/// @return validUntil is validity of cover.
function getCoverDetailsByCoverID2(
uint _cid
)
external
view
returns (
uint cid,
uint8 status,
uint sumAssured,
uint16 coverPeriod,
uint validUntil
)
{
return (
_cid,
coverStatus[_cid],
allCovers[_cid].sumAssured,
allCovers[_cid].coverPeriod,
allCovers[_cid].validUntil
);
}
/// @dev Provides details of a holded cover Id
/// @param _hcid holded cover Id
/// @return scAddress SmartCover address of cover.
/// @return coverCurr currency of cover.
/// @return coverPeriod Cover Period of cover (in days).
function getHoldedCoverDetailsByID1(
uint _hcid
)
external
view
returns (
uint hcid,
address scAddress,
bytes4 coverCurr,
uint16 coverPeriod
)
{
return (
_hcid,
allCoverHolded[_hcid].scAddress,
allCoverHolded[_hcid].coverCurr,
allCoverHolded[_hcid].coverPeriod
);
}
/// @dev Gets total number holded covers created till date.
function getUserHoldedCoverLength(address _add) external view returns (uint) {
return userHoldedCover[_add].length;
}
/// @dev Gets holded cover index by index of user holded covers.
function getUserHoldedCoverByIndex(address _add, uint index) external view returns (uint) {
return userHoldedCover[_add][index];
}
/// @dev Provides the details of a holded cover Id
/// @param _hcid holded cover Id
/// @return memberAddress holded cover user address.
/// @return coverDetails array contains SA, Cover Currency Price,Price in NXM, Expiration time of Qoute.
function getHoldedCoverDetailsByID2(
uint _hcid
)
external
view
returns (
uint hcid,
address payable memberAddress,
uint[] memory coverDetails
)
{
return (
_hcid,
allCoverHolded[_hcid].userAddress,
allCoverHolded[_hcid].coverDetails
);
}
/// @dev Gets the Total Sum Assured amount of a given currency and smart contract address.
function getTotalSumAssuredSC(address _add, bytes4 _curr) external view returns (uint amount) {
amount = currencyCSAOfSCAdd[_add][_curr];
}
//solhint-disable-next-line
function changeDependentContractAddress() public {}
/// @dev Changes the status of a given cover.
/// @param _cid cover Id.
/// @param _stat New status.
function changeCoverStatusNo(uint _cid, uint8 _stat) public onlyInternal {
coverStatus[_cid] = _stat;
emit CoverStatusEvent(_cid, _stat);
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "STLP") {
_changeSTLP(val);
} else if (code == "STL") {
_changeSTL(val);
} else if (code == "PM") {
_changePM(val);
} else if (code == "QUOMIND") {
_changeMinDays(val);
} else if (code == "QUOTOK") {
_setTokensRetained(val);
} else {
revert("Invalid param code");
}
}
/// @dev Changes the existing Profit Margin value
function _changePM(uint _pm) internal {
pm = _pm;
}
/// @dev Changes the existing Short Term Load Period (STLP) value.
function _changeSTLP(uint _stlp) internal {
stlp = _stlp;
}
/// @dev Changes the existing Short Term Load (STL) value.
function _changeSTL(uint _stl) internal {
stl = _stl;
}
/// @dev Changes the existing Minimum cover period (in days)
function _changeMinDays(uint _days) internal {
minDays = _days;
}
/**
* @dev to set the the amount of tokens retained
* @param val is the amount retained
*/
function _setTokensRetained(uint val) internal {
tokensRetained = val;
}
}pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol";
import "../../abstract/MasterAware.sol";
import "../cover/Quotation.sol";
import "../oracles/PriceFeedOracle.sol";
import "../token/NXMToken.sol";
import "../token/TokenController.sol";
import "./MCR.sol";
import "./SwapAgent.sol";
contract Pool is MasterAware, ReentrancyGuard {
using Address for address;
using SafeMath for uint;
using SafeERC20 for IERC20;
/* storage */
address[] public assets;
mapping(address => SwapAgent.AssetData) public assetData;
// contracts
Quotation public quotation;
NXMToken public nxmToken;
TokenController public tokenController;
MCR public mcr;
// parameters
address public twapOracle;
address public swapController;
uint public minPoolEth;
PriceFeedOracle public priceFeedOracle;
/* constants */
address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint public constant MCR_RATIO_DECIMALS = 4;
uint public constant MAX_MCR_RATIO = 40000; // 400%
uint public constant MAX_BUY_SELL_MCR_ETH_FRACTION = 500; // 5%. 4 decimal points
uint internal constant CONSTANT_C = 5800000;
uint internal constant CONSTANT_A = 1028 * 1e13;
uint internal constant TOKEN_EXPONENT = 4;
/* events */
event Payout(address indexed to, address indexed asset, uint amount);
event NXMSold (address indexed member, uint nxmIn, uint ethOut);
event NXMBought (address indexed member, uint ethIn, uint nxmOut);
event Swapped(address indexed fromAsset, address indexed toAsset, uint amountIn, uint amountOut);
/* logic */
modifier onlySwapController {
require(msg.sender == swapController, "Pool: not swapController");
_;
}
constructor (
address[] memory _assets,
uint112[] memory _minAmounts,
uint112[] memory _maxAmounts,
uint[] memory _maxSlippageRatios,
address _master,
address _priceOracle,
address _twapOracle,
address _swapController
) public {
require(_assets.length == _minAmounts.length, "Pool: length mismatch");
require(_assets.length == _maxAmounts.length, "Pool: length mismatch");
require(_assets.length == _maxSlippageRatios.length, "Pool: length mismatch");
for (uint i = 0; i < _assets.length; i++) {
address asset = _assets[i];
require(asset != address(0), "Pool: asset is zero address");
require(_maxAmounts[i] >= _minAmounts[i], "Pool: max < min");
require(_maxSlippageRatios[i] <= 1 ether, "Pool: max < min");
assets.push(asset);
assetData[asset].minAmount = _minAmounts[i];
assetData[asset].maxAmount = _maxAmounts[i];
assetData[asset].maxSlippageRatio = _maxSlippageRatios[i];
}
master = INXMMaster(_master);
priceFeedOracle = PriceFeedOracle(_priceOracle);
twapOracle = _twapOracle;
swapController = _swapController;
}
// fallback function
function() external payable {}
// for legacy Pool1 upgrade compatibility
function sendEther() external payable {}
/**
* @dev Calculates total value of all pool assets in ether
*/
function getPoolValueInEth() public view returns (uint) {
uint total = address(this).balance;
for (uint i = 0; i < assets.length; i++) {
address assetAddress = assets[i];
IERC20 token = IERC20(assetAddress);
uint rate = priceFeedOracle.getAssetToEthRate(assetAddress);
require(rate > 0, "Pool: zero rate");
uint assetBalance = token.balanceOf(address(this));
uint assetValue = assetBalance.mul(rate).div(1e18);
total = total.add(assetValue);
}
return total;
}
/* asset related functions */
function getAssets() external view returns (address[] memory) {
return assets;
}
function getAssetDetails(address _asset) external view returns (
uint balance,
uint112 min,
uint112 max,
uint32 lastAssetSwapTime,
uint maxSlippageRatio
) {
IERC20 token = IERC20(_asset);
balance = token.balanceOf(address(this));
SwapAgent.AssetData memory data = assetData[_asset];
return (balance, data.minAmount, data.maxAmount, data.lastSwapTime, data.maxSlippageRatio);
}
function addAsset(
address _asset,
uint112 _min,
uint112 _max,
uint _maxSlippageRatio
) external onlyGovernance {
require(_asset != address(0), "Pool: asset is zero address");
require(_max >= _min, "Pool: max < min");
require(_maxSlippageRatio <= 1 ether, "Pool: max slippage ratio > 1");
for (uint i = 0; i < assets.length; i++) {
require(_asset != assets[i], "Pool: asset exists");
}
assets.push(_asset);
assetData[_asset] = SwapAgent.AssetData(_min, _max, 0, _maxSlippageRatio);
}
function removeAsset(address _asset) external onlyGovernance {
for (uint i = 0; i < assets.length; i++) {
if (_asset != assets[i]) {
continue;
}
delete assetData[_asset];
assets[i] = assets[assets.length - 1];
assets.pop();
return;
}
revert("Pool: asset not found");
}
function setAssetDetails(
address _asset,
uint112 _min,
uint112 _max,
uint _maxSlippageRatio
) external onlyGovernance {
require(_min <= _max, "Pool: min > max");
require(_maxSlippageRatio <= 1 ether, "Pool: max slippage ratio > 1");
for (uint i = 0; i < assets.length; i++) {
if (_asset != assets[i]) {
continue;
}
assetData[_asset].minAmount = _min;
assetData[_asset].maxAmount = _max;
assetData[_asset].maxSlippageRatio = _maxSlippageRatio;
return;
}
revert("Pool: asset not found");
}
/* swap functions */
function getSwapQuote(
uint tokenAmountIn,
IERC20 fromToken,
IERC20 toToken
) public view returns (uint tokenAmountOut) {
return SwapAgent.getSwapQuote(
tokenAmountIn,
fromToken,
toToken
);
}
function swapETHForAsset(
address toTokenAddress,
uint amountIn,
uint amountOutMin
) external whenNotPaused onlySwapController nonReentrant {
SwapAgent.AssetData storage assetDetails = assetData[toTokenAddress];
uint amountOut = SwapAgent.swapETHForAsset(
twapOracle,
assetDetails,
toTokenAddress,
amountIn,
amountOutMin,
minPoolEth
);
emit Swapped(ETH, toTokenAddress, amountIn, amountOut);
}
function swapAssetForETH(
address fromTokenAddress,
uint amountIn,
uint amountOutMin
) external whenNotPaused onlySwapController nonReentrant {
uint amountOut = SwapAgent.swapAssetForETH(
twapOracle,
assetData[fromTokenAddress],
fromTokenAddress,
amountIn,
amountOutMin
);
emit Swapped(fromTokenAddress, ETH, amountIn, amountOut);
}
/* claim related functions */
/**
* @dev Execute the payout in case a claim is accepted
* @param asset token address or 0xEee...EEeE for ether
* @param payoutAddress send funds to this address
* @param amount amount to send
*/
function sendClaimPayout (
address asset,
address payable payoutAddress,
uint amount
) external onlyInternal nonReentrant returns (bool success) {
bool ok;
if (asset == ETH) {
// solhint-disable-next-line avoid-low-level-calls
(ok, /* data */) = payoutAddress.call.value(amount)("");
} else {
ok = _safeTokenTransfer(asset, payoutAddress, amount);
}
if (ok) {
emit Payout(payoutAddress, asset, amount);
}
return ok;
}
/**
* @dev safeTransfer implementation that does not revert
* @param tokenAddress ERC20 address
* @param to destination
* @param value amount to send
* @return success true if the transfer was successfull
*/
function _safeTokenTransfer (
address tokenAddress,
address to,
uint256 value
) internal returns (bool) {
// token address is not a contract
if (!tokenAddress.isContract()) {
return false;
}
IERC20 token = IERC20(tokenAddress);
bytes memory data = abi.encodeWithSelector(token.transfer.selector, to, value);
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = tokenAddress.call(data);
// low-level call failed/reverted
if (!success) {
return false;
}
// tokens that don't have return data
if (returndata.length == 0) {
return true;
}
// tokens that have return data will return a bool
return abi.decode(returndata, (bool));
}
/* pool lifecycle functions */
function transferAsset(
address asset,
address payable destination,
uint amount
) external onlyGovernance nonReentrant {
require(assetData[asset].maxAmount == 0, "Pool: max not zero");
require(destination != address(0), "Pool: dest zero");
IERC20 token = IERC20(asset);
uint balance = token.balanceOf(address(this));
uint transferableAmount = amount > balance ? balance : amount;
token.safeTransfer(destination, transferableAmount);
}
function upgradeCapitalPool(address payable newPoolAddress) external onlyMaster nonReentrant {
// transfer ether
uint ethBalance = address(this).balance;
(bool ok, /* data */) = newPoolAddress.call.value(ethBalance)("");
require(ok, "Pool: transfer failed");
// transfer assets
for (uint i = 0; i < assets.length; i++) {
IERC20 token = IERC20(assets[i]);
uint tokenBalance = token.balanceOf(address(this));
token.safeTransfer(newPoolAddress, tokenBalance);
}
}
/**
* @dev Update dependent contract address
* @dev Implements MasterAware interface function
*/
function changeDependentContractAddress() public {
nxmToken = NXMToken(master.tokenAddress());
tokenController = TokenController(master.getLatestAddress("TC"));
quotation = Quotation(master.getLatestAddress("QT"));
mcr = MCR(master.getLatestAddress("MC"));
}
/* cover purchase functions */
/// @dev Enables user to purchase cover with funding in ETH.
/// @param smartCAdd Smart Contract Address
function makeCoverBegin(
address smartCAdd,
bytes4 coverCurr,
uint[] memory coverDetails,
uint16 coverPeriod,
uint8 _v,
bytes32 _r,
bytes32 _s
) public payable onlyMember whenNotPaused {
require(coverCurr == "ETH", "Pool: Unexpected asset type");
require(msg.value == coverDetails[1], "Pool: ETH amount does not match premium");
quotation.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s);
}
/**
* @dev Enables user to purchase cover via currency asset eg DAI
*/
function makeCoverUsingCA(
address smartCAdd,
bytes4 coverCurr,
uint[] memory coverDetails,
uint16 coverPeriod,
uint8 _v,
bytes32 _r,
bytes32 _s
) public onlyMember whenNotPaused {
require(coverCurr != "ETH", "Pool: Unexpected asset type");
quotation.verifyCoverDetails(msg.sender, smartCAdd, coverCurr, coverDetails, coverPeriod, _v, _r, _s);
}
function transferAssetFrom (address asset, address from, uint amount) public onlyInternal whenNotPaused {
IERC20 token = IERC20(asset);
token.safeTransferFrom(from, address(this), amount);
}
/* token sale functions */
/**
* @dev (DEPRECATED, use sellTokens function instead) Allows selling of NXM for ether.
* Seller first needs to give this contract allowance to
* transfer/burn tokens in the NXMToken contract
* @param _amount Amount of NXM to sell
* @return success returns true on successfull sale
*/
function sellNXMTokens(uint _amount) public onlyMember whenNotPaused returns (bool success) {
sellNXM(_amount, 0);
return true;
}
/**
* @dev (DEPRECATED, use calculateNXMForEth function instead) Returns the amount of wei a seller will get for selling NXM
* @param amount Amount of NXM to sell
* @return weiToPay Amount of wei the seller will get
*/
function getWei(uint amount) external view returns (uint weiToPay) {
return getEthForNXM(amount);
}
/**
* @dev Buys NXM tokens with ETH.
* @param minTokensOut Minimum amount of tokens to be bought. Revert if boughtTokens falls below this number.
* @return boughtTokens number of bought tokens.
*/
function buyNXM(uint minTokensOut) public payable onlyMember whenNotPaused {
uint ethIn = msg.value;
require(ethIn > 0, "Pool: ethIn > 0");
uint totalAssetValue = getPoolValueInEth().sub(ethIn);
uint mcrEth = mcr.getLastMCREther();
uint mcrRatio = calculateMCRRatio(totalAssetValue, mcrEth);
require(mcrRatio <= MAX_MCR_RATIO, "Pool: Cannot purchase if MCR% > 400%");
uint tokensOut = calculateNXMForEth(ethIn, totalAssetValue, mcrEth);
require(tokensOut >= minTokensOut, "Pool: tokensOut is less than minTokensOut");
tokenController.mint(msg.sender, tokensOut);
emit NXMBought(msg.sender, ethIn, tokensOut);
}
/**
* @dev Sell NXM tokens and receive ETH.
* @param tokenAmount Amount of tokens to sell.
* @param minEthOut Minimum amount of ETH to be received. Revert if ethOut falls below this number.
* @return ethOut amount of ETH received in exchange for the tokens.
*/
function sellNXM(uint tokenAmount, uint minEthOut) public onlyMember nonReentrant whenNotPaused {
require(nxmToken.balanceOf(msg.sender) >= tokenAmount, "Pool: Not enough balance");
require(nxmToken.isLockedForMV(msg.sender) <= now, "Pool: NXM tokens are locked for voting");
uint currentTotalAssetValue = getPoolValueInEth();
uint mcrEth = mcr.getLastMCREther();
uint ethOut = calculateEthForNXM(tokenAmount, currentTotalAssetValue, mcrEth);
require(currentTotalAssetValue.sub(ethOut) >= mcrEth, "Pool: MCR% cannot fall below 100%");
require(ethOut >= minEthOut, "Pool: ethOut < minEthOut");
tokenController.burnFrom(msg.sender, tokenAmount);
(bool ok, /* data */) = msg.sender.call.value(ethOut)("");
require(ok, "Pool: Sell transfer failed");
emit NXMSold(msg.sender, tokenAmount, ethOut);
}
/**
* @dev Get value in tokens for an ethAmount purchase.
* @param ethAmount amount of ETH used for buying.
* @return tokenValue tokens obtained by buying worth of ethAmount
*/
function getNXMForEth(
uint ethAmount
) public view returns (uint) {
uint totalAssetValue = getPoolValueInEth();
uint mcrEth = mcr.getLastMCREther();
return calculateNXMForEth(ethAmount, totalAssetValue, mcrEth);
}
function calculateNXMForEth(
uint ethAmount,
uint currentTotalAssetValue,
uint mcrEth
) public pure returns (uint) {
require(
ethAmount <= mcrEth.mul(MAX_BUY_SELL_MCR_ETH_FRACTION).div(10 ** MCR_RATIO_DECIMALS),
"Pool: Purchases worth higher than 5% of MCReth are not allowed"
);
/*
The price formula is:
P(V) = A + MCReth / C * MCR% ^ 4
where MCR% = V / MCReth
P(V) = A + 1 / (C * MCReth ^ 3) * V ^ 4
To compute the number of tokens issued we can integrate with respect to V the following:
ΔT = ΔV / P(V)
which assumes that for an infinitesimally small change in locked value V price is constant and we
get an infinitesimally change in token supply ΔT.
This is not computable on-chain, below we use an approximation that works well assuming
* MCR% stays within [100%, 400%]
* ethAmount <= 5% * MCReth
Use a simplified formula excluding the constant A price offset to compute the amount of tokens to be minted.
AdjustedP(V) = 1 / (C * MCReth ^ 3) * V ^ 4
AdjustedP(V) = 1 / (C * MCReth ^ 3) * V ^ 4
For a very small variation in tokens ΔT, we have, ΔT = ΔV / P(V), to get total T we integrate with respect to V.
adjustedTokenAmount = ∫ (dV / AdjustedP(V)) from V0 (currentTotalAssetValue) to V1 (nextTotalAssetValue)
adjustedTokenAmount = ∫ ((C * MCReth ^ 3) / V ^ 4 * dV) from V0 to V1
Evaluating the above using the antiderivative of the function we get:
adjustedTokenAmount = - MCReth ^ 3 * C / (3 * V1 ^3) + MCReth * C /(3 * V0 ^ 3)
*/
if (currentTotalAssetValue == 0 || mcrEth.div(currentTotalAssetValue) > 1e12) {
/*
If the currentTotalAssetValue = 0, adjustedTokenPrice approaches 0. Therefore we can assume the price is A.
If currentTotalAssetValue is far smaller than mcrEth, MCR% approaches 0, let the price be A (baseline price).
This avoids overflow in the calculateIntegralAtPoint computation.
This approximation is safe from arbitrage since at MCR% < 100% no sells are possible.
*/
uint tokenPrice = CONSTANT_A;
return ethAmount.mul(1e18).div(tokenPrice);
}
// MCReth * C /(3 * V0 ^ 3)
uint point0 = calculateIntegralAtPoint(currentTotalAssetValue, mcrEth);
// MCReth * C / (3 * V1 ^3)
uint nextTotalAssetValue = currentTotalAssetValue.add(ethAmount);
uint point1 = calculateIntegralAtPoint(nextTotalAssetValue, mcrEth);
uint adjustedTokenAmount = point0.sub(point1);
/*
Compute a preliminary adjustedTokenPrice for the minted tokens based on the adjustedTokenAmount above,
and to that add the A constant (the price offset previously removed in the adjusted Price formula)
to obtain the finalPrice and ultimately the tokenValue based on the finalPrice.
adjustedPrice = ethAmount / adjustedTokenAmount
finalPrice = adjustedPrice + A
tokenValue = ethAmount / finalPrice
*/
// ethAmount is multiplied by 1e18 to cancel out the multiplication factor of 1e18 of the adjustedTokenAmount
uint adjustedTokenPrice = ethAmount.mul(1e18).div(adjustedTokenAmount);
uint tokenPrice = adjustedTokenPrice.add(CONSTANT_A);
return ethAmount.mul(1e18).div(tokenPrice);
}
/**
* @dev integral(V) = MCReth ^ 3 * C / (3 * V ^ 3) * 1e18
* computation result is multiplied by 1e18 to allow for a precision of 18 decimals.
* NOTE: omits the minus sign of the correct integral to use a uint result type for simplicity
* WARNING: this low-level function should be called from a contract which checks that
* mcrEth / assetValue < 1e17 (no overflow) and assetValue != 0
*/
function calculateIntegralAtPoint(
uint assetValue,
uint mcrEth
) internal pure returns (uint) {
return CONSTANT_C
.mul(1e18)
.div(3)
.mul(mcrEth).div(assetValue)
.mul(mcrEth).div(assetValue)
.mul(mcrEth).div(assetValue);
}
function getEthForNXM(uint nxmAmount) public view returns (uint ethAmount) {
uint currentTotalAssetValue = getPoolValueInEth();
uint mcrEth = mcr.getLastMCREther();
return calculateEthForNXM(nxmAmount, currentTotalAssetValue, mcrEth);
}
/**
* @dev Computes token sell value for a tokenAmount in ETH with a sell spread of 2.5%.
* for values in ETH of the sale <= 1% * MCReth the sell spread is very close to the exact value of 2.5%.
* for values higher than that sell spread may exceed 2.5%
* (The higher amount being sold at any given time the higher the spread)
*/
function calculateEthForNXM(
uint nxmAmount,
uint currentTotalAssetValue,
uint mcrEth
) public pure returns (uint) {
// Step 1. Calculate spot price at current values and amount of ETH if tokens are sold at that price
uint spotPrice0 = calculateTokenSpotPrice(currentTotalAssetValue, mcrEth);
uint spotEthAmount = nxmAmount.mul(spotPrice0).div(1e18);
// Step 2. Calculate spot price using V = currentTotalAssetValue - spotEthAmount from step 1
uint totalValuePostSpotPriceSell = currentTotalAssetValue.sub(spotEthAmount);
uint spotPrice1 = calculateTokenSpotPrice(totalValuePostSpotPriceSell, mcrEth);
// Step 3. Min [average[Price(0), Price(1)] x ( 1 - Sell Spread), Price(1) ]
// Sell Spread = 2.5%
uint averagePriceWithSpread = spotPrice0.add(spotPrice1).div(2).mul(975).div(1000);
uint finalPrice = averagePriceWithSpread < spotPrice1 ? averagePriceWithSpread : spotPrice1;
uint ethAmount = finalPrice.mul(nxmAmount).div(1e18);
require(
ethAmount <= mcrEth.mul(MAX_BUY_SELL_MCR_ETH_FRACTION).div(10 ** MCR_RATIO_DECIMALS),
"Pool: Sales worth more than 5% of MCReth are not allowed"
);
return ethAmount;
}
function calculateMCRRatio(uint totalAssetValue, uint mcrEth) public pure returns (uint) {
return totalAssetValue.mul(10 ** MCR_RATIO_DECIMALS).div(mcrEth);
}
/**
* @dev Calculates token price in ETH 1 NXM token. TokenPrice = A + (MCReth / C) * MCR%^4
*/
function calculateTokenSpotPrice(uint totalAssetValue, uint mcrEth) public pure returns (uint tokenPrice) {
uint mcrRatio = calculateMCRRatio(totalAssetValue, mcrEth);
uint precisionDecimals = 10 ** TOKEN_EXPONENT.mul(MCR_RATIO_DECIMALS);
return mcrEth
.mul(mcrRatio ** TOKEN_EXPONENT)
.div(CONSTANT_C)
.div(precisionDecimals)
.add(CONSTANT_A);
}
/**
* @dev Returns the NXM price in a given asset
* @param asset Asset name.
*/
function getTokenPrice(address asset) public view returns (uint tokenPrice) {
uint totalAssetValue = getPoolValueInEth();
uint mcrEth = mcr.getLastMCREther();
uint tokenSpotPriceEth = calculateTokenSpotPrice(totalAssetValue, mcrEth);
return priceFeedOracle.getAssetForEth(asset, tokenSpotPriceEth);
}
function getMCRRatio() public view returns (uint) {
uint totalAssetValue = getPoolValueInEth();
uint mcrEth = mcr.getLastMCREther();
return calculateMCRRatio(totalAssetValue, mcrEth);
}
function updateUintParameters(bytes8 code, uint value) external onlyGovernance {
if (code == "MIN_ETH") {
minPoolEth = value;
return;
}
revert("Pool: unknown parameter");
}
function updateAddressParameters(bytes8 code, address value) external onlyGovernance {
if (code == "TWAP") {
twapOracle = value;
return;
}
if (code == "SWAP") {
swapController = value;
return;
}
if (code == "PRC_FEED") {
priceFeedOracle = PriceFeedOracle(value);
return;
}
revert("Pool: unknown parameter");
}
}/* Copyright (C) 2017 GovBlocks.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "../../abstract/Iupgradable.sol";
import "./MemberRoles.sol";
import "./external/Governed.sol";
import "./external/IProposalCategory.sol";
contract ProposalCategory is Governed, IProposalCategory, Iupgradable {
bool public constructorCheck;
MemberRoles internal mr;
struct CategoryStruct {
uint memberRoleToVote;
uint majorityVotePerc;
uint quorumPerc;
uint[] allowedToCreateProposal;
uint closingTime;
uint minStake;
}
struct CategoryAction {
uint defaultIncentive;
address contractAddress;
bytes2 contractName;
}
CategoryStruct[] internal allCategory;
mapping(uint => CategoryAction) internal categoryActionData;
mapping(uint => uint) public categoryABReq;
mapping(uint => uint) public isSpecialResolution;
mapping(uint => bytes) public categoryActionHashes;
bool public categoryActionHashUpdated;
/**
* @dev Adds new category (Discontinued, moved functionality to newCategory)
* @param _name Category name
* @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
* @param _majorityVotePerc Majority Vote threshold for Each voting layer
* @param _quorumPerc minimum threshold percentage required in voting to calculate result
* @param _allowedToCreateProposal Member roles allowed to create the proposal
* @param _closingTime Vote closing time for Each voting layer
* @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
* @param _contractAddress address of contract to call after proposal is accepted
* @param _contractName name of contract to be called after proposal is accepted
* @param _incentives rewards to distributed after proposal is accepted
*/
function addCategory(
string calldata _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] calldata _allowedToCreateProposal,
uint _closingTime,
string calldata _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] calldata _incentives
) external {}
/**
* @dev Initiates Default settings for Proposal Category contract (Adding default categories)
*/
function proposalCategoryInitiate() external {}
/**
* @dev Initiates Default action function hashes for existing categories
* To be called after the contract has been upgraded by governance
*/
function updateCategoryActionHashes() external onlyOwner {
require(!categoryActionHashUpdated, "Category action hashes already updated");
categoryActionHashUpdated = true;
categoryActionHashes[1] = abi.encodeWithSignature("addRole(bytes32,string,address)");
categoryActionHashes[2] = abi.encodeWithSignature("updateRole(address,uint256,bool)");
categoryActionHashes[3] = abi.encodeWithSignature("newCategory(string,uint256,uint256,uint256,uint256[],uint256,string,address,bytes2,uint256[],string)"); // solhint-disable-line
categoryActionHashes[4] = abi.encodeWithSignature("editCategory(uint256,string,uint256,uint256,uint256,uint256[],uint256,string,address,bytes2,uint256[],string)"); // solhint-disable-line
categoryActionHashes[5] = abi.encodeWithSignature("upgradeContractImplementation(bytes2,address)");
categoryActionHashes[6] = abi.encodeWithSignature("startEmergencyPause()");
categoryActionHashes[7] = abi.encodeWithSignature("addEmergencyPause(bool,bytes4)");
categoryActionHashes[8] = abi.encodeWithSignature("burnCAToken(uint256,uint256,address)");
categoryActionHashes[9] = abi.encodeWithSignature("setUserClaimVotePausedOn(address)");
categoryActionHashes[12] = abi.encodeWithSignature("transferEther(uint256,address)");
categoryActionHashes[13] = abi.encodeWithSignature("addInvestmentAssetCurrency(bytes4,address,bool,uint64,uint64,uint8)"); // solhint-disable-line
categoryActionHashes[14] = abi.encodeWithSignature("changeInvestmentAssetHoldingPerc(bytes4,uint64,uint64)");
categoryActionHashes[15] = abi.encodeWithSignature("changeInvestmentAssetStatus(bytes4,bool)");
categoryActionHashes[16] = abi.encodeWithSignature("swapABMember(address,address)");
categoryActionHashes[17] = abi.encodeWithSignature("addCurrencyAssetCurrency(bytes4,address,uint256)");
categoryActionHashes[20] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[21] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[22] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[23] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[24] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[25] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[26] = abi.encodeWithSignature("updateUintParameters(bytes8,uint256)");
categoryActionHashes[27] = abi.encodeWithSignature("updateAddressParameters(bytes8,address)");
categoryActionHashes[28] = abi.encodeWithSignature("updateOwnerParameters(bytes8,address)");
categoryActionHashes[29] = abi.encodeWithSignature("upgradeMultipleContracts(bytes2[],address[])");
categoryActionHashes[30] = abi.encodeWithSignature("changeCurrencyAssetAddress(bytes4,address)");
categoryActionHashes[31] = abi.encodeWithSignature("changeCurrencyAssetBaseMin(bytes4,uint256)");
categoryActionHashes[32] = abi.encodeWithSignature("changeInvestmentAssetAddressAndDecimal(bytes4,address,uint8)"); // solhint-disable-line
categoryActionHashes[33] = abi.encodeWithSignature("externalLiquidityTrade()");
}
/**
* @dev Gets Total number of categories added till now
*/
function totalCategories() external view returns (uint) {
return allCategory.length;
}
/**
* @dev Gets category details
*/
function category(uint _categoryId) external view returns (uint, uint, uint, uint, uint[] memory, uint, uint) {
return (
_categoryId,
allCategory[_categoryId].memberRoleToVote,
allCategory[_categoryId].majorityVotePerc,
allCategory[_categoryId].quorumPerc,
allCategory[_categoryId].allowedToCreateProposal,
allCategory[_categoryId].closingTime,
allCategory[_categoryId].minStake
);
}
/**
* @dev Gets category ab required and isSpecialResolution
* @return the category id
* @return if AB voting is required
* @return is category a special resolution
*/
function categoryExtendedData(uint _categoryId) external view returns (uint, uint, uint) {
return (
_categoryId,
categoryABReq[_categoryId],
isSpecialResolution[_categoryId]
);
}
/**
* @dev Gets the category acion details
* @param _categoryId is the category id in concern
* @return the category id
* @return the contract address
* @return the contract name
* @return the default incentive
*/
function categoryAction(uint _categoryId) external view returns (uint, address, bytes2, uint) {
return (
_categoryId,
categoryActionData[_categoryId].contractAddress,
categoryActionData[_categoryId].contractName,
categoryActionData[_categoryId].defaultIncentive
);
}
/**
* @dev Gets the category acion details of a category id
* @param _categoryId is the category id in concern
* @return the category id
* @return the contract address
* @return the contract name
* @return the default incentive
* @return action function hash
*/
function categoryActionDetails(uint _categoryId) external view returns (uint, address, bytes2, uint, bytes memory) {
return (
_categoryId,
categoryActionData[_categoryId].contractAddress,
categoryActionData[_categoryId].contractName,
categoryActionData[_categoryId].defaultIncentive,
categoryActionHashes[_categoryId]
);
}
/**
* @dev Updates dependant contract addresses
*/
function changeDependentContractAddress() public {
mr = MemberRoles(ms.getLatestAddress("MR"));
}
/**
* @dev Adds new category
* @param _name Category name
* @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
* @param _majorityVotePerc Majority Vote threshold for Each voting layer
* @param _quorumPerc minimum threshold percentage required in voting to calculate result
* @param _allowedToCreateProposal Member roles allowed to create the proposal
* @param _closingTime Vote closing time for Each voting layer
* @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
* @param _contractAddress address of contract to call after proposal is accepted
* @param _contractName name of contract to be called after proposal is accepted
* @param _incentives rewards to distributed after proposal is accepted
* @param _functionHash function signature to be executed
*/
function newCategory(
string memory _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] memory _allowedToCreateProposal,
uint _closingTime,
string memory _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] memory _incentives,
string memory _functionHash
)
public
onlyAuthorizedToGovern
{
require(_quorumPerc <= 100 && _majorityVotePerc <= 100, "Invalid percentage");
require((_contractName == "EX" && _contractAddress == address(0)) || bytes(_functionHash).length > 0);
require(_incentives[3] <= 1, "Invalid special resolution flag");
//If category is special resolution role authorized should be member
if (_incentives[3] == 1) {
require(_memberRoleToVote == uint(MemberRoles.Role.Member));
_majorityVotePerc = 0;
_quorumPerc = 0;
}
_addCategory(
_name,
_memberRoleToVote,
_majorityVotePerc,
_quorumPerc,
_allowedToCreateProposal,
_closingTime,
_actionHash,
_contractAddress,
_contractName,
_incentives
);
if (bytes(_functionHash).length > 0 && abi.encodeWithSignature(_functionHash).length == 4) {
categoryActionHashes[allCategory.length - 1] = abi.encodeWithSignature(_functionHash);
}
}
/**
* @dev Changes the master address and update it's instance
* @param _masterAddress is the new master address
*/
function changeMasterAddress(address _masterAddress) public {
if (masterAddress != address(0))
require(masterAddress == msg.sender);
masterAddress = _masterAddress;
ms = INXMMaster(_masterAddress);
nxMasterAddress = _masterAddress;
}
/**
* @dev Updates category details (Discontinued, moved functionality to editCategory)
* @param _categoryId Category id that needs to be updated
* @param _name Category name
* @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
* @param _allowedToCreateProposal Member roles allowed to create the proposal
* @param _majorityVotePerc Majority Vote threshold for Each voting layer
* @param _quorumPerc minimum threshold percentage required in voting to calculate result
* @param _closingTime Vote closing time for Each voting layer
* @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
* @param _contractAddress address of contract to call after proposal is accepted
* @param _contractName name of contract to be called after proposal is accepted
* @param _incentives rewards to distributed after proposal is accepted
*/
function updateCategory(
uint _categoryId,
string memory _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] memory _allowedToCreateProposal,
uint _closingTime,
string memory _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] memory _incentives
) public {}
/**
* @dev Updates category details
* @param _categoryId Category id that needs to be updated
* @param _name Category name
* @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
* @param _allowedToCreateProposal Member roles allowed to create the proposal
* @param _majorityVotePerc Majority Vote threshold for Each voting layer
* @param _quorumPerc minimum threshold percentage required in voting to calculate result
* @param _closingTime Vote closing time for Each voting layer
* @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
* @param _contractAddress address of contract to call after proposal is accepted
* @param _contractName name of contract to be called after proposal is accepted
* @param _incentives rewards to distributed after proposal is accepted
* @param _functionHash function signature to be executed
*/
function editCategory(
uint _categoryId,
string memory _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] memory _allowedToCreateProposal,
uint _closingTime,
string memory _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] memory _incentives,
string memory _functionHash
)
public
onlyAuthorizedToGovern
{
require(_verifyMemberRoles(_memberRoleToVote, _allowedToCreateProposal) == 1, "Invalid Role");
require(_quorumPerc <= 100 && _majorityVotePerc <= 100, "Invalid percentage");
require((_contractName == "EX" && _contractAddress == address(0)) || bytes(_functionHash).length > 0);
require(_incentives[3] <= 1, "Invalid special resolution flag");
//If category is special resolution role authorized should be member
if (_incentives[3] == 1) {
require(_memberRoleToVote == uint(MemberRoles.Role.Member));
_majorityVotePerc = 0;
_quorumPerc = 0;
}
delete categoryActionHashes[_categoryId];
if (bytes(_functionHash).length > 0 && abi.encodeWithSignature(_functionHash).length == 4) {
categoryActionHashes[_categoryId] = abi.encodeWithSignature(_functionHash);
}
allCategory[_categoryId].memberRoleToVote = _memberRoleToVote;
allCategory[_categoryId].majorityVotePerc = _majorityVotePerc;
allCategory[_categoryId].closingTime = _closingTime;
allCategory[_categoryId].allowedToCreateProposal = _allowedToCreateProposal;
allCategory[_categoryId].minStake = _incentives[0];
allCategory[_categoryId].quorumPerc = _quorumPerc;
categoryActionData[_categoryId].defaultIncentive = _incentives[1];
categoryActionData[_categoryId].contractName = _contractName;
categoryActionData[_categoryId].contractAddress = _contractAddress;
categoryABReq[_categoryId] = _incentives[2];
isSpecialResolution[_categoryId] = _incentives[3];
emit Category(_categoryId, _name, _actionHash);
}
/**
* @dev Internal call to add new category
* @param _name Category name
* @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
* @param _majorityVotePerc Majority Vote threshold for Each voting layer
* @param _quorumPerc minimum threshold percentage required in voting to calculate result
* @param _allowedToCreateProposal Member roles allowed to create the proposal
* @param _closingTime Vote closing time for Each voting layer
* @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
* @param _contractAddress address of contract to call after proposal is accepted
* @param _contractName name of contract to be called after proposal is accepted
* @param _incentives rewards to distributed after proposal is accepted
*/
function _addCategory(
string memory _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] memory _allowedToCreateProposal,
uint _closingTime,
string memory _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] memory _incentives
)
internal
{
require(_verifyMemberRoles(_memberRoleToVote, _allowedToCreateProposal) == 1, "Invalid Role");
allCategory.push(
CategoryStruct(
_memberRoleToVote,
_majorityVotePerc,
_quorumPerc,
_allowedToCreateProposal,
_closingTime,
_incentives[0]
)
);
uint categoryId = allCategory.length - 1;
categoryActionData[categoryId] = CategoryAction(_incentives[1], _contractAddress, _contractName);
categoryABReq[categoryId] = _incentives[2];
isSpecialResolution[categoryId] = _incentives[3];
emit Category(categoryId, _name, _actionHash);
}
/**
* @dev Internal call to check if given roles are valid or not
*/
function _verifyMemberRoles(uint _memberRoleToVote, uint[] memory _allowedToCreateProposal)
internal view returns (uint) {
uint totalRoles = mr.totalRoles();
if (_memberRoleToVote >= totalRoles) {
return 0;
}
for (uint i = 0; i < _allowedToCreateProposal.length; i++) {
if (_allowedToCreateProposal[i] >= totalRoles) {
return 0;
}
}
return 1;
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
contract Aggregator {
function latestAnswer() public view returns (int);
}
contract PriceFeedOracle {
using SafeMath for uint;
mapping (address => address) public aggregators;
address public daiAddress;
address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
constructor (address[] memory assets, address[] memory _aggregators, address _daiAddress) public {
require(assets.length == _aggregators.length, "PriceFeedOracle: assets and _aggregators need to have same length");
for (uint i = 0; i < assets.length; i++) {
aggregators[assets[i]] = _aggregators[i];
}
daiAddress = _daiAddress;
}
/**
* @dev Returns the amount of ether in wei that are equivalent to 1 unit (10 ** decimals) of asset
* @param asset quoted currency
* @return price in ether
*/
function getAssetToEthRate(address asset) public view returns (uint) {
if (asset == ETH) {
return 1 ether;
}
address aggregatorAddress = aggregators[asset];
if (aggregatorAddress == address(0)) {
revert("PriceFeedOracle: Oracle asset not found");
}
int rate = Aggregator(aggregatorAddress).latestAnswer();
require(rate > 0, "PriceFeedOracle: Rate must be > 0");
return uint(rate);
}
/**
* @dev Returns the amount of currency that is equivalent to ethIn amount of ether.
* @param asset quoted Supported values: ["DAI", "ETH"]
* @param ethIn amount of ether to be converted to the currency
* @return price in ether
*/
function getAssetForEth(address asset, uint ethIn) external view returns (uint) {
if (asset == daiAddress) {
return ethIn.mul(1e18).div(getAssetToEthRate(daiAddress));
}
if (asset == ETH) {
return ethIn;
}
revert("PriceFeedOracle: Unknown asset");
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "./external/OZIERC20.sol";
import "./external/OZSafeMath.sol";
contract NXMToken is OZIERC20 {
using OZSafeMath for uint256;
event WhiteListed(address indexed member);
event BlackListed(address indexed member);
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowed;
mapping(address => bool) public whiteListed;
mapping(address => uint) public isLockedForMV;
uint256 private _totalSupply;
string public name = "NXM";
string public symbol = "NXM";
uint8 public decimals = 18;
address public operator;
modifier canTransfer(address _to) {
require(whiteListed[_to]);
_;
}
modifier onlyOperator() {
if (operator != address(0))
require(msg.sender == operator);
_;
}
constructor(address _founderAddress, uint _initialSupply) public {
_mint(_founderAddress, _initialSupply);
}
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(
address owner,
address spender
)
public
view
returns (uint256)
{
return _allowed[owner][spender];
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function approve(address spender, uint256 value) public returns (bool) {
require(spender != address(0));
_allowed[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed_[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param spender The address which will spend the funds.
* @param addedValue The amount of tokens to increase the allowance by.
*/
function increaseAllowance(
address spender,
uint256 addedValue
)
public
returns (bool)
{
require(spender != address(0));
_allowed[msg.sender][spender] = (
_allowed[msg.sender][spender].add(addedValue));
emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed_[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param spender The address which will spend the funds.
* @param subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseAllowance(
address spender,
uint256 subtractedValue
)
public
returns (bool)
{
require(spender != address(0));
_allowed[msg.sender][spender] = (
_allowed[msg.sender][spender].sub(subtractedValue));
emit Approval(msg.sender, spender, _allowed[msg.sender][spender]);
return true;
}
/**
* @dev Adds a user to whitelist
* @param _member address to add to whitelist
*/
function addToWhiteList(address _member) public onlyOperator returns (bool) {
whiteListed[_member] = true;
emit WhiteListed(_member);
return true;
}
/**
* @dev removes a user from whitelist
* @param _member address to remove from whitelist
*/
function removeFromWhiteList(address _member) public onlyOperator returns (bool) {
whiteListed[_member] = false;
emit BlackListed(_member);
return true;
}
/**
* @dev change operator address
* @param _newOperator address of new operator
*/
function changeOperator(address _newOperator) public onlyOperator returns (bool) {
operator = _newOperator;
return true;
}
/**
* @dev burns an amount of the tokens of the message sender
* account.
* @param amount The amount that will be burnt.
*/
function burn(uint256 amount) public returns (bool) {
_burn(msg.sender, amount);
return true;
}
/**
* @dev Burns a specific amount of tokens from the target address and decrements allowance
* @param from address The address which you want to send tokens from
* @param value uint256 The amount of token to be burned
*/
function burnFrom(address from, uint256 value) public returns (bool) {
_burnFrom(from, value);
return true;
}
/**
* @dev function that mints an amount of the token and assigns it to
* an account.
* @param account The account that will receive the created tokens.
* @param amount The amount that will be created.
*/
function mint(address account, uint256 amount) public onlyOperator {
_mint(account, amount);
}
/**
* @dev Transfer token for a specified address
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function transfer(address to, uint256 value) public canTransfer(to) returns (bool) {
require(isLockedForMV[msg.sender] < now); // if not voted under governance
require(value <= _balances[msg.sender]);
_transfer(to, value);
return true;
}
/**
* @dev Transfer tokens to the operator from the specified address
* @param from The address to transfer from.
* @param value The amount to be transferred.
*/
function operatorTransfer(address from, uint256 value) public onlyOperator returns (bool) {
require(value <= _balances[from]);
_transferFrom(from, operator, value);
return true;
}
/**
* @dev Transfer tokens from one address to another
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 the amount of tokens to be transferred
*/
function transferFrom(
address from,
address to,
uint256 value
)
public
canTransfer(to)
returns (bool)
{
require(isLockedForMV[from] < now); // if not voted under governance
require(value <= _balances[from]);
require(value <= _allowed[from][msg.sender]);
_transferFrom(from, to, value);
return true;
}
/**
* @dev Lock the user's tokens
* @param _of user's address.
*/
function lockForMemberVote(address _of, uint _days) public onlyOperator {
if (_days.add(now) > isLockedForMV[_of])
isLockedForMV[_of] = _days.add(now);
}
/**
* @dev Transfer token for a specified address
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function _transfer(address to, uint256 value) internal {
_balances[msg.sender] = _balances[msg.sender].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(msg.sender, to, value);
}
/**
* @dev Transfer tokens from one address to another
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 the amount of tokens to be transferred
*/
function _transferFrom(
address from,
address to,
uint256 value
)
internal
{
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
_allowed[from][msg.sender] = _allowed[from][msg.sender].sub(value);
emit Transfer(from, to, value);
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param account The account that will receive the created tokens.
* @param amount The amount that will be created.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0));
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account.
* @param account The account whose tokens will be burnt.
* @param amount The amount that will be burnt.
*/
function _burn(address account, uint256 amount) internal {
require(amount <= _balances[account]);
_totalSupply = _totalSupply.sub(amount);
_balances[account] = _balances[account].sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account, deducting from the sender's allowance for said account. Uses the
* internal burn function.
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burnFrom(address account, uint256 value) internal {
require(value <= _allowed[account][msg.sender]);
// Should https://github.com/OpenZeppelin/zeppelin-solidity/issues/707 be accepted,
// this function needs to emit an event with the updated approval.
_allowed[account][msg.sender] = _allowed[account][msg.sender].sub(
value);
_burn(account, value);
}
}pragma solidity ^0.5.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}pragma solidity ^0.5.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*
* _Since v2.5.0:_ this module is now much more gas efficient, given net gas
* metering changes introduced in the Istanbul hardfork.
*/
contract ReentrancyGuard {
bool private _notEntered;
constructor () internal {
// Storing an initial non-zero value makes deployment a bit more
// expensive, but in exchange the refund on every call to nonReentrant
// will be lower in amount. Since refunds are capped to a percetange of
// the total transaction's gas, it is best to keep them low in cases
// like this one, to increase the likelihood of the full refund coming
// into effect.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_notEntered = false;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
}pragma solidity ^0.5.5;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* _Available since v2.4.0._
*/
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*
* _Available since v2.4.0._
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-call-value
(bool success, ) = recipient.call.value(amount)("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}/*
Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/
*/
pragma solidity ^0.5.0;
import "./INXMMaster.sol";
contract MasterAware {
INXMMaster public master;
modifier onlyMember {
require(master.isMember(msg.sender), "Caller is not a member");
_;
}
modifier onlyInternal {
require(master.isInternal(msg.sender), "Caller is not an internal contract");
_;
}
modifier onlyMaster {
if (address(master) != address(0)) {
require(address(master) == msg.sender, "Not master");
}
_;
}
modifier onlyGovernance {
require(
master.checkIsAuthToGoverned(msg.sender),
"Caller is not authorized to govern"
);
_;
}
modifier whenPaused {
require(master.isPause(), "System is not paused");
_;
}
modifier whenNotPaused {
require(!master.isPause(), "System is paused");
_;
}
function changeMasterAddress(address masterAddress) public onlyMaster {
master = INXMMaster(masterAddress);
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../../external/uniswap/IUniswapV2Router02.sol";
import "../oracles/TwapOracle.sol";
library SwapAgent {
using SafeMath for uint;
struct AssetData {
uint112 minAmount;
uint112 maxAmount;
uint32 lastSwapTime;
// 18 decimals of precision. 0.01% -> 0.0001 -> 1e14
uint maxSlippageRatio;
}
IUniswapV2Router02 constant public router = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
uint constant public MAX_LIQUIDITY_RATIO = 3 * 1e15;
function getSwapQuote(
uint tokenAmountIn,
IERC20 fromToken,
IERC20 toToken
) public view returns (uint tokenAmountOut) {
address[] memory path = new address[](2);
path[0] = address(fromToken);
path[1] = address(toToken);
uint[] memory amountsOut = router.getAmountsOut(tokenAmountIn, path);
return amountsOut[1];
}
function swapETHForAsset(
address _oracle,
AssetData storage assetData,
address toTokenAddress,
uint amountIn,
uint amountOutMin,
uint minLeftETH
) external returns (uint) {
uint balanceBefore = IERC20(toTokenAddress).balanceOf(address(this));
address WETH = router.WETH();
{
// scope for swap frequency check
uint timeSinceLastTrade = block.timestamp.sub(uint(assetData.lastSwapTime));
require(timeSinceLastTrade > TwapOracle(_oracle).periodSize(), "SwapAgent: too fast");
}
{
// scope for liquidity check
address pairAddress = TwapOracle(_oracle).pairFor(WETH, toTokenAddress);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
(uint112 reserve0, uint112 reserve1, /* time */) = pair.getReserves();
uint ethReserve = WETH < toTokenAddress ? reserve0 : reserve1;
uint maxTradable = ethReserve.mul(MAX_LIQUIDITY_RATIO).div(1e18);
require(amountIn <= maxTradable, "SwapAgent: exceeds max tradable amount");
}
{
// scope for ether checks
uint ethBalanceBefore = address(this).balance;
uint ethBalanceAfter = ethBalanceBefore.sub(amountIn);
require(ethBalanceAfter >= minLeftETH, "SwapAgent: insufficient ether left");
}
{
// scope for token checks
uint avgAmountOut = TwapOracle(_oracle).consult(WETH, amountIn, toTokenAddress);
uint maxSlippageAmount = avgAmountOut.mul(assetData.maxSlippageRatio).div(1e18);
uint minOutOnMaxSlippage = avgAmountOut.sub(maxSlippageAmount);
// gas optimisation: reads both values using a single SLOAD
(uint minAssetAmount, uint maxAssetAmount) = (assetData.minAmount, assetData.maxAmount);
require(amountOutMin >= minOutOnMaxSlippage, "SwapAgent: amountOutMin < minOutOnMaxSlippage");
require(balanceBefore < minAssetAmount, "SwapAgent: balanceBefore >= min");
require(balanceBefore.add(amountOutMin) <= maxAssetAmount, "SwapAgent: balanceAfter > max");
}
address[] memory path = new address[](2);
path[0] = WETH;
path[1] = toTokenAddress;
router.swapExactETHForTokens.value(amountIn)(amountOutMin, path, address(this), block.timestamp);
assetData.lastSwapTime = uint32(block.timestamp);
uint balanceAfter = IERC20(toTokenAddress).balanceOf(address(this));
uint amountOut = balanceAfter.sub(balanceBefore);
return amountOut;
}
function swapAssetForETH(
address _oracle,
AssetData storage assetData,
address fromTokenAddress,
uint amountIn,
uint amountOutMin
) external returns (uint) {
uint tokenBalanceBefore = IERC20(fromTokenAddress).balanceOf(address(this));
uint balanceBefore = address(this).balance;
address WETH = router.WETH();
{
// scope for swap frequency check
uint timeSinceLastTrade = block.timestamp.sub(uint(assetData.lastSwapTime));
require(timeSinceLastTrade > TwapOracle(_oracle).periodSize(), "SwapAgent: too fast");
}
{
// scope for liquidity check
address pairAddress = TwapOracle(_oracle).pairFor(fromTokenAddress, WETH);
IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
(uint112 reserve0, uint112 reserve1, /* time */) = pair.getReserves();
uint tokenReserve = fromTokenAddress < WETH ? reserve0 : reserve1;
uint maxTradable = tokenReserve.mul(MAX_LIQUIDITY_RATIO).div(1e18);
require(amountIn <= maxTradable, "SwapAgent: exceeds max tradable amount");
}
{
// scope for token checks
uint avgAmountOut = TwapOracle(_oracle).consult(fromTokenAddress, amountIn, WETH);
uint maxSlippageAmount = avgAmountOut.mul(assetData.maxSlippageRatio).div(1e18);
uint minOutOnMaxSlippage = avgAmountOut.sub(maxSlippageAmount);
// gas optimisation: reads both values using a single SLOAD
(uint minAssetAmount, uint maxAssetAmount) = (assetData.minAmount, assetData.maxAmount);
require(amountOutMin >= minOutOnMaxSlippage, "SwapAgent: amountOutMin < minOutOnMaxSlippage");
require(tokenBalanceBefore > maxAssetAmount, "SwapAgent: tokenBalanceBefore <= max");
require(tokenBalanceBefore.sub(amountIn) >= minAssetAmount, "SwapAgent: tokenBalanceAfter < min");
}
address[] memory path = new address[](2);
path[0] = fromTokenAddress;
path[1] = router.WETH();
IERC20(fromTokenAddress).approve(address(router), amountIn);
router.swapExactTokensForETH(amountIn, amountOutMin, path, address(this), block.timestamp);
assetData.lastSwapTime = uint32(block.timestamp);
uint balanceAfter = address(this).balance;
uint amountOut = balanceAfter.sub(balanceBefore);
return amountOut;
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
contract INXMMaster {
address public tokenAddress;
address public owner;
uint public pauseTime;
function delegateCallBack(bytes32 myid) external;
function masterInitialized() public view returns (bool);
function isInternal(address _add) public view returns (bool);
function isPause() public view returns (bool check);
function isOwner(address _add) public view returns (bool);
function isMember(address _add) public view returns (bool);
function checkIsAuthToGoverned(address _add) public view returns (bool);
function updatePauseTime(uint _time) public;
function dAppLocker() public view returns (address _add);
function dAppToken() public view returns (address _add);
function getLatestAddress(bytes2 _contractName) public view returns (address payable contractAddress);
}pragma solidity ^0.5.0;
import "./INXMMaster.sol";
contract Iupgradable {
INXMMaster public ms;
address public nxMasterAddress;
modifier onlyInternal {
require(ms.isInternal(msg.sender));
_;
}
modifier isMemberAndcheckPause {
require(ms.isPause() == false && ms.isMember(msg.sender) == true);
_;
}
modifier onlyOwner {
require(ms.isOwner(msg.sender));
_;
}
modifier checkPause {
require(ms.isPause() == false);
_;
}
modifier isMember {
require(ms.isMember(msg.sender), "Not member");
_;
}
/**
* @dev Iupgradable Interface to update dependent contract address
*/
function changeDependentContractAddress() public;
/**
* @dev change master address
* @param _masterAddress is the new address
*/
function changeMasterAddress(address _masterAddress) public {
if (address(ms) != address(0)) {
require(address(ms) == msg.sender, "Not master");
}
ms = INXMMaster(_masterAddress);
nxMasterAddress = _masterAddress;
}
}pragma solidity ^0.5.0;
interface IPooledStaking {
function accumulateReward(address contractAddress, uint amount) external;
function pushBurn(address contractAddress, uint amount) external;
function hasPendingActions() external view returns (bool);
function contractStake(address contractAddress) external view returns (uint);
function stakerReward(address staker) external view returns (uint);
function stakerDeposit(address staker) external view returns (uint);
function stakerContractStake(address staker, address contractAddress) external view returns (uint);
function withdraw(uint amount) external;
function stakerMaxWithdrawable(address stakerAddress) external view returns (uint);
function withdrawReward(address stakerAddress) external;
}// /* Copyright (C) 2017 GovBlocks.io
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../token/TokenController.sol";
import "./MemberRoles.sol";
import "./ProposalCategory.sol";
import "./external/IGovernance.sol";
contract Governance is IGovernance, Iupgradable {
using SafeMath for uint;
enum ProposalStatus {
Draft,
AwaitingSolution,
VotingStarted,
Accepted,
Rejected,
Majority_Not_Reached_But_Accepted,
Denied
}
struct ProposalData {
uint propStatus;
uint finalVerdict;
uint category;
uint commonIncentive;
uint dateUpd;
address owner;
}
struct ProposalVote {
address voter;
uint proposalId;
uint dateAdd;
}
struct VoteTally {
mapping(uint => uint) memberVoteValue;
mapping(uint => uint) abVoteValue;
uint voters;
}
struct DelegateVote {
address follower;
address leader;
uint lastUpd;
}
ProposalVote[] internal allVotes;
DelegateVote[] public allDelegation;
mapping(uint => ProposalData) internal allProposalData;
mapping(uint => bytes[]) internal allProposalSolutions;
mapping(address => uint[]) internal allVotesByMember;
mapping(uint => mapping(address => bool)) public rewardClaimed;
mapping(address => mapping(uint => uint)) public memberProposalVote;
mapping(address => uint) public followerDelegation;
mapping(address => uint) internal followerCount;
mapping(address => uint[]) internal leaderDelegation;
mapping(uint => VoteTally) public proposalVoteTally;
mapping(address => bool) public isOpenForDelegation;
mapping(address => uint) public lastRewardClaimed;
bool internal constructorCheck;
uint public tokenHoldingTime;
uint internal roleIdAllowedToCatgorize;
uint internal maxVoteWeigthPer;
uint internal specialResolutionMajPerc;
uint internal maxFollowers;
uint internal totalProposals;
uint internal maxDraftTime;
MemberRoles internal memberRole;
ProposalCategory internal proposalCategory;
TokenController internal tokenInstance;
mapping(uint => uint) public proposalActionStatus;
mapping(uint => uint) internal proposalExecutionTime;
mapping(uint => mapping(address => bool)) public proposalRejectedByAB;
mapping(uint => uint) internal actionRejectedCount;
bool internal actionParamsInitialised;
uint internal actionWaitingTime;
uint constant internal AB_MAJ_TO_REJECT_ACTION = 3;
enum ActionStatus {
Pending,
Accepted,
Rejected,
Executed,
NoAction
}
/**
* @dev Called whenever an action execution is failed.
*/
event ActionFailed (
uint256 proposalId
);
/**
* @dev Called whenever an AB member rejects the action execution.
*/
event ActionRejected (
uint256 indexed proposalId,
address rejectedBy
);
/**
* @dev Checks if msg.sender is proposal owner
*/
modifier onlyProposalOwner(uint _proposalId) {
require(msg.sender == allProposalData[_proposalId].owner, "Not allowed");
_;
}
/**
* @dev Checks if proposal is opened for voting
*/
modifier voteNotStarted(uint _proposalId) {
require(allProposalData[_proposalId].propStatus < uint(ProposalStatus.VotingStarted));
_;
}
/**
* @dev Checks if msg.sender is allowed to create proposal under given category
*/
modifier isAllowed(uint _categoryId) {
require(allowedToCreateProposal(_categoryId), "Not allowed");
_;
}
/**
* @dev Checks if msg.sender is allowed categorize proposal under given category
*/
modifier isAllowedToCategorize() {
require(memberRole.checkRole(msg.sender, roleIdAllowedToCatgorize), "Not allowed");
_;
}
/**
* @dev Checks if msg.sender had any pending rewards to be claimed
*/
modifier checkPendingRewards {
require(getPendingReward(msg.sender) == 0, "Claim reward");
_;
}
/**
* @dev Event emitted whenever a proposal is categorized
*/
event ProposalCategorized(
uint indexed proposalId,
address indexed categorizedBy,
uint categoryId
);
/**
* @dev Removes delegation of an address.
* @param _add address to undelegate.
*/
function removeDelegation(address _add) external onlyInternal {
_unDelegate(_add);
}
/**
* @dev Creates a new proposal
* @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
* @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
*/
function createProposal(
string calldata _proposalTitle,
string calldata _proposalSD,
string calldata _proposalDescHash,
uint _categoryId
)
external isAllowed(_categoryId)
{
require(ms.isMember(msg.sender), "Not Member");
_createProposal(_proposalTitle, _proposalSD, _proposalDescHash, _categoryId);
}
/**
* @dev Edits the details of an existing proposal
* @param _proposalId Proposal id that details needs to be updated
* @param _proposalDescHash Proposal description hash having long and short description of proposal.
*/
function updateProposal(
uint _proposalId,
string calldata _proposalTitle,
string calldata _proposalSD,
string calldata _proposalDescHash
)
external onlyProposalOwner(_proposalId)
{
require(
allProposalSolutions[_proposalId].length < 2,
"Not allowed"
);
allProposalData[_proposalId].propStatus = uint(ProposalStatus.Draft);
allProposalData[_proposalId].category = 0;
allProposalData[_proposalId].commonIncentive = 0;
emit Proposal(
allProposalData[_proposalId].owner,
_proposalId,
now,
_proposalTitle,
_proposalSD,
_proposalDescHash
);
}
/**
* @dev Categorizes proposal to proceed further. Categories shows the proposal objective.
*/
function categorizeProposal(
uint _proposalId,
uint _categoryId,
uint _incentive
)
external
voteNotStarted(_proposalId) isAllowedToCategorize
{
_categorizeProposal(_proposalId, _categoryId, _incentive);
}
/**
* @dev Submit proposal with solution
* @param _proposalId Proposal id
* @param _solutionHash Solution hash contains parameters, values and description needed according to proposal
*/
function submitProposalWithSolution(
uint _proposalId,
string calldata _solutionHash,
bytes calldata _action
)
external
onlyProposalOwner(_proposalId)
{
require(allProposalData[_proposalId].propStatus == uint(ProposalStatus.AwaitingSolution));
_proposalSubmission(_proposalId, _solutionHash, _action);
}
/**
* @dev Creates a new proposal with solution
* @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
* @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
* @param _solutionHash Solution hash contains parameters, values and description needed according to proposal
*/
function createProposalwithSolution(
string calldata _proposalTitle,
string calldata _proposalSD,
string calldata _proposalDescHash,
uint _categoryId,
string calldata _solutionHash,
bytes calldata _action
)
external isAllowed(_categoryId)
{
uint proposalId = totalProposals;
_createProposal(_proposalTitle, _proposalSD, _proposalDescHash, _categoryId);
require(_categoryId > 0);
_proposalSubmission(
proposalId,
_solutionHash,
_action
);
}
/**
* @dev Submit a vote on the proposal.
* @param _proposalId to vote upon.
* @param _solutionChosen is the chosen vote.
*/
function submitVote(uint _proposalId, uint _solutionChosen) external {
require(allProposalData[_proposalId].propStatus ==
uint(Governance.ProposalStatus.VotingStarted), "Not allowed");
require(_solutionChosen < allProposalSolutions[_proposalId].length);
_submitVote(_proposalId, _solutionChosen);
}
/**
* @dev Closes the proposal.
* @param _proposalId of proposal to be closed.
*/
function closeProposal(uint _proposalId) external {
uint category = allProposalData[_proposalId].category;
uint _memberRole;
if (allProposalData[_proposalId].dateUpd.add(maxDraftTime) <= now &&
allProposalData[_proposalId].propStatus < uint(ProposalStatus.VotingStarted)) {
_updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
} else {
require(canCloseProposal(_proposalId) == 1);
(, _memberRole,,,,,) = proposalCategory.category(allProposalData[_proposalId].category);
if (_memberRole == uint(MemberRoles.Role.AdvisoryBoard)) {
_closeAdvisoryBoardVote(_proposalId, category);
} else {
_closeMemberVote(_proposalId, category);
}
}
}
/**
* @dev Claims reward for member.
* @param _memberAddress to claim reward of.
* @param _maxRecords maximum number of records to claim reward for.
_proposals list of proposals of which reward will be claimed.
* @return amount of pending reward.
*/
function claimReward(address _memberAddress, uint _maxRecords)
external returns (uint pendingDAppReward)
{
uint voteId;
address leader;
uint lastUpd;
require(msg.sender == ms.getLatestAddress("CR"));
uint delegationId = followerDelegation[_memberAddress];
DelegateVote memory delegationData = allDelegation[delegationId];
if (delegationId > 0 && delegationData.leader != address(0)) {
leader = delegationData.leader;
lastUpd = delegationData.lastUpd;
} else
leader = _memberAddress;
uint proposalId;
uint totalVotes = allVotesByMember[leader].length;
uint lastClaimed = totalVotes;
uint j;
uint i;
for (i = lastRewardClaimed[_memberAddress]; i < totalVotes && j < _maxRecords; i++) {
voteId = allVotesByMember[leader][i];
proposalId = allVotes[voteId].proposalId;
if (proposalVoteTally[proposalId].voters > 0 && (allVotes[voteId].dateAdd > (
lastUpd.add(tokenHoldingTime)) || leader == _memberAddress)) {
if (allProposalData[proposalId].propStatus > uint(ProposalStatus.VotingStarted)) {
if (!rewardClaimed[voteId][_memberAddress]) {
pendingDAppReward = pendingDAppReward.add(
allProposalData[proposalId].commonIncentive.div(
proposalVoteTally[proposalId].voters
)
);
rewardClaimed[voteId][_memberAddress] = true;
j++;
}
} else {
if (lastClaimed == totalVotes) {
lastClaimed = i;
}
}
}
}
if (lastClaimed == totalVotes) {
lastRewardClaimed[_memberAddress] = i;
} else {
lastRewardClaimed[_memberAddress] = lastClaimed;
}
if (j > 0) {
emit RewardClaimed(
_memberAddress,
pendingDAppReward
);
}
}
/**
* @dev Sets delegation acceptance status of individual user
* @param _status delegation acceptance status
*/
function setDelegationStatus(bool _status) external isMemberAndcheckPause checkPendingRewards {
isOpenForDelegation[msg.sender] = _status;
}
/**
* @dev Delegates vote to an address.
* @param _add is the address to delegate vote to.
*/
function delegateVote(address _add) external isMemberAndcheckPause checkPendingRewards {
require(ms.masterInitialized());
require(allDelegation[followerDelegation[_add]].leader == address(0));
if (followerDelegation[msg.sender] > 0) {
require((allDelegation[followerDelegation[msg.sender]].lastUpd).add(tokenHoldingTime) < now);
}
require(!alreadyDelegated(msg.sender));
require(!memberRole.checkRole(msg.sender, uint(MemberRoles.Role.Owner)));
require(!memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)));
require(followerCount[_add] < maxFollowers);
if (allVotesByMember[msg.sender].length > 0) {
require((allVotes[allVotesByMember[msg.sender][allVotesByMember[msg.sender].length - 1]].dateAdd).add(tokenHoldingTime)
< now);
}
require(ms.isMember(_add));
require(isOpenForDelegation[_add]);
allDelegation.push(DelegateVote(msg.sender, _add, now));
followerDelegation[msg.sender] = allDelegation.length - 1;
leaderDelegation[_add].push(allDelegation.length - 1);
followerCount[_add]++;
lastRewardClaimed[msg.sender] = allVotesByMember[_add].length;
}
/**
* @dev Undelegates the sender
*/
function unDelegate() external isMemberAndcheckPause checkPendingRewards {
_unDelegate(msg.sender);
}
/**
* @dev Triggers action of accepted proposal after waiting time is finished
*/
function triggerAction(uint _proposalId) external {
require(proposalActionStatus[_proposalId] == uint(ActionStatus.Accepted) && proposalExecutionTime[_proposalId] <= now, "Cannot trigger");
_triggerAction(_proposalId, allProposalData[_proposalId].category);
}
/**
* @dev Provides option to Advisory board member to reject proposal action execution within actionWaitingTime, if found suspicious
*/
function rejectAction(uint _proposalId) external {
require(memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)) && proposalExecutionTime[_proposalId] > now);
require(proposalActionStatus[_proposalId] == uint(ActionStatus.Accepted));
require(!proposalRejectedByAB[_proposalId][msg.sender]);
require(
keccak256(proposalCategory.categoryActionHashes(allProposalData[_proposalId].category))
!= keccak256(abi.encodeWithSignature("swapABMember(address,address)"))
);
proposalRejectedByAB[_proposalId][msg.sender] = true;
actionRejectedCount[_proposalId]++;
emit ActionRejected(_proposalId, msg.sender);
if (actionRejectedCount[_proposalId] == AB_MAJ_TO_REJECT_ACTION) {
proposalActionStatus[_proposalId] = uint(ActionStatus.Rejected);
}
}
/**
* @dev Sets intial actionWaitingTime value
* To be called after governance implementation has been updated
*/
function setInitialActionParameters() external onlyOwner {
require(!actionParamsInitialised);
actionParamsInitialised = true;
actionWaitingTime = 24 * 1 hours;
}
/**
* @dev Gets Uint Parameters of a code
* @param code whose details we want
* @return string value of the code
* @return associated amount (time or perc or value) to the code
*/
function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
codeVal = code;
if (code == "GOVHOLD") {
val = tokenHoldingTime / (1 days);
} else if (code == "MAXFOL") {
val = maxFollowers;
} else if (code == "MAXDRFT") {
val = maxDraftTime / (1 days);
} else if (code == "EPTIME") {
val = ms.pauseTime() / (1 days);
} else if (code == "ACWT") {
val = actionWaitingTime / (1 hours);
}
}
/**
* @dev Gets all details of a propsal
* @param _proposalId whose details we want
* @return proposalId
* @return category
* @return status
* @return finalVerdict
* @return totalReward
*/
function proposal(uint _proposalId)
external
view
returns (
uint proposalId,
uint category,
uint status,
uint finalVerdict,
uint totalRewar
)
{
return (
_proposalId,
allProposalData[_proposalId].category,
allProposalData[_proposalId].propStatus,
allProposalData[_proposalId].finalVerdict,
allProposalData[_proposalId].commonIncentive
);
}
/**
* @dev Gets some details of a propsal
* @param _proposalId whose details we want
* @return proposalId
* @return number of all proposal solutions
* @return amount of votes
*/
function proposalDetails(uint _proposalId) external view returns (uint, uint, uint) {
return (
_proposalId,
allProposalSolutions[_proposalId].length,
proposalVoteTally[_proposalId].voters
);
}
/**
* @dev Gets solution action on a proposal
* @param _proposalId whose details we want
* @param _solution whose details we want
* @return action of a solution on a proposal
*/
function getSolutionAction(uint _proposalId, uint _solution) external view returns (uint, bytes memory) {
return (
_solution,
allProposalSolutions[_proposalId][_solution]
);
}
/**
* @dev Gets length of propsal
* @return length of propsal
*/
function getProposalLength() external view returns (uint) {
return totalProposals;
}
/**
* @dev Get followers of an address
* @return get followers of an address
*/
function getFollowers(address _add) external view returns (uint[] memory) {
return leaderDelegation[_add];
}
/**
* @dev Gets pending rewards of a member
* @param _memberAddress in concern
* @return amount of pending reward
*/
function getPendingReward(address _memberAddress)
public view returns (uint pendingDAppReward)
{
uint delegationId = followerDelegation[_memberAddress];
address leader;
uint lastUpd;
DelegateVote memory delegationData = allDelegation[delegationId];
if (delegationId > 0 && delegationData.leader != address(0)) {
leader = delegationData.leader;
lastUpd = delegationData.lastUpd;
} else
leader = _memberAddress;
uint proposalId;
for (uint i = lastRewardClaimed[_memberAddress]; i < allVotesByMember[leader].length; i++) {
if (allVotes[allVotesByMember[leader][i]].dateAdd > (
lastUpd.add(tokenHoldingTime)) || leader == _memberAddress) {
if (!rewardClaimed[allVotesByMember[leader][i]][_memberAddress]) {
proposalId = allVotes[allVotesByMember[leader][i]].proposalId;
if (proposalVoteTally[proposalId].voters > 0 && allProposalData[proposalId].propStatus
> uint(ProposalStatus.VotingStarted)) {
pendingDAppReward = pendingDAppReward.add(
allProposalData[proposalId].commonIncentive.div(
proposalVoteTally[proposalId].voters
)
);
}
}
}
}
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "GOVHOLD") {
tokenHoldingTime = val * 1 days;
} else if (code == "MAXFOL") {
maxFollowers = val;
} else if (code == "MAXDRFT") {
maxDraftTime = val * 1 days;
} else if (code == "EPTIME") {
ms.updatePauseTime(val * 1 days);
} else if (code == "ACWT") {
actionWaitingTime = val * 1 hours;
} else {
revert("Invalid code");
}
}
/**
* @dev Updates all dependency addresses to latest ones from Master
*/
function changeDependentContractAddress() public {
tokenInstance = TokenController(ms.dAppLocker());
memberRole = MemberRoles(ms.getLatestAddress("MR"));
proposalCategory = ProposalCategory(ms.getLatestAddress("PC"));
}
/**
* @dev Checks if msg.sender is allowed to create a proposal under given category
*/
function allowedToCreateProposal(uint category) public view returns (bool check) {
if (category == 0)
return true;
uint[] memory mrAllowed;
(,,,, mrAllowed,,) = proposalCategory.category(category);
for (uint i = 0; i < mrAllowed.length; i++) {
if (mrAllowed[i] == 0 || memberRole.checkRole(msg.sender, mrAllowed[i]))
return true;
}
}
/**
* @dev Checks if an address is already delegated
* @param _add in concern
* @return bool value if the address is delegated or not
*/
function alreadyDelegated(address _add) public view returns (bool delegated) {
for (uint i = 0; i < leaderDelegation[_add].length; i++) {
if (allDelegation[leaderDelegation[_add][i]].leader == _add) {
return true;
}
}
}
/**
* @dev Checks If the proposal voting time is up and it's ready to close
* i.e. Closevalue is 1 if proposal is ready to be closed, 2 if already closed, 0 otherwise!
* @param _proposalId Proposal id to which closing value is being checked
*/
function canCloseProposal(uint _proposalId)
public
view
returns (uint)
{
uint dateUpdate;
uint pStatus;
uint _closingTime;
uint _roleId;
uint majority;
pStatus = allProposalData[_proposalId].propStatus;
dateUpdate = allProposalData[_proposalId].dateUpd;
(, _roleId, majority, , , _closingTime,) = proposalCategory.category(allProposalData[_proposalId].category);
if (
pStatus == uint(ProposalStatus.VotingStarted)
) {
uint numberOfMembers = memberRole.numberOfMembers(_roleId);
if (_roleId == uint(MemberRoles.Role.AdvisoryBoard)) {
if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100).div(numberOfMembers) >= majority
|| proposalVoteTally[_proposalId].abVoteValue[1].add(proposalVoteTally[_proposalId].abVoteValue[0]) == numberOfMembers
|| dateUpdate.add(_closingTime) <= now) {
return 1;
}
} else {
if (numberOfMembers == proposalVoteTally[_proposalId].voters
|| dateUpdate.add(_closingTime) <= now)
return 1;
}
} else if (pStatus > uint(ProposalStatus.VotingStarted)) {
return 2;
} else {
return 0;
}
}
/**
* @dev Gets Id of member role allowed to categorize the proposal
* @return roleId allowed to categorize the proposal
*/
function allowedToCatgorize() public view returns (uint roleId) {
return roleIdAllowedToCatgorize;
}
/**
* @dev Gets vote tally data
* @param _proposalId in concern
* @param _solution of a proposal id
* @return member vote value
* @return advisory board vote value
* @return amount of votes
*/
function voteTallyData(uint _proposalId, uint _solution) public view returns (uint, uint, uint) {
return (proposalVoteTally[_proposalId].memberVoteValue[_solution],
proposalVoteTally[_proposalId].abVoteValue[_solution], proposalVoteTally[_proposalId].voters);
}
/**
* @dev Internal call to create proposal
* @param _proposalTitle of proposal
* @param _proposalSD is short description of proposal
* @param _proposalDescHash IPFS hash value of propsal
* @param _categoryId of proposal
*/
function _createProposal(
string memory _proposalTitle,
string memory _proposalSD,
string memory _proposalDescHash,
uint _categoryId
)
internal
{
require(proposalCategory.categoryABReq(_categoryId) == 0 || _categoryId == 0);
uint _proposalId = totalProposals;
allProposalData[_proposalId].owner = msg.sender;
allProposalData[_proposalId].dateUpd = now;
allProposalSolutions[_proposalId].push("");
totalProposals++;
emit Proposal(
msg.sender,
_proposalId,
now,
_proposalTitle,
_proposalSD,
_proposalDescHash
);
if (_categoryId > 0)
_categorizeProposal(_proposalId, _categoryId, 0);
}
/**
* @dev Internal call to categorize a proposal
* @param _proposalId of proposal
* @param _categoryId of proposal
* @param _incentive is commonIncentive
*/
function _categorizeProposal(
uint _proposalId,
uint _categoryId,
uint _incentive
)
internal
{
require(
_categoryId > 0 && _categoryId < proposalCategory.totalCategories(),
"Invalid category"
);
allProposalData[_proposalId].category = _categoryId;
allProposalData[_proposalId].commonIncentive = _incentive;
allProposalData[_proposalId].propStatus = uint(ProposalStatus.AwaitingSolution);
emit ProposalCategorized(_proposalId, msg.sender, _categoryId);
}
/**
* @dev Internal call to add solution to a proposal
* @param _proposalId in concern
* @param _action on that solution
* @param _solutionHash string value
*/
function _addSolution(uint _proposalId, bytes memory _action, string memory _solutionHash)
internal
{
allProposalSolutions[_proposalId].push(_action);
emit Solution(_proposalId, msg.sender, allProposalSolutions[_proposalId].length - 1, _solutionHash, now);
}
/**
* @dev Internal call to add solution and open proposal for voting
*/
function _proposalSubmission(
uint _proposalId,
string memory _solutionHash,
bytes memory _action
)
internal
{
uint _categoryId = allProposalData[_proposalId].category;
if (proposalCategory.categoryActionHashes(_categoryId).length == 0) {
require(keccak256(_action) == keccak256(""));
proposalActionStatus[_proposalId] = uint(ActionStatus.NoAction);
}
_addSolution(
_proposalId,
_action,
_solutionHash
);
_updateProposalStatus(_proposalId, uint(ProposalStatus.VotingStarted));
(, , , , , uint closingTime,) = proposalCategory.category(_categoryId);
emit CloseProposalOnTime(_proposalId, closingTime.add(now));
}
/**
* @dev Internal call to submit vote
* @param _proposalId of proposal in concern
* @param _solution for that proposal
*/
function _submitVote(uint _proposalId, uint _solution) internal {
uint delegationId = followerDelegation[msg.sender];
uint mrSequence;
uint majority;
uint closingTime;
(, mrSequence, majority, , , closingTime,) = proposalCategory.category(allProposalData[_proposalId].category);
require(allProposalData[_proposalId].dateUpd.add(closingTime) > now, "Closed");
require(memberProposalVote[msg.sender][_proposalId] == 0, "Not allowed");
require((delegationId == 0) || (delegationId > 0 && allDelegation[delegationId].leader == address(0) &&
_checkLastUpd(allDelegation[delegationId].lastUpd)));
require(memberRole.checkRole(msg.sender, mrSequence), "Not Authorized");
uint totalVotes = allVotes.length;
allVotesByMember[msg.sender].push(totalVotes);
memberProposalVote[msg.sender][_proposalId] = totalVotes;
allVotes.push(ProposalVote(msg.sender, _proposalId, now));
emit Vote(msg.sender, _proposalId, totalVotes, now, _solution);
if (mrSequence == uint(MemberRoles.Role.Owner)) {
if (_solution == 1)
_callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), allProposalData[_proposalId].category, 1, MemberRoles.Role.Owner);
else
_updateProposalStatus(_proposalId, uint(ProposalStatus.Rejected));
} else {
uint numberOfMembers = memberRole.numberOfMembers(mrSequence);
_setVoteTally(_proposalId, _solution, mrSequence);
if (mrSequence == uint(MemberRoles.Role.AdvisoryBoard)) {
if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100).div(numberOfMembers)
>= majority
|| (proposalVoteTally[_proposalId].abVoteValue[1].add(proposalVoteTally[_proposalId].abVoteValue[0])) == numberOfMembers) {
emit VoteCast(_proposalId);
}
} else {
if (numberOfMembers == proposalVoteTally[_proposalId].voters)
emit VoteCast(_proposalId);
}
}
}
/**
* @dev Internal call to set vote tally of a proposal
* @param _proposalId of proposal in concern
* @param _solution of proposal in concern
* @param mrSequence number of members for a role
*/
function _setVoteTally(uint _proposalId, uint _solution, uint mrSequence) internal
{
uint categoryABReq;
uint isSpecialResolution;
(, categoryABReq, isSpecialResolution) = proposalCategory.categoryExtendedData(allProposalData[_proposalId].category);
if (memberRole.checkRole(msg.sender, uint(MemberRoles.Role.AdvisoryBoard)) && (categoryABReq > 0) ||
mrSequence == uint(MemberRoles.Role.AdvisoryBoard)) {
proposalVoteTally[_proposalId].abVoteValue[_solution]++;
}
tokenInstance.lockForMemberVote(msg.sender, tokenHoldingTime);
if (mrSequence != uint(MemberRoles.Role.AdvisoryBoard)) {
uint voteWeight;
uint voters = 1;
uint tokenBalance = tokenInstance.totalBalanceOf(msg.sender);
uint totalSupply = tokenInstance.totalSupply();
if (isSpecialResolution == 1) {
voteWeight = tokenBalance.add(10 ** 18);
} else {
voteWeight = (_minOf(tokenBalance, maxVoteWeigthPer.mul(totalSupply).div(100))).add(10 ** 18);
}
DelegateVote memory delegationData;
for (uint i = 0; i < leaderDelegation[msg.sender].length; i++) {
delegationData = allDelegation[leaderDelegation[msg.sender][i]];
if (delegationData.leader == msg.sender &&
_checkLastUpd(delegationData.lastUpd)) {
if (memberRole.checkRole(delegationData.follower, mrSequence)) {
tokenBalance = tokenInstance.totalBalanceOf(delegationData.follower);
tokenInstance.lockForMemberVote(delegationData.follower, tokenHoldingTime);
voters++;
if (isSpecialResolution == 1) {
voteWeight = voteWeight.add(tokenBalance.add(10 ** 18));
} else {
voteWeight = voteWeight.add((_minOf(tokenBalance, maxVoteWeigthPer.mul(totalSupply).div(100))).add(10 ** 18));
}
}
}
}
proposalVoteTally[_proposalId].memberVoteValue[_solution] = proposalVoteTally[_proposalId].memberVoteValue[_solution].add(voteWeight);
proposalVoteTally[_proposalId].voters = proposalVoteTally[_proposalId].voters + voters;
}
}
/**
* @dev Gets minimum of two numbers
* @param a one of the two numbers
* @param b one of the two numbers
* @return minimum number out of the two
*/
function _minOf(uint a, uint b) internal pure returns (uint res) {
res = a;
if (res > b)
res = b;
}
/**
* @dev Check the time since last update has exceeded token holding time or not
* @param _lastUpd is last update time
* @return the bool which tells if the time since last update has exceeded token holding time or not
*/
function _checkLastUpd(uint _lastUpd) internal view returns (bool) {
return (now - _lastUpd) > tokenHoldingTime;
}
/**
* @dev Checks if the vote count against any solution passes the threshold value or not.
*/
function _checkForThreshold(uint _proposalId, uint _category) internal view returns (bool check) {
uint categoryQuorumPerc;
uint roleAuthorized;
(, roleAuthorized, , categoryQuorumPerc, , ,) = proposalCategory.category(_category);
check = ((proposalVoteTally[_proposalId].memberVoteValue[0]
.add(proposalVoteTally[_proposalId].memberVoteValue[1]))
.mul(100))
.div(
tokenInstance.totalSupply().add(
memberRole.numberOfMembers(roleAuthorized).mul(10 ** 18)
)
) >= categoryQuorumPerc;
}
/**
* @dev Called when vote majority is reached
* @param _proposalId of proposal in concern
* @param _status of proposal in concern
* @param category of proposal in concern
* @param max vote value of proposal in concern
*/
function _callIfMajReached(uint _proposalId, uint _status, uint category, uint max, MemberRoles.Role role) internal {
allProposalData[_proposalId].finalVerdict = max;
_updateProposalStatus(_proposalId, _status);
emit ProposalAccepted(_proposalId);
if (proposalActionStatus[_proposalId] != uint(ActionStatus.NoAction)) {
if (role == MemberRoles.Role.AdvisoryBoard) {
_triggerAction(_proposalId, category);
} else {
proposalActionStatus[_proposalId] = uint(ActionStatus.Accepted);
proposalExecutionTime[_proposalId] = actionWaitingTime.add(now);
}
}
}
/**
* @dev Internal function to trigger action of accepted proposal
*/
function _triggerAction(uint _proposalId, uint _categoryId) internal {
proposalActionStatus[_proposalId] = uint(ActionStatus.Executed);
bytes2 contractName;
address actionAddress;
bytes memory _functionHash;
(, actionAddress, contractName, , _functionHash) = proposalCategory.categoryActionDetails(_categoryId);
if (contractName == "MS") {
actionAddress = address(ms);
} else if (contractName != "EX") {
actionAddress = ms.getLatestAddress(contractName);
}
// solhint-disable-next-line avoid-low-level-calls
(bool actionStatus,) = actionAddress.call(abi.encodePacked(_functionHash, allProposalSolutions[_proposalId][1]));
if (actionStatus) {
emit ActionSuccess(_proposalId);
} else {
proposalActionStatus[_proposalId] = uint(ActionStatus.Accepted);
emit ActionFailed(_proposalId);
}
}
/**
* @dev Internal call to update proposal status
* @param _proposalId of proposal in concern
* @param _status of proposal to set
*/
function _updateProposalStatus(uint _proposalId, uint _status) internal {
if (_status == uint(ProposalStatus.Rejected) || _status == uint(ProposalStatus.Denied)) {
proposalActionStatus[_proposalId] = uint(ActionStatus.NoAction);
}
allProposalData[_proposalId].dateUpd = now;
allProposalData[_proposalId].propStatus = _status;
}
/**
* @dev Internal call to undelegate a follower
* @param _follower is address of follower to undelegate
*/
function _unDelegate(address _follower) internal {
uint followerId = followerDelegation[_follower];
if (followerId > 0) {
followerCount[allDelegation[followerId].leader] = followerCount[allDelegation[followerId].leader].sub(1);
allDelegation[followerId].leader = address(0);
allDelegation[followerId].lastUpd = now;
lastRewardClaimed[_follower] = allVotesByMember[_follower].length;
}
}
/**
* @dev Internal call to close member voting
* @param _proposalId of proposal in concern
* @param category of proposal in concern
*/
function _closeMemberVote(uint _proposalId, uint category) internal {
uint isSpecialResolution;
uint abMaj;
(, abMaj, isSpecialResolution) = proposalCategory.categoryExtendedData(category);
if (isSpecialResolution == 1) {
uint acceptedVotePerc = proposalVoteTally[_proposalId].memberVoteValue[1].mul(100)
.div(
tokenInstance.totalSupply().add(
memberRole.numberOfMembers(uint(MemberRoles.Role.Member)).mul(10 ** 18)
));
if (acceptedVotePerc >= specialResolutionMajPerc) {
_callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member);
} else {
_updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
}
} else {
if (_checkForThreshold(_proposalId, category)) {
uint majorityVote;
(,, majorityVote,,,,) = proposalCategory.category(category);
if (
((proposalVoteTally[_proposalId].memberVoteValue[1].mul(100))
.div(proposalVoteTally[_proposalId].memberVoteValue[0]
.add(proposalVoteTally[_proposalId].memberVoteValue[1])
))
>= majorityVote
) {
_callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member);
} else {
_updateProposalStatus(_proposalId, uint(ProposalStatus.Rejected));
}
} else {
if (abMaj > 0 && proposalVoteTally[_proposalId].abVoteValue[1].mul(100)
.div(memberRole.numberOfMembers(uint(MemberRoles.Role.AdvisoryBoard))) >= abMaj) {
_callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, MemberRoles.Role.Member);
} else {
_updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
}
}
}
if (proposalVoteTally[_proposalId].voters > 0) {
tokenInstance.mint(ms.getLatestAddress("CR"), allProposalData[_proposalId].commonIncentive);
}
}
/**
* @dev Internal call to close advisory board voting
* @param _proposalId of proposal in concern
* @param category of proposal in concern
*/
function _closeAdvisoryBoardVote(uint _proposalId, uint category) internal {
uint _majorityVote;
MemberRoles.Role _roleId = MemberRoles.Role.AdvisoryBoard;
(,, _majorityVote,,,,) = proposalCategory.category(category);
if (proposalVoteTally[_proposalId].abVoteValue[1].mul(100)
.div(memberRole.numberOfMembers(uint(_roleId))) >= _majorityVote) {
_callIfMajReached(_proposalId, uint(ProposalStatus.Accepted), category, 1, _roleId);
} else {
_updateProposalStatus(_proposalId, uint(ProposalStatus.Denied));
}
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "../capital/Pool.sol";
import "../claims/ClaimsReward.sol";
import "../token/NXMToken.sol";
import "../token/TokenController.sol";
import "../token/TokenFunctions.sol";
import "./ClaimsData.sol";
contract Claims is Iupgradable {
using SafeMath for uint;
TokenFunctions internal tf;
NXMToken internal tk;
TokenController internal tc;
ClaimsReward internal cr;
Pool internal p1;
ClaimsData internal cd;
TokenData internal td;
QuotationData internal qd;
uint private constant DECIMAL1E18 = uint(10) ** 18;
/**
* @dev Sets the status of claim using claim id.
* @param claimId claim id.
* @param stat status to be set.
*/
function setClaimStatus(uint claimId, uint stat) external onlyInternal {
_setClaimStatus(claimId, stat);
}
/**
* @dev Gets claim details of claim id = pending claim start + given index
*/
function getClaimFromNewStart(
uint index
)
external
view
returns (
uint coverId,
uint claimId,
int8 voteCA,
int8 voteMV,
uint statusnumber
)
{
(coverId, claimId, voteCA, voteMV, statusnumber) = cd.getClaimFromNewStart(index, msg.sender);
}
/**
* @dev Gets details of a claim submitted by the calling user, at a given index
*/
function getUserClaimByIndex(
uint index
)
external
view
returns (
uint status,
uint coverId,
uint claimId
)
{
uint statusno;
(statusno, coverId, claimId) = cd.getUserClaimByIndex(index, msg.sender);
status = statusno;
}
/**
* @dev Gets details of a given claim id.
* @param _claimId Claim Id.
* @return status Current status of claim id
* @return finalVerdict Decision made on the claim, 1 -> acceptance, -1 -> denial
* @return claimOwner Address through which claim is submitted
* @return coverId Coverid associated with the claim id
*/
function getClaimbyIndex(uint _claimId) external view returns (
uint claimId,
uint status,
int8 finalVerdict,
address claimOwner,
uint coverId
)
{
uint stat;
claimId = _claimId;
(, coverId, finalVerdict, stat,,) = cd.getClaim(_claimId);
claimOwner = qd.getCoverMemberAddress(coverId);
status = stat;
}
/**
* @dev Calculates total amount that has been used to assess a claim.
* Computaion:Adds acceptCA(tokens used for voting in favor of a claim)
* denyCA(tokens used for voting against a claim) * current token price.
* @param claimId Claim Id.
* @param member Member type 0 -> Claim Assessors, else members.
* @return tokens Total Amount used in Claims assessment.
*/
function getCATokens(uint claimId, uint member) external view returns (uint tokens) {
uint coverId;
(, coverId) = cd.getClaimCoverId(claimId);
bytes4 currency = qd.getCurrencyOfCover(coverId);
address asset = cr.getCurrencyAssetAddress(currency);
uint tokenx1e18 = p1.getTokenPrice(asset);
uint accept;
uint deny;
if (member == 0) {
(, accept, deny) = cd.getClaimsTokenCA(claimId);
} else {
(, accept, deny) = cd.getClaimsTokenMV(claimId);
}
tokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18); // amount (not in tokens)
}
/**
* Iupgradable Interface to update dependent contract address
*/
function changeDependentContractAddress() public onlyInternal {
tk = NXMToken(ms.tokenAddress());
td = TokenData(ms.getLatestAddress("TD"));
tf = TokenFunctions(ms.getLatestAddress("TF"));
tc = TokenController(ms.getLatestAddress("TC"));
p1 = Pool(ms.getLatestAddress("P1"));
cr = ClaimsReward(ms.getLatestAddress("CR"));
cd = ClaimsData(ms.getLatestAddress("CD"));
qd = QuotationData(ms.getLatestAddress("QD"));
}
/**
* @dev Updates the pending claim start variable,
* the lowest claim id with a pending decision/payout.
*/
function changePendingClaimStart() public onlyInternal {
uint origstat;
uint state12Count;
uint pendingClaimStart = cd.pendingClaimStart();
uint actualClaimLength = cd.actualClaimLength();
for (uint i = pendingClaimStart; i < actualClaimLength; i++) {
(, , , origstat, , state12Count) = cd.getClaim(i);
if (origstat > 5 && ((origstat != 12) || (origstat == 12 && state12Count >= 60))) {
cd.setpendingClaimStart(i);
} else {
break;
}
}
}
/**
* @dev Submits a claim for a given cover note.
* Adds claim to queue incase of emergency pause else directly submits the claim.
* @param coverId Cover Id.
*/
function submitClaim(uint coverId) public {
address qadd = qd.getCoverMemberAddress(coverId);
require(qadd == msg.sender);
uint8 cStatus;
(, cStatus,,,) = qd.getCoverDetailsByCoverID2(coverId);
require(cStatus != uint8(QuotationData.CoverStatus.ClaimSubmitted), "Claim already submitted");
require(cStatus != uint8(QuotationData.CoverStatus.CoverExpired), "Cover already expired");
if (ms.isPause() == false) {
_addClaim(coverId, now, qadd);
} else {
cd.setClaimAtEmergencyPause(coverId, now, false);
qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.Requested));
}
}
/**
* @dev Submits the Claims queued once the emergency pause is switched off.
*/
function submitClaimAfterEPOff() public onlyInternal {
uint lengthOfClaimSubmittedAtEP = cd.getLengthOfClaimSubmittedAtEP();
uint firstClaimIndexToSubmitAfterEP = cd.getFirstClaimIndexToSubmitAfterEP();
uint coverId;
uint dateUpd;
bool submit;
address qadd;
for (uint i = firstClaimIndexToSubmitAfterEP; i < lengthOfClaimSubmittedAtEP; i++) {
(coverId, dateUpd, submit) = cd.getClaimOfEmergencyPauseByIndex(i);
require(submit == false);
qadd = qd.getCoverMemberAddress(coverId);
_addClaim(coverId, dateUpd, qadd);
cd.setClaimSubmittedAtEPTrue(i, true);
}
cd.setFirstClaimIndexToSubmitAfterEP(lengthOfClaimSubmittedAtEP);
}
/**
* @dev Castes vote for members who have tokens locked under Claims Assessment
* @param claimId claim id.
* @param verdict 1 for Accept,-1 for Deny.
*/
function submitCAVote(uint claimId, int8 verdict) public isMemberAndcheckPause {
require(checkVoteClosing(claimId) != 1);
require(cd.userClaimVotePausedOn(msg.sender).add(cd.pauseDaysCA()) < now);
uint tokens = tc.tokensLockedAtTime(msg.sender, "CLA", now.add(cd.claimDepositTime()));
require(tokens > 0);
uint stat;
(, stat) = cd.getClaimStatusNumber(claimId);
require(stat == 0);
require(cd.getUserClaimVoteCA(msg.sender, claimId) == 0);
td.bookCATokens(msg.sender);
cd.addVote(msg.sender, tokens, claimId, verdict);
cd.callVoteEvent(msg.sender, claimId, "CAV", tokens, now, verdict);
uint voteLength = cd.getAllVoteLength();
cd.addClaimVoteCA(claimId, voteLength);
cd.setUserClaimVoteCA(msg.sender, claimId, voteLength);
cd.setClaimTokensCA(claimId, verdict, tokens);
tc.extendLockOf(msg.sender, "CLA", td.lockCADays());
int close = checkVoteClosing(claimId);
if (close == 1) {
cr.changeClaimStatus(claimId);
}
}
/**
* @dev Submits a member vote for assessing a claim.
* Tokens other than those locked under Claims
* Assessment can be used to cast a vote for a given claim id.
* @param claimId Selected claim id.
* @param verdict 1 for Accept,-1 for Deny.
*/
function submitMemberVote(uint claimId, int8 verdict) public isMemberAndcheckPause {
require(checkVoteClosing(claimId) != 1);
uint stat;
uint tokens = tc.totalBalanceOf(msg.sender);
(, stat) = cd.getClaimStatusNumber(claimId);
require(stat >= 1 && stat <= 5);
require(cd.getUserClaimVoteMember(msg.sender, claimId) == 0);
cd.addVote(msg.sender, tokens, claimId, verdict);
cd.callVoteEvent(msg.sender, claimId, "MV", tokens, now, verdict);
tc.lockForMemberVote(msg.sender, td.lockMVDays());
uint voteLength = cd.getAllVoteLength();
cd.addClaimVotemember(claimId, voteLength);
cd.setUserClaimVoteMember(msg.sender, claimId, voteLength);
cd.setClaimTokensMV(claimId, verdict, tokens);
int close = checkVoteClosing(claimId);
if (close == 1) {
cr.changeClaimStatus(claimId);
}
}
/**
* @dev Pause Voting of All Pending Claims when Emergency Pause Start.
*/
function pauseAllPendingClaimsVoting() public onlyInternal {
uint firstIndex = cd.pendingClaimStart();
uint actualClaimLength = cd.actualClaimLength();
for (uint i = firstIndex; i < actualClaimLength; i++) {
if (checkVoteClosing(i) == 0) {
uint dateUpd = cd.getClaimDateUpd(i);
cd.setPendingClaimDetails(i, (dateUpd.add(cd.maxVotingTime())).sub(now), false);
}
}
}
/**
* @dev Resume the voting phase of all Claims paused due to an emergency pause.
*/
function startAllPendingClaimsVoting() public onlyInternal {
uint firstIndx = cd.getFirstClaimIndexToStartVotingAfterEP();
uint i;
uint lengthOfClaimVotingPause = cd.getLengthOfClaimVotingPause();
for (i = firstIndx; i < lengthOfClaimVotingPause; i++) {
uint pendingTime;
uint claimID;
(claimID, pendingTime,) = cd.getPendingClaimDetailsByIndex(i);
uint pTime = (now.sub(cd.maxVotingTime())).add(pendingTime);
cd.setClaimdateUpd(claimID, pTime);
cd.setPendingClaimVoteStatus(i, true);
uint coverid;
(, coverid) = cd.getClaimCoverId(claimID);
address qadd = qd.getCoverMemberAddress(coverid);
tf.extendCNEPOff(qadd, coverid, pendingTime.add(cd.claimDepositTime()));
}
cd.setFirstClaimIndexToStartVotingAfterEP(i);
}
/**
* @dev Checks if voting of a claim should be closed or not.
* @param claimId Claim Id.
* @return close 1 -> voting should be closed, 0 -> if voting should not be closed,
* -1 -> voting has already been closed.
*/
function checkVoteClosing(uint claimId) public view returns (int8 close) {
close = 0;
uint status;
(, status) = cd.getClaimStatusNumber(claimId);
uint dateUpd = cd.getClaimDateUpd(claimId);
if (status == 12 && dateUpd.add(cd.payoutRetryTime()) < now) {
if (cd.getClaimState12Count(claimId) < 60)
close = 1;
}
if (status > 5 && status != 12) {
close = - 1;
} else if (status != 12 && dateUpd.add(cd.maxVotingTime()) <= now) {
close = 1;
} else if (status != 12 && dateUpd.add(cd.minVotingTime()) >= now) {
close = 0;
} else if (status == 0 || (status >= 1 && status <= 5)) {
close = _checkVoteClosingFinal(claimId, status);
}
}
/**
* @dev Checks if voting of a claim should be closed or not.
* Internally called by checkVoteClosing method
* for Claims whose status number is 0 or status number lie between 2 and 6.
* @param claimId Claim Id.
* @param status Current status of claim.
* @return close 1 if voting should be closed,0 in case voting should not be closed,
* -1 if voting has already been closed.
*/
function _checkVoteClosingFinal(uint claimId, uint status) internal view returns (int8 close) {
close = 0;
uint coverId;
(, coverId) = cd.getClaimCoverId(claimId);
bytes4 currency = qd.getCurrencyOfCover(coverId);
address asset = cr.getCurrencyAssetAddress(currency);
uint tokenx1e18 = p1.getTokenPrice(asset);
uint accept;
uint deny;
(, accept, deny) = cd.getClaimsTokenCA(claimId);
uint caTokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18);
(, accept, deny) = cd.getClaimsTokenMV(claimId);
uint mvTokens = ((accept.add(deny)).mul(tokenx1e18)).div(DECIMAL1E18);
uint sumassured = qd.getCoverSumAssured(coverId).mul(DECIMAL1E18);
if (status == 0 && caTokens >= sumassured.mul(10)) {
close = 1;
} else if (status >= 1 && status <= 5 && mvTokens >= sumassured.mul(10)) {
close = 1;
}
}
/**
* @dev Changes the status of an existing claim id, based on current
* status and current conditions of the system
* @param claimId Claim Id.
* @param stat status number.
*/
function _setClaimStatus(uint claimId, uint stat) internal {
uint origstat;
uint state12Count;
uint dateUpd;
uint coverId;
(, coverId, , origstat, dateUpd, state12Count) = cd.getClaim(claimId);
(, origstat) = cd.getClaimStatusNumber(claimId);
if (stat == 12 && origstat == 12) {
cd.updateState12Count(claimId, 1);
}
cd.setClaimStatus(claimId, stat);
if (state12Count >= 60 && stat == 12) {
cd.setClaimStatus(claimId, 13);
qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.ClaimDenied));
}
cd.setClaimdateUpd(claimId, now);
}
/**
* @dev Submits a claim for a given cover note.
* Set deposits flag against cover.
*/
function _addClaim(uint coverId, uint time, address add) internal {
tf.depositCN(coverId);
uint len = cd.actualClaimLength();
cd.addClaim(len, coverId, add, now);
cd.callClaimEvent(coverId, add, len, time);
qd.changeCoverStatusNo(coverId, uint8(QuotationData.CoverStatus.ClaimSubmitted));
}
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../abstract/Iupgradable.sol";
contract ClaimsData is Iupgradable {
using SafeMath for uint;
struct Claim {
uint coverId;
uint dateUpd;
}
struct Vote {
address voter;
uint tokens;
uint claimId;
int8 verdict;
bool rewardClaimed;
}
struct ClaimsPause {
uint coverid;
uint dateUpd;
bool submit;
}
struct ClaimPauseVoting {
uint claimid;
uint pendingTime;
bool voting;
}
struct RewardDistributed {
uint lastCAvoteIndex;
uint lastMVvoteIndex;
}
struct ClaimRewardDetails {
uint percCA;
uint percMV;
uint tokenToBeDist;
}
struct ClaimTotalTokens {
uint accept;
uint deny;
}
struct ClaimRewardStatus {
uint percCA;
uint percMV;
}
ClaimRewardStatus[] internal rewardStatus;
Claim[] internal allClaims;
Vote[] internal allvotes;
ClaimsPause[] internal claimPause;
ClaimPauseVoting[] internal claimPauseVotingEP;
mapping(address => RewardDistributed) internal voterVoteRewardReceived;
mapping(uint => ClaimRewardDetails) internal claimRewardDetail;
mapping(uint => ClaimTotalTokens) internal claimTokensCA;
mapping(uint => ClaimTotalTokens) internal claimTokensMV;
mapping(uint => int8) internal claimVote;
mapping(uint => uint) internal claimsStatus;
mapping(uint => uint) internal claimState12Count;
mapping(uint => uint[]) internal claimVoteCA;
mapping(uint => uint[]) internal claimVoteMember;
mapping(address => uint[]) internal voteAddressCA;
mapping(address => uint[]) internal voteAddressMember;
mapping(address => uint[]) internal allClaimsByAddress;
mapping(address => mapping(uint => uint)) internal userClaimVoteCA;
mapping(address => mapping(uint => uint)) internal userClaimVoteMember;
mapping(address => uint) public userClaimVotePausedOn;
uint internal claimPauseLastsubmit;
uint internal claimStartVotingFirstIndex;
uint public pendingClaimStart;
uint public claimDepositTime;
uint public maxVotingTime;
uint public minVotingTime;
uint public payoutRetryTime;
uint public claimRewardPerc;
uint public minVoteThreshold;
uint public maxVoteThreshold;
uint public majorityConsensus;
uint public pauseDaysCA;
event ClaimRaise(
uint indexed coverId,
address indexed userAddress,
uint claimId,
uint dateSubmit
);
event VoteCast(
address indexed userAddress,
uint indexed claimId,
bytes4 indexed typeOf,
uint tokens,
uint submitDate,
int8 verdict
);
constructor() public {
pendingClaimStart = 1;
maxVotingTime = 48 * 1 hours;
minVotingTime = 12 * 1 hours;
payoutRetryTime = 24 * 1 hours;
allvotes.push(Vote(address(0), 0, 0, 0, false));
allClaims.push(Claim(0, 0));
claimDepositTime = 7 days;
claimRewardPerc = 20;
minVoteThreshold = 5;
maxVoteThreshold = 10;
majorityConsensus = 70;
pauseDaysCA = 3 days;
_addRewardIncentive();
}
/**
* @dev Updates the pending claim start variable,
* the lowest claim id with a pending decision/payout.
*/
function setpendingClaimStart(uint _start) external onlyInternal {
require(pendingClaimStart <= _start);
pendingClaimStart = _start;
}
/**
* @dev Updates the max vote index for which claim assessor has received reward
* @param _voter address of the voter.
* @param caIndex last index till which reward was distributed for CA
*/
function setRewardDistributedIndexCA(address _voter, uint caIndex) external onlyInternal {
voterVoteRewardReceived[_voter].lastCAvoteIndex = caIndex;
}
/**
* @dev Used to pause claim assessor activity for 3 days
* @param user Member address whose claim voting ability needs to be paused
*/
function setUserClaimVotePausedOn(address user) external {
require(ms.checkIsAuthToGoverned(msg.sender));
userClaimVotePausedOn[user] = now;
}
/**
* @dev Updates the max vote index for which member has received reward
* @param _voter address of the voter.
* @param mvIndex last index till which reward was distributed for member
*/
function setRewardDistributedIndexMV(address _voter, uint mvIndex) external onlyInternal {
voterVoteRewardReceived[_voter].lastMVvoteIndex = mvIndex;
}
/**
* @param claimid claim id.
* @param percCA reward Percentage reward for claim assessor
* @param percMV reward Percentage reward for members
* @param tokens total tokens to be rewarded
*/
function setClaimRewardDetail(
uint claimid,
uint percCA,
uint percMV,
uint tokens
)
external
onlyInternal
{
claimRewardDetail[claimid].percCA = percCA;
claimRewardDetail[claimid].percMV = percMV;
claimRewardDetail[claimid].tokenToBeDist = tokens;
}
/**
* @dev Sets the reward claim status against a vote id.
* @param _voteid vote Id.
* @param claimed true if reward for vote is claimed, else false.
*/
function setRewardClaimed(uint _voteid, bool claimed) external onlyInternal {
allvotes[_voteid].rewardClaimed = claimed;
}
/**
* @dev Sets the final vote's result(either accepted or declined)of a claim.
* @param _claimId Claim Id.
* @param _verdict 1 if claim is accepted,-1 if declined.
*/
function changeFinalVerdict(uint _claimId, int8 _verdict) external onlyInternal {
claimVote[_claimId] = _verdict;
}
/**
* @dev Creates a new claim.
*/
function addClaim(
uint _claimId,
uint _coverId,
address _from,
uint _nowtime
)
external
onlyInternal
{
allClaims.push(Claim(_coverId, _nowtime));
allClaimsByAddress[_from].push(_claimId);
}
/**
* @dev Add Vote's details of a given claim.
*/
function addVote(
address _voter,
uint _tokens,
uint claimId,
int8 _verdict
)
external
onlyInternal
{
allvotes.push(Vote(_voter, _tokens, claimId, _verdict, false));
}
/**
* @dev Stores the id of the claim assessor vote given to a claim.
* Maintains record of all votes given by all the CA to a claim.
* @param _claimId Claim Id to which vote has given by the CA.
* @param _voteid Vote Id.
*/
function addClaimVoteCA(uint _claimId, uint _voteid) external onlyInternal {
claimVoteCA[_claimId].push(_voteid);
}
/**
* @dev Sets the id of the vote.
* @param _from Claim assessor's address who has given the vote.
* @param _claimId Claim Id for which vote has been given by the CA.
* @param _voteid Vote Id which will be stored against the given _from and claimid.
*/
function setUserClaimVoteCA(
address _from,
uint _claimId,
uint _voteid
)
external
onlyInternal
{
userClaimVoteCA[_from][_claimId] = _voteid;
voteAddressCA[_from].push(_voteid);
}
/**
* @dev Stores the tokens locked by the Claim Assessors during voting of a given claim.
* @param _claimId Claim Id.
* @param _vote 1 for accept and increases the tokens of claim as accept,
* -1 for deny and increases the tokens of claim as deny.
* @param _tokens Number of tokens.
*/
function setClaimTokensCA(uint _claimId, int8 _vote, uint _tokens) external onlyInternal {
if (_vote == 1)
claimTokensCA[_claimId].accept = claimTokensCA[_claimId].accept.add(_tokens);
if (_vote == - 1)
claimTokensCA[_claimId].deny = claimTokensCA[_claimId].deny.add(_tokens);
}
/**
* @dev Stores the tokens locked by the Members during voting of a given claim.
* @param _claimId Claim Id.
* @param _vote 1 for accept and increases the tokens of claim as accept,
* -1 for deny and increases the tokens of claim as deny.
* @param _tokens Number of tokens.
*/
function setClaimTokensMV(uint _claimId, int8 _vote, uint _tokens) external onlyInternal {
if (_vote == 1)
claimTokensMV[_claimId].accept = claimTokensMV[_claimId].accept.add(_tokens);
if (_vote == - 1)
claimTokensMV[_claimId].deny = claimTokensMV[_claimId].deny.add(_tokens);
}
/**
* @dev Stores the id of the member vote given to a claim.
* Maintains record of all votes given by all the Members to a claim.
* @param _claimId Claim Id to which vote has been given by the Member.
* @param _voteid Vote Id.
*/
function addClaimVotemember(uint _claimId, uint _voteid) external onlyInternal {
claimVoteMember[_claimId].push(_voteid);
}
/**
* @dev Sets the id of the vote.
* @param _from Member's address who has given the vote.
* @param _claimId Claim Id for which vote has been given by the Member.
* @param _voteid Vote Id which will be stored against the given _from and claimid.
*/
function setUserClaimVoteMember(
address _from,
uint _claimId,
uint _voteid
)
external
onlyInternal
{
userClaimVoteMember[_from][_claimId] = _voteid;
voteAddressMember[_from].push(_voteid);
}
/**
* @dev Increases the count of failure until payout of a claim is successful.
*/
function updateState12Count(uint _claimId, uint _cnt) external onlyInternal {
claimState12Count[_claimId] = claimState12Count[_claimId].add(_cnt);
}
/**
* @dev Sets status of a claim.
* @param _claimId Claim Id.
* @param _stat Status number.
*/
function setClaimStatus(uint _claimId, uint _stat) external onlyInternal {
claimsStatus[_claimId] = _stat;
}
/**
* @dev Sets the timestamp of a given claim at which the Claim's details has been updated.
* @param _claimId Claim Id of claim which has been changed.
* @param _dateUpd timestamp at which claim is updated.
*/
function setClaimdateUpd(uint _claimId, uint _dateUpd) external onlyInternal {
allClaims[_claimId].dateUpd = _dateUpd;
}
/**
@dev Queues Claims during Emergency Pause.
*/
function setClaimAtEmergencyPause(
uint _coverId,
uint _dateUpd,
bool _submit
)
external
onlyInternal
{
claimPause.push(ClaimsPause(_coverId, _dateUpd, _submit));
}
/**
* @dev Set submission flag for Claims queued during emergency pause.
* Set to true after EP is turned off and the claim is submitted .
*/
function setClaimSubmittedAtEPTrue(uint _index, bool _submit) external onlyInternal {
claimPause[_index].submit = _submit;
}
/**
* @dev Sets the index from which claim needs to be
* submitted when emergency pause is swithched off.
*/
function setFirstClaimIndexToSubmitAfterEP(
uint _firstClaimIndexToSubmit
)
external
onlyInternal
{
claimPauseLastsubmit = _firstClaimIndexToSubmit;
}
/**
* @dev Sets the pending vote duration for a claim in case of emergency pause.
*/
function setPendingClaimDetails(
uint _claimId,
uint _pendingTime,
bool _voting
)
external
onlyInternal
{
claimPauseVotingEP.push(ClaimPauseVoting(_claimId, _pendingTime, _voting));
}
/**
* @dev Sets voting flag true after claim is reopened for voting after emergency pause.
*/
function setPendingClaimVoteStatus(uint _claimId, bool _vote) external onlyInternal {
claimPauseVotingEP[_claimId].voting = _vote;
}
/**
* @dev Sets the index from which claim needs to be
* reopened when emergency pause is swithched off.
*/
function setFirstClaimIndexToStartVotingAfterEP(
uint _claimStartVotingFirstIndex
)
external
onlyInternal
{
claimStartVotingFirstIndex = _claimStartVotingFirstIndex;
}
/**
* @dev Calls Vote Event.
*/
function callVoteEvent(
address _userAddress,
uint _claimId,
bytes4 _typeOf,
uint _tokens,
uint _submitDate,
int8 _verdict
)
external
onlyInternal
{
emit VoteCast(
_userAddress,
_claimId,
_typeOf,
_tokens,
_submitDate,
_verdict
);
}
/**
* @dev Calls Claim Event.
*/
function callClaimEvent(
uint _coverId,
address _userAddress,
uint _claimId,
uint _datesubmit
)
external
onlyInternal
{
emit ClaimRaise(_coverId, _userAddress, _claimId, _datesubmit);
}
/**
* @dev Gets Uint Parameters by parameter code
* @param code whose details we want
* @return string value of the parameter
* @return associated amount (time or perc or value) to the code
*/
function getUintParameters(bytes8 code) external view returns (bytes8 codeVal, uint val) {
codeVal = code;
if (code == "CAMAXVT") {
val = maxVotingTime / (1 hours);
} else if (code == "CAMINVT") {
val = minVotingTime / (1 hours);
} else if (code == "CAPRETRY") {
val = payoutRetryTime / (1 hours);
} else if (code == "CADEPT") {
val = claimDepositTime / (1 days);
} else if (code == "CAREWPER") {
val = claimRewardPerc;
} else if (code == "CAMINTH") {
val = minVoteThreshold;
} else if (code == "CAMAXTH") {
val = maxVoteThreshold;
} else if (code == "CACONPER") {
val = majorityConsensus;
} else if (code == "CAPAUSET") {
val = pauseDaysCA / (1 days);
}
}
/**
* @dev Get claim queued during emergency pause by index.
*/
function getClaimOfEmergencyPauseByIndex(
uint _index
)
external
view
returns (
uint coverId,
uint dateUpd,
bool submit
)
{
coverId = claimPause[_index].coverid;
dateUpd = claimPause[_index].dateUpd;
submit = claimPause[_index].submit;
}
/**
* @dev Gets the Claim's details of given claimid.
*/
function getAllClaimsByIndex(
uint _claimId
)
external
view
returns (
uint coverId,
int8 vote,
uint status,
uint dateUpd,
uint state12Count
)
{
return (
allClaims[_claimId].coverId,
claimVote[_claimId],
claimsStatus[_claimId],
allClaims[_claimId].dateUpd,
claimState12Count[_claimId]
);
}
/**
* @dev Gets the vote id of a given claim of a given Claim Assessor.
*/
function getUserClaimVoteCA(
address _add,
uint _claimId
)
external
view
returns (uint idVote)
{
return userClaimVoteCA[_add][_claimId];
}
/**
* @dev Gets the vote id of a given claim of a given member.
*/
function getUserClaimVoteMember(
address _add,
uint _claimId
)
external
view
returns (uint idVote)
{
return userClaimVoteMember[_add][_claimId];
}
/**
* @dev Gets the count of all votes.
*/
function getAllVoteLength() external view returns (uint voteCount) {
return allvotes.length.sub(1); // Start Index always from 1.
}
/**
* @dev Gets the status number of a given claim.
* @param _claimId Claim id.
* @return statno Status Number.
*/
function getClaimStatusNumber(uint _claimId) external view returns (uint claimId, uint statno) {
return (_claimId, claimsStatus[_claimId]);
}
/**
* @dev Gets the reward percentage to be distributed for a given status id
* @param statusNumber the number of type of status
* @return percCA reward Percentage for claim assessor
* @return percMV reward Percentage for members
*/
function getRewardStatus(uint statusNumber) external view returns (uint percCA, uint percMV) {
return (rewardStatus[statusNumber].percCA, rewardStatus[statusNumber].percMV);
}
/**
* @dev Gets the number of tries that have been made for a successful payout of a Claim.
*/
function getClaimState12Count(uint _claimId) external view returns (uint num) {
num = claimState12Count[_claimId];
}
/**
* @dev Gets the last update date of a claim.
*/
function getClaimDateUpd(uint _claimId) external view returns (uint dateupd) {
dateupd = allClaims[_claimId].dateUpd;
}
/**
* @dev Gets all Claims created by a user till date.
* @param _member user's address.
* @return claimarr List of Claims id.
*/
function getAllClaimsByAddress(address _member) external view returns (uint[] memory claimarr) {
return allClaimsByAddress[_member];
}
/**
* @dev Gets the number of tokens that has been locked
* while giving vote to a claim by Claim Assessors.
* @param _claimId Claim Id.
* @return accept Total number of tokens when CA accepts the claim.
* @return deny Total number of tokens when CA declines the claim.
*/
function getClaimsTokenCA(
uint _claimId
)
external
view
returns (
uint claimId,
uint accept,
uint deny
)
{
return (
_claimId,
claimTokensCA[_claimId].accept,
claimTokensCA[_claimId].deny
);
}
/**
* @dev Gets the number of tokens that have been
* locked while assessing a claim as a member.
* @param _claimId Claim Id.
* @return accept Total number of tokens in acceptance of the claim.
* @return deny Total number of tokens against the claim.
*/
function getClaimsTokenMV(
uint _claimId
)
external
view
returns (
uint claimId,
uint accept,
uint deny
)
{
return (
_claimId,
claimTokensMV[_claimId].accept,
claimTokensMV[_claimId].deny
);
}
/**
* @dev Gets the total number of votes cast as Claims assessor for/against a given claim
*/
function getCaClaimVotesToken(uint _claimId) external view returns (uint claimId, uint cnt) {
claimId = _claimId;
cnt = 0;
for (uint i = 0; i < claimVoteCA[_claimId].length; i++) {
cnt = cnt.add(allvotes[claimVoteCA[_claimId][i]].tokens);
}
}
/**
* @dev Gets the total number of tokens cast as a member for/against a given claim
*/
function getMemberClaimVotesToken(
uint _claimId
)
external
view
returns (uint claimId, uint cnt)
{
claimId = _claimId;
cnt = 0;
for (uint i = 0; i < claimVoteMember[_claimId].length; i++) {
cnt = cnt.add(allvotes[claimVoteMember[_claimId][i]].tokens);
}
}
/**
* @dev Provides information of a vote when given its vote id.
* @param _voteid Vote Id.
*/
function getVoteDetails(uint _voteid)
external view
returns (
uint tokens,
uint claimId,
int8 verdict,
bool rewardClaimed
)
{
return (
allvotes[_voteid].tokens,
allvotes[_voteid].claimId,
allvotes[_voteid].verdict,
allvotes[_voteid].rewardClaimed
);
}
/**
* @dev Gets the voter's address of a given vote id.
*/
function getVoterVote(uint _voteid) external view returns (address voter) {
return allvotes[_voteid].voter;
}
/**
* @dev Provides information of a Claim when given its claim id.
* @param _claimId Claim Id.
*/
function getClaim(
uint _claimId
)
external
view
returns (
uint claimId,
uint coverId,
int8 vote,
uint status,
uint dateUpd,
uint state12Count
)
{
return (
_claimId,
allClaims[_claimId].coverId,
claimVote[_claimId],
claimsStatus[_claimId],
allClaims[_claimId].dateUpd,
claimState12Count[_claimId]
);
}
/**
* @dev Gets the total number of votes of a given claim.
* @param _claimId Claim Id.
* @param _ca if 1: votes given by Claim Assessors to a claim,
* else returns the number of votes of given by Members to a claim.
* @return len total number of votes for/against a given claim.
*/
function getClaimVoteLength(
uint _claimId,
uint8 _ca
)
external
view
returns (uint claimId, uint len)
{
claimId = _claimId;
if (_ca == 1)
len = claimVoteCA[_claimId].length;
else
len = claimVoteMember[_claimId].length;
}
/**
* @dev Gets the verdict of a vote using claim id and index.
* @param _ca 1 for vote given as a CA, else for vote given as a member.
* @return ver 1 if vote was given in favour,-1 if given in against.
*/
function getVoteVerdict(
uint _claimId,
uint _index,
uint8 _ca
)
external
view
returns (int8 ver)
{
if (_ca == 1)
ver = allvotes[claimVoteCA[_claimId][_index]].verdict;
else
ver = allvotes[claimVoteMember[_claimId][_index]].verdict;
}
/**
* @dev Gets the Number of tokens of a vote using claim id and index.
* @param _ca 1 for vote given as a CA, else for vote given as a member.
* @return tok Number of tokens.
*/
function getVoteToken(
uint _claimId,
uint _index,
uint8 _ca
)
external
view
returns (uint tok)
{
if (_ca == 1)
tok = allvotes[claimVoteCA[_claimId][_index]].tokens;
else
tok = allvotes[claimVoteMember[_claimId][_index]].tokens;
}
/**
* @dev Gets the Voter's address of a vote using claim id and index.
* @param _ca 1 for vote given as a CA, else for vote given as a member.
* @return voter Voter's address.
*/
function getVoteVoter(
uint _claimId,
uint _index,
uint8 _ca
)
external
view
returns (address voter)
{
if (_ca == 1)
voter = allvotes[claimVoteCA[_claimId][_index]].voter;
else
voter = allvotes[claimVoteMember[_claimId][_index]].voter;
}
/**
* @dev Gets total number of Claims created by a user till date.
* @param _add User's address.
*/
function getUserClaimCount(address _add) external view returns (uint len) {
len = allClaimsByAddress[_add].length;
}
/**
* @dev Calculates number of Claims that are in pending state.
*/
function getClaimLength() external view returns (uint len) {
len = allClaims.length.sub(pendingClaimStart);
}
/**
* @dev Gets the Number of all the Claims created till date.
*/
function actualClaimLength() external view returns (uint len) {
len = allClaims.length;
}
/**
* @dev Gets details of a claim.
* @param _index claim id = pending claim start + given index
* @param _add User's address.
* @return coverid cover against which claim has been submitted.
* @return claimId Claim Id.
* @return voteCA verdict of vote given as a Claim Assessor.
* @return voteMV verdict of vote given as a Member.
* @return statusnumber Status of claim.
*/
function getClaimFromNewStart(
uint _index,
address _add
)
external
view
returns (
uint coverid,
uint claimId,
int8 voteCA,
int8 voteMV,
uint statusnumber
)
{
uint i = pendingClaimStart.add(_index);
coverid = allClaims[i].coverId;
claimId = i;
if (userClaimVoteCA[_add][i] > 0)
voteCA = allvotes[userClaimVoteCA[_add][i]].verdict;
else
voteCA = 0;
if (userClaimVoteMember[_add][i] > 0)
voteMV = allvotes[userClaimVoteMember[_add][i]].verdict;
else
voteMV = 0;
statusnumber = claimsStatus[i];
}
/**
* @dev Gets details of a claim of a user at a given index.
*/
function getUserClaimByIndex(
uint _index,
address _add
)
external
view
returns (
uint status,
uint coverid,
uint claimId
)
{
claimId = allClaimsByAddress[_add][_index];
status = claimsStatus[claimId];
coverid = allClaims[claimId].coverId;
}
/**
* @dev Gets Id of all the votes given to a claim.
* @param _claimId Claim Id.
* @return ca id of all the votes given by Claim assessors to a claim.
* @return mv id of all the votes given by members to a claim.
*/
function getAllVotesForClaim(
uint _claimId
)
external
view
returns (
uint claimId,
uint[] memory ca,
uint[] memory mv
)
{
return (_claimId, claimVoteCA[_claimId], claimVoteMember[_claimId]);
}
/**
* @dev Gets Number of tokens deposit in a vote using
* Claim assessor's address and claim id.
* @return tokens Number of deposited tokens.
*/
function getTokensClaim(
address _of,
uint _claimId
)
external
view
returns (
uint claimId,
uint tokens
)
{
return (_claimId, allvotes[userClaimVoteCA[_of][_claimId]].tokens);
}
/**
* @param _voter address of the voter.
* @return lastCAvoteIndex last index till which reward was distributed for CA
* @return lastMVvoteIndex last index till which reward was distributed for member
*/
function getRewardDistributedIndex(
address _voter
)
external
view
returns (
uint lastCAvoteIndex,
uint lastMVvoteIndex
)
{
return (
voterVoteRewardReceived[_voter].lastCAvoteIndex,
voterVoteRewardReceived[_voter].lastMVvoteIndex
);
}
/**
* @param claimid claim id.
* @return perc_CA reward Percentage for claim assessor
* @return perc_MV reward Percentage for members
* @return tokens total tokens to be rewarded
*/
function getClaimRewardDetail(
uint claimid
)
external
view
returns (
uint percCA,
uint percMV,
uint tokens
)
{
return (
claimRewardDetail[claimid].percCA,
claimRewardDetail[claimid].percMV,
claimRewardDetail[claimid].tokenToBeDist
);
}
/**
* @dev Gets cover id of a claim.
*/
function getClaimCoverId(uint _claimId) external view returns (uint claimId, uint coverid) {
return (_claimId, allClaims[_claimId].coverId);
}
/**
* @dev Gets total number of tokens staked during voting by Claim Assessors.
* @param _claimId Claim Id.
* @param _verdict 1 to get total number of accept tokens, -1 to get total number of deny tokens.
* @return token token Number of tokens(either accept or deny on the basis of verdict given as parameter).
*/
function getClaimVote(uint _claimId, int8 _verdict) external view returns (uint claimId, uint token) {
claimId = _claimId;
token = 0;
for (uint i = 0; i < claimVoteCA[_claimId].length; i++) {
if (allvotes[claimVoteCA[_claimId][i]].verdict == _verdict)
token = token.add(allvotes[claimVoteCA[_claimId][i]].tokens);
}
}
/**
* @dev Gets total number of tokens staked during voting by Members.
* @param _claimId Claim Id.
* @param _verdict 1 to get total number of accept tokens,
* -1 to get total number of deny tokens.
* @return token token Number of tokens(either accept or
* deny on the basis of verdict given as parameter).
*/
function getClaimMVote(uint _claimId, int8 _verdict) external view returns (uint claimId, uint token) {
claimId = _claimId;
token = 0;
for (uint i = 0; i < claimVoteMember[_claimId].length; i++) {
if (allvotes[claimVoteMember[_claimId][i]].verdict == _verdict)
token = token.add(allvotes[claimVoteMember[_claimId][i]].tokens);
}
}
/**
* @param _voter address of voteid
* @param index index to get voteid in CA
*/
function getVoteAddressCA(address _voter, uint index) external view returns (uint) {
return voteAddressCA[_voter][index];
}
/**
* @param _voter address of voter
* @param index index to get voteid in member vote
*/
function getVoteAddressMember(address _voter, uint index) external view returns (uint) {
return voteAddressMember[_voter][index];
}
/**
* @param _voter address of voter
*/
function getVoteAddressCALength(address _voter) external view returns (uint) {
return voteAddressCA[_voter].length;
}
/**
* @param _voter address of voter
*/
function getVoteAddressMemberLength(address _voter) external view returns (uint) {
return voteAddressMember[_voter].length;
}
/**
* @dev Gets the Final result of voting of a claim.
* @param _claimId Claim id.
* @return verdict 1 if claim is accepted, -1 if declined.
*/
function getFinalVerdict(uint _claimId) external view returns (int8 verdict) {
return claimVote[_claimId];
}
/**
* @dev Get number of Claims queued for submission during emergency pause.
*/
function getLengthOfClaimSubmittedAtEP() external view returns (uint len) {
len = claimPause.length;
}
/**
* @dev Gets the index from which claim needs to be
* submitted when emergency pause is swithched off.
*/
function getFirstClaimIndexToSubmitAfterEP() external view returns (uint indexToSubmit) {
indexToSubmit = claimPauseLastsubmit;
}
/**
* @dev Gets number of Claims to be reopened for voting post emergency pause period.
*/
function getLengthOfClaimVotingPause() external view returns (uint len) {
len = claimPauseVotingEP.length;
}
/**
* @dev Gets claim details to be reopened for voting after emergency pause.
*/
function getPendingClaimDetailsByIndex(
uint _index
)
external
view
returns (
uint claimId,
uint pendingTime,
bool voting
)
{
claimId = claimPauseVotingEP[_index].claimid;
pendingTime = claimPauseVotingEP[_index].pendingTime;
voting = claimPauseVotingEP[_index].voting;
}
/**
* @dev Gets the index from which claim needs to be reopened when emergency pause is swithched off.
*/
function getFirstClaimIndexToStartVotingAfterEP() external view returns (uint firstindex) {
firstindex = claimStartVotingFirstIndex;
}
/**
* @dev Updates Uint Parameters of a code
* @param code whose details we want to update
* @param val value to set
*/
function updateUintParameters(bytes8 code, uint val) public {
require(ms.checkIsAuthToGoverned(msg.sender));
if (code == "CAMAXVT") {
_setMaxVotingTime(val * 1 hours);
} else if (code == "CAMINVT") {
_setMinVotingTime(val * 1 hours);
} else if (code == "CAPRETRY") {
_setPayoutRetryTime(val * 1 hours);
} else if (code == "CADEPT") {
_setClaimDepositTime(val * 1 days);
} else if (code == "CAREWPER") {
_setClaimRewardPerc(val);
} else if (code == "CAMINTH") {
_setMinVoteThreshold(val);
} else if (code == "CAMAXTH") {
_setMaxVoteThreshold(val);
} else if (code == "CACONPER") {
_setMajorityConsensus(val);
} else if (code == "CAPAUSET") {
_setPauseDaysCA(val * 1 days);
} else {
revert("Invalid param code");
}
}
/**
* @dev Iupgradable Interface to update dependent contract address
*/
function changeDependentContractAddress() public onlyInternal {}
/**
* @dev Adds status under which a claim can lie.
* @param percCA reward percentage for claim assessor
* @param percMV reward percentage for members
*/
function _pushStatus(uint percCA, uint percMV) internal {
rewardStatus.push(ClaimRewardStatus(percCA, percMV));
}
/**
* @dev adds reward incentive for all possible claim status for Claim assessors and members
*/
function _addRewardIncentive() internal {
_pushStatus(0, 0); // 0 Pending-Claim Assessor Vote
_pushStatus(0, 0); // 1 Pending-Claim Assessor Vote Denied, Pending Member Vote
_pushStatus(0, 0); // 2 Pending-CA Vote Threshold not Reached Accept, Pending Member Vote
_pushStatus(0, 0); // 3 Pending-CA Vote Threshold not Reached Deny, Pending Member Vote
_pushStatus(0, 0); // 4 Pending-CA Consensus not reached Accept, Pending Member Vote
_pushStatus(0, 0); // 5 Pending-CA Consensus not reached Deny, Pending Member Vote
_pushStatus(100, 0); // 6 Final-Claim Assessor Vote Denied
_pushStatus(100, 0); // 7 Final-Claim Assessor Vote Accepted
_pushStatus(0, 100); // 8 Final-Claim Assessor Vote Denied, MV Accepted
_pushStatus(0, 100); // 9 Final-Claim Assessor Vote Denied, MV Denied
_pushStatus(0, 0); // 10 Final-Claim Assessor Vote Accept, MV Nodecision
_pushStatus(0, 0); // 11 Final-Claim Assessor Vote Denied, MV Nodecision
_pushStatus(0, 0); // 12 Claim Accepted Payout Pending
_pushStatus(0, 0); // 13 Claim Accepted No Payout
_pushStatus(0, 0); // 14 Claim Accepted Payout Done
}
/**
* @dev Sets Maximum time(in seconds) for which claim assessment voting is open
*/
function _setMaxVotingTime(uint _time) internal {
maxVotingTime = _time;
}
/**
* @dev Sets Minimum time(in seconds) for which claim assessment voting is open
*/
function _setMinVotingTime(uint _time) internal {
minVotingTime = _time;
}
/**
* @dev Sets Minimum vote threshold required
*/
function _setMinVoteThreshold(uint val) internal {
minVoteThreshold = val;
}
/**
* @dev Sets Maximum vote threshold required
*/
function _setMaxVoteThreshold(uint val) internal {
maxVoteThreshold = val;
}
/**
* @dev Sets the value considered as Majority Consenus in voting
*/
function _setMajorityConsensus(uint val) internal {
majorityConsensus = val;
}
/**
* @dev Sets the payout retry time
*/
function _setPayoutRetryTime(uint _time) internal {
payoutRetryTime = _time;
}
/**
* @dev Sets percentage of reward given for claim assessment
*/
function _setClaimRewardPerc(uint _val) internal {
claimRewardPerc = _val;
}
/**
* @dev Sets the time for which claim is deposited.
*/
function _setClaimDepositTime(uint _time) internal {
claimDepositTime = _time;
}
/**
* @dev Sets number of days claim assessment will be paused
*/
function _setPauseDaysCA(uint val) internal {
pauseDaysCA = val;
}
}pragma solidity ^0.5.0;
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
interface OZIERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value)
external returns (bool);
function transferFrom(address from, address to, uint256 value)
external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender)
external view returns (uint256);
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
}pragma solidity ^0.5.0;
/**
* @title SafeMath
* @dev Math operations with safety checks that revert on error
*/
library OZSafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0); // Solidity only automatically asserts when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two numbers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}pragma solidity ^0.5.0;
/**
* @title ERC1132 interface
* @dev see https://github.com/ethereum/EIPs/issues/1132
*/
contract IERC1132 {
/**
* @dev Reasons why a user's tokens have been locked
*/
mapping(address => bytes32[]) public lockReason;
/**
* @dev locked token structure
*/
struct LockToken {
uint256 amount;
uint256 validity;
bool claimed;
}
/**
* @dev Holds number & validity of tokens locked for a given reason for
* a specified address
*/
mapping(address => mapping(bytes32 => LockToken)) public locked;
/**
* @dev Records data of all the tokens Locked
*/
event Locked(
address indexed _of,
bytes32 indexed _reason,
uint256 _amount,
uint256 _validity
);
/**
* @dev Records data of all the tokens unlocked
*/
event Unlocked(
address indexed _of,
bytes32 indexed _reason,
uint256 _amount
);
/**
* @dev Locks a specified amount of tokens against an address,
* for a specified reason and time
* @param _reason The reason to lock tokens
* @param _amount Number of tokens to be locked
* @param _time Lock time in seconds
*/
function lock(bytes32 _reason, uint256 _amount, uint256 _time)
public returns (bool);
/**
* @dev Returns tokens locked for a specified address for a
* specified reason
*
* @param _of The address whose tokens are locked
* @param _reason The reason to query the lock tokens for
*/
function tokensLocked(address _of, bytes32 _reason)
public view returns (uint256 amount);
/**
* @dev Returns tokens locked for a specified address for a
* specified reason at a specific time
*
* @param _of The address whose tokens are locked
* @param _reason The reason to query the lock tokens for
* @param _time The timestamp to query the lock tokens for
*/
function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time)
public view returns (uint256 amount);
/**
* @dev Returns total tokens held by an address (locked + transferable)
* @param _of The address to query the total balance of
*/
function totalBalanceOf(address _of)
public view returns (uint256 amount);
/**
* @dev Extends lock for a specified reason and time
* @param _reason The reason to lock tokens
* @param _time Lock extension time in seconds
*/
function extendLock(bytes32 _reason, uint256 _time)
public returns (bool);
/**
* @dev Increase number of tokens locked for a specified reason
* @param _reason The reason to lock tokens
* @param _amount Number of tokens to be increased
*/
function increaseLockAmount(bytes32 _reason, uint256 _amount)
public returns (bool);
/**
* @dev Returns unlockable tokens for a specified address for a specified reason
* @param _of The address to query the the unlockable token count of
* @param _reason The reason to query the unlockable tokens for
*/
function tokensUnlockable(address _of, bytes32 _reason)
public view returns (uint256 amount);
/**
* @dev Unlocks the unlockable tokens of a specified address
* @param _of Address of user, claiming back unlockable tokens
*/
function unlock(address _of)
public returns (uint256 unlockableTokens);
/**
* @dev Gets the unlockable tokens of a specified address
* @param _of The address to query the the unlockable token count of
*/
function getUnlockableTokens(address _of)
public view returns (uint256 unlockableTokens);
}/* Copyright (C) 2017 GovBlocks.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
interface IMaster {
function getLatestAddress(bytes2 _module) external view returns (address);
}
contract Governed {
address public masterAddress; // Name of the dApp, needs to be set by contracts inheriting this contract
/// @dev modifier that allows only the authorized addresses to execute the function
modifier onlyAuthorizedToGovern() {
IMaster ms = IMaster(masterAddress);
require(ms.getLatestAddress("GV") == msg.sender, "Not authorized");
_;
}
/// @dev checks if an address is authorized to govern
function isAuthorizedToGovern(address _toCheck) public view returns (bool) {
IMaster ms = IMaster(masterAddress);
return (ms.getLatestAddress("GV") == _toCheck);
}
}/* Copyright (C) 2017 GovBlocks.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
contract IGovernance {
event Proposal(
address indexed proposalOwner,
uint256 indexed proposalId,
uint256 dateAdd,
string proposalTitle,
string proposalSD,
string proposalDescHash
);
event Solution(
uint256 indexed proposalId,
address indexed solutionOwner,
uint256 indexed solutionId,
string solutionDescHash,
uint256 dateAdd
);
event Vote(
address indexed from,
uint256 indexed proposalId,
uint256 indexed voteId,
uint256 dateAdd,
uint256 solutionChosen
);
event RewardClaimed(
address indexed member,
uint gbtReward
);
/// @dev VoteCast event is called whenever a vote is cast that can potentially close the proposal.
event VoteCast (uint256 proposalId);
/// @dev ProposalAccepted event is called when a proposal is accepted so that a server can listen that can
/// call any offchain actions
event ProposalAccepted (uint256 proposalId);
/// @dev CloseProposalOnTime event is called whenever a proposal is created or updated to close it on time.
event CloseProposalOnTime (
uint256 indexed proposalId,
uint256 time
);
/// @dev ActionSuccess event is called whenever an onchain action is executed.
event ActionSuccess (
uint256 proposalId
);
/// @dev Creates a new proposal
/// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
/// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
function createProposal(
string calldata _proposalTitle,
string calldata _proposalSD,
string calldata _proposalDescHash,
uint _categoryId
)
external;
/// @dev Edits the details of an existing proposal and creates new version
/// @param _proposalId Proposal id that details needs to be updated
/// @param _proposalDescHash Proposal description hash having long and short description of proposal.
function updateProposal(
uint _proposalId,
string calldata _proposalTitle,
string calldata _proposalSD,
string calldata _proposalDescHash
)
external;
/// @dev Categorizes proposal to proceed further. Categories shows the proposal objective.
function categorizeProposal(
uint _proposalId,
uint _categoryId,
uint _incentives
)
external;
/// @dev Submit proposal with solution
/// @param _proposalId Proposal id
/// @param _solutionHash Solution hash contains parameters, values and description needed according to proposal
function submitProposalWithSolution(
uint _proposalId,
string calldata _solutionHash,
bytes calldata _action
)
external;
/// @dev Creates a new proposal with solution and votes for the solution
/// @param _proposalDescHash Proposal description hash through IPFS having Short and long description of proposal
/// @param _categoryId This id tells under which the proposal is categorized i.e. Proposal's Objective
/// @param _solutionHash Solution hash contains parameters, values and description needed according to proposal
function createProposalwithSolution(
string calldata _proposalTitle,
string calldata _proposalSD,
string calldata _proposalDescHash,
uint _categoryId,
string calldata _solutionHash,
bytes calldata _action
)
external;
/// @dev Casts vote
/// @param _proposalId Proposal id
/// @param _solutionChosen solution chosen while voting. _solutionChosen[0] is the chosen solution
function submitVote(uint _proposalId, uint _solutionChosen) external;
function closeProposal(uint _proposalId) external;
function claimReward(address _memberAddress, uint _maxRecords) external returns (uint pendingDAppReward);
function proposal(uint _proposalId)
external
view
returns (
uint proposalId,
uint category,
uint status,
uint finalVerdict,
uint totalReward
);
function canCloseProposal(uint _proposalId) public view returns (uint closeValue);
function allowedToCatgorize() public view returns (uint roleId);
}/* Copyright (C) 2017 GovBlocks.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
contract IProposalCategory {
event Category(
uint indexed categoryId,
string categoryName,
string actionHash
);
/// @dev Adds new category
/// @param _name Category name
/// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
/// @param _allowedToCreateProposal Member roles allowed to create the proposal
/// @param _majorityVotePerc Majority Vote threshold for Each voting layer
/// @param _quorumPerc minimum threshold percentage required in voting to calculate result
/// @param _closingTime Vote closing time for Each voting layer
/// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
/// @param _contractAddress address of contract to call after proposal is accepted
/// @param _contractName name of contract to be called after proposal is accepted
/// @param _incentives rewards to distributed after proposal is accepted
function addCategory(
string calldata _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] calldata _allowedToCreateProposal,
uint _closingTime,
string calldata _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] calldata _incentives
)
external;
/// @dev gets category details
function category(uint _categoryId)
external
view
returns (
uint categoryId,
uint memberRoleToVote,
uint majorityVotePerc,
uint quorumPerc,
uint[] memory allowedToCreateProposal,
uint closingTime,
uint minStake
);
///@dev gets category action details
function categoryAction(uint _categoryId)
external
view
returns (
uint categoryId,
address contractAddress,
bytes2 contractName,
uint defaultIncentive
);
/// @dev Gets Total number of categories added till now
function totalCategories() external view returns (uint numberOfCategories);
/// @dev Updates category details
/// @param _categoryId Category id that needs to be updated
/// @param _name Category name
/// @param _memberRoleToVote Voting Layer sequence in which the voting has to be performed.
/// @param _allowedToCreateProposal Member roles allowed to create the proposal
/// @param _majorityVotePerc Majority Vote threshold for Each voting layer
/// @param _quorumPerc minimum threshold percentage required in voting to calculate result
/// @param _closingTime Vote closing time for Each voting layer
/// @param _actionHash hash of details containing the action that has to be performed after proposal is accepted
/// @param _contractAddress address of contract to call after proposal is accepted
/// @param _contractName name of contract to be called after proposal is accepted
/// @param _incentives rewards to distributed after proposal is accepted
function updateCategory(
uint _categoryId,
string memory _name,
uint _memberRoleToVote,
uint _majorityVotePerc,
uint _quorumPerc,
uint[] memory _allowedToCreateProposal,
uint _closingTime,
string memory _actionHash,
address _contractAddress,
bytes2 _contractName,
uint[] memory _incentives
)
public;
}pragma solidity ^0.5.0;
import './IUniswapV2Router01.sol';
contract IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}/* Copyright (C) 2020 NexusMutual.io
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/ */
pragma solidity ^0.5.0;
import "@uniswap/lib/contracts/libraries/FixedPoint.sol";
import "@uniswap/v2-periphery/contracts/libraries/UniswapV2OracleLibrary.sol";
contract TwapOracle {
using FixedPoint for *;
struct Bucket {
uint timestamp;
uint price0Cumulative;
uint price1Cumulative;
}
event Updated(address indexed pair, uint timestamp, uint price0Cumulative, uint price1Cumulative);
uint constant public periodSize = 1800;
uint constant public periodsPerWindow = 8;
uint constant public windowSize = periodSize * periodsPerWindow;
address public factory;
// token pair => Bucket[8]
mapping(address => Bucket[8]) public buckets;
constructor (address _factory) public {
factory = _factory;
}
/* utils */
// https://uniswap.org/docs/v2/smart-contract-integration/getting-pair-addresses/
function _pairFor(address _factory, address tokenA, address tokenB) internal pure returns (address pair) {
// sort tokens
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != token1, "TWAP: identical addresses");
require(token0 != address(0), "TWAP: zero address");
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
_factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
))));
}
function timestampToIndex(uint timestamp) internal pure returns (uint index) {
uint epochPeriod = timestamp / periodSize;
return epochPeriod % periodsPerWindow;
}
function pairFor(address tokenA, address tokenB) external view returns (address pair) {
return _pairFor(factory, tokenA, tokenB);
}
function currentBucketIndex() external view returns (uint index) {
return timestampToIndex(block.timestamp);
}
/* update */
function update(address[] calldata pairs) external {
for (uint i = 0; i < pairs.length; i++) {
// note: not reusing canUpdate() because we need the bucket variable
address pair = pairs[i];
uint index = timestampToIndex(block.timestamp);
Bucket storage bucket = buckets[pair][index];
if (block.timestamp - bucket.timestamp < periodSize) {
continue;
}
(uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
bucket.timestamp = block.timestamp;
bucket.price0Cumulative = price0Cumulative;
bucket.price1Cumulative = price1Cumulative;
emit Updated(pair, block.timestamp, price0Cumulative, price1Cumulative);
}
}
function canUpdate(address pair) external view returns (bool) {
uint index = timestampToIndex(block.timestamp);
Bucket storage bucket = buckets[pair][index];
uint timeElapsed = block.timestamp - bucket.timestamp;
return timeElapsed > periodSize;
}
/* consult */
function _getCumulativePrices(
address tokenIn,
address tokenOut
) internal view returns (uint priceCumulativeStart, uint priceCumulativeEnd, uint timeElapsed) {
uint currentIndex = timestampToIndex(block.timestamp);
uint firstBucketIndex = (currentIndex + 1) % periodsPerWindow;
address pair = _pairFor(factory, tokenIn, tokenOut);
Bucket storage firstBucket = buckets[pair][firstBucketIndex];
timeElapsed = block.timestamp - firstBucket.timestamp;
require(timeElapsed <= windowSize, "TWAP: missing historical reading");
require(timeElapsed >= windowSize - periodSize * 2, "TWAP: unexpected time elapsed");
(uint price0Cumulative, uint price1Cumulative,) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
if (tokenIn < tokenOut) {
return (firstBucket.price0Cumulative, price0Cumulative, timeElapsed);
}
return (firstBucket.price1Cumulative, price1Cumulative, timeElapsed);
}
function _computeAmountOut(
uint priceCumulativeStart,
uint priceCumulativeEnd,
uint timeElapsed,
uint amountIn
) internal pure returns (uint amountOut) {
// overflow is desired.
FixedPoint.uq112x112 memory priceAverage = FixedPoint.uq112x112(
uint224((priceCumulativeEnd - priceCumulativeStart) / timeElapsed)
);
return priceAverage.mul(amountIn).decode144();
}
/**
* @dev Returns the amount out corresponding to the amount in for a given token using the
* @dev moving average over the time range [now - [windowSize, windowSize - periodSize * 2], now]
* @dev update must have been called for the bucket corresponding to timestamp `now - windowSize`
*/
function consult(address tokenIn, uint amountIn, address tokenOut) external view returns (uint amountOut) {
uint pastPriceCumulative;
uint currentPriceCumulative;
uint timeElapsed;
(pastPriceCumulative, currentPriceCumulative, timeElapsed) = _getCumulativePrices(tokenIn, tokenOut);
return _computeAmountOut(
pastPriceCumulative,
currentPriceCumulative,
timeElapsed,
amountIn
);
}
}pragma solidity ^0.5.0;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}pragma solidity >=0.4.0;
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
library FixedPoint {
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
struct uq112x112 {
uint224 _x;
}
// range: [0, 2**144 - 1]
// resolution: 1 / 2**112
struct uq144x112 {
uint _x;
}
uint8 private constant RESOLUTION = 112;
// encode a uint112 as a UQ112x112
function encode(uint112 x) internal pure returns (uq112x112 memory) {
return uq112x112(uint224(x) << RESOLUTION);
}
// encodes a uint144 as a UQ144x112
function encode144(uint144 x) internal pure returns (uq144x112 memory) {
return uq144x112(uint256(x) << RESOLUTION);
}
// divide a UQ112x112 by a uint112, returning a UQ112x112
function div(uq112x112 memory self, uint112 x) internal pure returns (uq112x112 memory) {
require(x != 0, 'FixedPoint: DIV_BY_ZERO');
return uq112x112(self._x / uint224(x));
}
// multiply a UQ112x112 by a uint, returning a UQ144x112
// reverts on overflow
function mul(uq112x112 memory self, uint y) internal pure returns (uq144x112 memory) {
uint z;
require(y == 0 || (z = uint(self._x) * y) / y == uint(self._x), "FixedPoint: MULTIPLICATION_OVERFLOW");
return uq144x112(z);
}
// returns a UQ112x112 which represents the ratio of the numerator to the denominator
// equivalent to encode(numerator).div(denominator)
function fraction(uint112 numerator, uint112 denominator) internal pure returns (uq112x112 memory) {
require(denominator > 0, "FixedPoint: DIV_BY_ZERO");
return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
}
// decode a UQ112x112 into a uint112 by truncating after the radix point
function decode(uq112x112 memory self) internal pure returns (uint112) {
return uint112(self._x >> RESOLUTION);
}
// decode a UQ144x112 into a uint144 by truncating after the radix point
function decode144(uq144x112 memory self) internal pure returns (uint144) {
return uint144(self._x >> RESOLUTION);
}
}pragma solidity >=0.5.0;
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol';
import '@uniswap/lib/contracts/libraries/FixedPoint.sol';
// library with helper methods for oracles that are concerned with computing average prices
library UniswapV2OracleLibrary {
using FixedPoint for *;
// helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
function currentBlockTimestamp() internal view returns (uint32) {
return uint32(block.timestamp % 2 ** 32);
}
// produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
function currentCumulativePrices(
address pair
) internal view returns (uint price0Cumulative, uint price1Cumulative, uint32 blockTimestamp) {
blockTimestamp = currentBlockTimestamp();
price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
// if time has elapsed since the last update on the pair, mock the accumulated price values
(uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves();
if (blockTimestampLast != blockTimestamp) {
// subtraction overflow is desired
uint32 timeElapsed = blockTimestamp - blockTimestampLast;
// addition overflow is desired
// counterfactual
price0Cumulative += uint(FixedPoint.fraction(reserve1, reserve0)._x) * timeElapsed;
// counterfactual
price1Cumulative += uint(FixedPoint.fraction(reserve0, reserve1)._x) * timeElapsed;
}
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"},{"indexed":false,"internalType":"uint256","name":"holdedCoverID","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"reason","type":"bytes32"}],"name":"RefundEvent","type":"event"},{"constant":false,"inputs":[],"name":"changeDependentContractAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_masterAddress","type":"address"}],"name":"changeMasterAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"_cid","type":"uint256"}],"name":"checkCoverExpired","outputs":[{"internalType":"bool","name":"expire","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"cr","outputs":[{"internalType":"contract ClaimsReward","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_cid","type":"uint256"}],"name":"expireCover","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256[]","name":"coverDetails","type":"uint256[]"},{"internalType":"uint16","name":"coverPeriod","type":"uint16"},{"internalType":"bytes4","name":"curr","type":"bytes4"},{"internalType":"address","name":"smaratCA","type":"address"}],"name":"getOrderHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"userAdd","type":"address"}],"name":"getRecentHoldedCoverIdStatus","outputs":[{"internalType":"int256","name":"","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"hash","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"isValidSignature","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_add","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"kycVerdict","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"m1","outputs":[{"internalType":"contract MCR","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256[]","name":"coverDetails","type":"uint256[]"},{"internalType":"uint16","name":"coverPeriod","type":"uint16"},{"internalType":"bytes4","name":"coverCurr","type":"bytes4"},{"internalType":"address","name":"smartCAdd","type":"address"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"makeCoverUsingNXMTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"mr","outputs":[{"internalType":"contract MemberRoles","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ms","outputs":[{"internalType":"contract INXMMaster","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nxMasterAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pd","outputs":[{"internalType":"contract PoolData","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pool","outputs":[{"internalType":"contract Pool","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"qd","outputs":[{"internalType":"contract QuotationData","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_cid","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"removeSAFromCSA","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"sendEther","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"tc","outputs":[{"internalType":"contract TokenController","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"td","outputs":[{"internalType":"contract TokenData","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tf","outputs":[{"internalType":"contract TokenFunctions","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newAdd","type":"address"}],"name":"transferAssetsToNewContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address payable","name":"from","type":"address"},{"internalType":"address","name":"scAddress","type":"address"},{"internalType":"bytes4","name":"coverCurr","type":"bytes4"},{"internalType":"uint256[]","name":"coverDetails","type":"uint256[]"},{"internalType":"uint16","name":"coverPeriod","type":"uint16"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"verifyCoverDetails","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256[]","name":"coverDetails","type":"uint256[]"},{"internalType":"uint16","name":"coverPeriod","type":"uint16"},{"internalType":"bytes4","name":"curr","type":"bytes4"},{"internalType":"address","name":"smaratCA","type":"address"},{"internalType":"uint8","name":"_v","type":"uint8"},{"internalType":"bytes32","name":"_r","type":"bytes32"},{"internalType":"bytes32","name":"_s","type":"bytes32"}],"name":"verifySign","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50613a15806100206000396000f3fe6080604052600436106101665760003560e01c8063a0b2d57f116100d1578063d8b2114e1161008a578063e262997411610064578063e26299741461074e578063f17a3bec14610781578063f4136f7f14610796578063f6420e15146107c057610166565b8063d8b2114e146106d4578063da3ca8b51461070f578063dce2c8611461072457610166565b8063a0b2d57f1461054d578063a3ffac0d14610562578063a77f92ff14610577578063d077e3b11461058c578063d46655f41461066e578063d817a351146106a157610166565b806365a818251161012357806365a81825146102d657806377f4b690146103cf57806379c84328146103e45780637f68a79e146104c95780638b257d3d146104f95780639be363171461053857610166565b80630e29df221461016b5780630ea9c9841461017557806316f0115b1461018a5780633bf3301c146101bb5780634a178472146101d057806362eb33e3146102c1575b600080fd5b6101736107d5565b005b34801561018157600080fd5b506101736107d7565b34801561019657600080fd5b5061019f610d6b565b604080516001600160a01b039092168252519081900360200190f35b3480156101c757600080fd5b5061019f610d7a565b3480156101dc57600080fd5b5061017360048036036101008110156101f457600080fd5b6001600160a01b0382358116926020810135909116916001600160e01b03196040830135169190810190608081016060820135600160201b81111561023857600080fd5b82018360208201111561024a57600080fd5b803590602001918460208302840111600160201b8311171561026b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff83351693505060ff602083013516916040810135915060600135610d89565b3480156102cd57600080fd5b5061019f610e25565b3480156102e257600080fd5b506103bb600480360360e08110156102f957600080fd5b810190602081018135600160201b81111561031357600080fd5b82018360208201111561032557600080fd5b803590602001918460208302840111600160201b8311171561034657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff8335169350506001600160e01b0319602083013516916001600160a01b03604082013516915060ff6060820135169060808101359060a00135610e34565b604080519115158252519081900360200190f35b3480156103db57600080fd5b5061019f610f2a565b3480156103f057600080fd5b50610173600480360360e081101561040757600080fd5b810190602081018135600160201b81111561042157600080fd5b82018360208201111561043357600080fd5b803590602001918460208302840111600160201b8311171561045457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff8335169350506001600160e01b0319602083013516916001600160a01b03604082013516915060ff6060820135169060808101359060a00135610f39565b3480156104d557600080fd5b50610173600480360360408110156104ec57600080fd5b5080359060200135611105565b34801561050557600080fd5b506103bb6004803603608081101561051c57600080fd5b5080359060ff6020820135169060408101359060600135611193565b34801561054457600080fd5b5061019f611329565b34801561055957600080fd5b5061019f611338565b34801561056e57600080fd5b5061019f611347565b34801561058357600080fd5b5061019f611356565b34801561059857600080fd5b5061065c600480360360808110156105af57600080fd5b810190602081018135600160201b8111156105c957600080fd5b8201836020820111156105db57600080fd5b803590602001918460208302840111600160201b831117156105fc57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505050813561ffff1692505060208101356001600160e01b03191690604001356001600160a01b0316611365565b60408051918252519081900360200190f35b34801561067a57600080fd5b506101736004803603602081101561069157600080fd5b50356001600160a01b0316611466565b3480156106ad57600080fd5b5061065c600480360360208110156106c457600080fd5b50356001600160a01b03166114ef565b3480156106e057600080fd5b50610173600480360360408110156106f757600080fd5b506001600160a01b03813516906020013515156116a5565b34801561071b57600080fd5b5061019f61182c565b34801561073057600080fd5b506103bb6004803603602081101561074757600080fd5b503561183b565b34801561075a57600080fd5b506101736004803603602081101561077157600080fd5b50356001600160a01b03166118c9565b34801561078d57600080fd5b5061019f611d41565b3480156107a257600080fd5b50610173600480360360208110156107b957600080fd5b5035611d50565b3480156107cc57600080fd5b5061019f611fd2565b565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561082257600080fd5b505afa158015610836573d6000803e3d6000fd5b505050506040513d602081101561084c57600080fd5b505161085757600080fd5b600054604080516227050b60e31b8152614d4360f01b600482015290516001600160a01b0390921691630138285891602480820192602092909190829003018186803b1580156108a657600080fd5b505afa1580156108ba573d6000803e3d6000fd5b505050506040513d60208110156108d057600080fd5b5051600880546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b8152612a2360f11b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561093557600080fd5b505afa158015610949573d6000803e3d6000fd5b505050506040513d602081101561095f57600080fd5b5051600280546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261544360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b1580156109c457600080fd5b505afa1580156109d8573d6000803e3d6000fd5b505050506040513d60208110156109ee57600080fd5b5051600380546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261151160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610a5357600080fd5b505afa158015610a67573d6000803e3d6000fd5b505050506040513d6020811015610a7d57600080fd5b5051600480546001600160a01b0319166001600160a01b03928316178155600054604080516227050b60e31b815261145160f21b9381019390935251921691630138285891602480820192602092909190829003018186803b158015610ae257600080fd5b505afa158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b5051600780546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261141160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610b7157600080fd5b505afa158015610b85573d6000803e3d6000fd5b505050506040513d6020811015610b9b57600080fd5b5051600680546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526126a960f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610c0057600080fd5b505afa158015610c14573d6000803e3d6000fd5b505050506040513d6020811015610c2a57600080fd5b5051600980546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526121a960f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610c8f57600080fd5b505afa158015610ca3573d6000803e3d6000fd5b505050506040513d6020811015610cb957600080fd5b5051600a80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261503160f01b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610d1e57600080fd5b505afa158015610d32573d6000803e3d6000fd5b505050506040513d6020811015610d4857600080fd5b5051600580546001600160a01b0319166001600160a01b03909216919091179055565b6005546001600160a01b031681565b6002546001600160a01b031681565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015610dd457600080fd5b505afa158015610de8573d6000803e3d6000fd5b505050506040513d6020811015610dfe57600080fd5b5051610e0957600080fd5b610e1b88888888888888886000611fe1565b5050505050505050565b600a546001600160a01b031681565b60006001600160a01b038516610e4957600080fd5b600660009054906101000a90046001600160a01b03166001600160a01b0316634f9359456040518163ffffffff1660e01b815260040160206040518083038186803b158015610e9757600080fd5b505afa158015610eab573d6000803e3d6000fd5b505050506040513d6020811015610ec157600080fd5b5051600114610f015760405162461bcd60e51b81526004018080602001828103825260308152602001806139686030913960400191505060405180910390fd5b6000610f0f89898989611365565b9050610f1d81868686611193565b9998505050505050505050565b6006546001600160a01b031681565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015610f8657600080fd5b505afa158015610f9a573d6000803e3d6000fd5b505050506040513d6020811015610fb057600080fd5b505115801561103757506000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b15801561100557600080fd5b505afa158015611019573d6000803e3d6000fd5b505050506040513d602081101561102f57600080fd5b505115156001145b61104057600080fd5b60035487516001600160a01b03909116906379cc67909033908a90600290811061106657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156110bd57600080fd5b505af11580156110d1573d6000803e3d6000fd5b505050506040513d60208110156110e757600080fd5b506110fc90503385878a8a8888886001611fe1565b50505050505050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561115057600080fd5b505afa158015611164573d6000803e3d6000fd5b505050506040513d602081101561117a57600080fd5b505161118557600080fd5b61118f82826122ef565b5050565b600060606040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152509050600081876040516020018083805190602001908083835b602083106112055780518252601f1990920191602091820191016111e6565b51815160209384036101000a600019018019909216911617905292019384525060408051808503815284830180835281519184019190912060009182905282860180845281905260ff8d166060870152608086018c905260a086018b90529151919650945060019360c08082019450601f19830192918290030190855afa158015611294573d6000803e3d6000fd5b505060408051601f19810151600754633a34eba760e21b835292519094506001600160a01b03909216925063e8d3ae9c916004808301926020929190829003018186803b1580156112e457600080fd5b505afa1580156112f8573d6000803e3d6000fd5b505050506040513d602081101561130e57600080fd5b50516001600160a01b03908116911614979650505050505050565b6009546001600160a01b031681565b6000546001600160a01b031681565b6004546001600160a01b031681565b6008546001600160a01b031681565b60008460008151811061137457fe5b60200260200101518385848860018151811061138c57fe5b6020026020010151896002815181106113a157fe5b60200260200101518a6003815181106113b657fe5b60200260200101518b6004815181106113cb57fe5b602090810291909101810151604080518084019a909a526001600160e01b0319989098168989015260f09690961b6001600160f01b0319166044890152606094851b6bffffffffffffffffffffffff19166046890152605a880193909352607a870191909152609a86015260ba85019290925230901b60da840152815180840360ce01815260ee909301909152815191012095945050505050565b6000546001600160a01b0316156114c3576000546001600160a01b031633146114c3576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b039092166001600160a01b0319928316811790915560018054909216179055565b6007546040805163c60781bd60e01b81526001600160a01b03848116600483015291516000938493169163c60781bd916024808301926020929190829003018186803b15801561153e57600080fd5b505afa158015611552573d6000803e3d6000fd5b505050506040513d602081101561156857600080fd5b505190508061157c576000199150506116a0565b6007546000906001600160a01b031663f0bd0222856115a285600163ffffffff6124d616565b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b1580156115ef57600080fd5b505afa158015611603573d6000803e3d6000fd5b505050506040513d602081101561161957600080fd5b50516007546040805163baaa46f960e01b81526004810184905290519293506001600160a01b039091169163baaa46f991602480820192602092909190829003018186803b15801561166a57600080fd5b505afa15801561167e573d6000803e3d6000fd5b505050506040513d602081101561169457600080fd5b505192506116a0915050565b919050565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b1580156116f257600080fd5b505afa158015611706573d6000803e3d6000fd5b505050506040513d602081101561171c57600080fd5b50511561172857600080fd5b600a54600160a01b900460ff1615611779576040805162461bcd60e51b815260206004820152600f60248201526e2932b2b73a3930b73a1031b0b6361760891b604482015290519081900360640190fd5b600a805460ff60a01b1916600160a01b179055600754604080516384434b9f60e01b815290516001600160a01b03909216916384434b9f91600480820192602092909190829003018186803b1580156117d157600080fd5b505afa1580156117e5573d6000803e3d6000fd5b505050506040513d60208110156117fb57600080fd5b50516001600160a01b0316331461181157600080fd5b61181b8183612521565b5050600a805460ff60a01b19169055565b6007546001600160a01b031681565b6007546040805163388d67c360e11b815260048101849052905160009267ffffffffffffffff4216926001600160a01b039091169163711acf8691602480820192602092909190829003018186803b15801561189657600080fd5b505afa1580156118aa573d6000803e3d6000fd5b505050506040513d60208110156118c057600080fd5b50511092915050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561191457600080fd5b505afa158015611928573d6000803e3d6000fd5b505050506040513d602081101561193e57600080fd5b505161194957600080fd5b600a54600160a01b900460ff161561199a576040805162461bcd60e51b815260206004820152600f60248201526e2932b2b73a3930b73a1031b0b6361760891b604482015290519081900360640190fd5b600a805460ff60a01b1916600160a01b1790554760008115611a11576000839050806001600160a01b0316630e29df22846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156119f657600080fd5b505af1158015611a0a573d6000803e3d6000fd5b5050505050505b6006546040805163255f504960e01b815290516000926001600160a01b03169163255f5049916004808301926020929190829003018186803b158015611a5657600080fd5b505afa158015611a6a573d6000803e3d6000fd5b505050506040513d6020811015611a8057600080fd5b5051905060015b818167ffffffffffffffff161015611d2d57600654604080516303c40b1f60e41b815267ffffffffffffffff8416600482015290516000926001600160a01b031691633c40b1f0916024808301926020929190829003018186803b158015611aee57600080fd5b505afa158015611b02573d6000803e3d6000fd5b505050506040513d6020811015611b1857600080fd5b50516006546040805163e3dee23960e01b81526001600160e01b03198416600482015290519293506000926001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b158015611b7557600080fd5b505afa158015611b89573d6000803e3d6000fd5b505050506040513d6020811015611b9f57600080fd5b5051604080516370a0823160e01b815230600482015290519196508692506000916001600160a01b038416916370a08231916024808301926020929190829003018186803b158015611bf057600080fd5b505afa158015611c04573d6000803e3d6000fd5b505050506040513d6020811015611c1a57600080fd5b50511115611d2357604080516370a0823160e01b815230600482015290516001600160a01b0387169163a9059cbb918a9184916370a0823191602480820192602092909190829003018186803b158015611c7357600080fd5b505afa158015611c87573d6000803e3d6000fd5b505050506040513d6020811015611c9d57600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b03909316600484015260248301919091525160448083019260209291908290030181600087803b158015611cee57600080fd5b505af1158015611d02573d6000803e3d6000fd5b505050506040513d6020811015611d1857600080fd5b5051611d2357600080fd5b5050600101611a87565b5050600a805460ff60a01b19169055505050565b6001546001600160a01b031681565b611d598161183b565b8015611dde575060075460408051634b2cc0a160e01b81526004810184905290516003926001600160a01b031691634b2cc0a1916024808301926020929190829003018186803b158015611dac57600080fd5b505afa158015611dc0573d6000803e3d6000fd5b505050506040513d6020811015611dd657600080fd5b505160ff1614155b611de757600080fd5b6002546040805163217d488160e21b81526004810184905290516001600160a01b03909216916385f522049160248082019260009290919082900301818387803b158015611e3457600080fd5b505af1158015611e48573d6000803e3d6000fd5b50506007546040805163df09fea760e01b81526004810186905290516000945084935083926001600160a01b03169163df09fea79160248083019260c0929190829003018186803b158015611e9c57600080fd5b505afa158015611eb0573d6000803e3d6000fd5b505050506040513d60c0811015611ec657600080fd5b506040810151606082015160809092015191945092509050600160075460408051634b2cc0a160e01b81526004810188905290516001600160a01b0390921691634b2cc0a191602480820192602092909190829003018186803b158015611f2c57600080fd5b505afa158015611f40573d6000803e3d6000fd5b505050506040513d6020811015611f5657600080fd5b505160ff1614611f6a57611f6a84826122ef565b6007546040805163570e2f6560e11b8152600481018790526003602482015290516001600160a01b039092169163ae1c5eca9160448082019260009290919082900301818387803b158015611fbe57600080fd5b505af1158015610e1b573d6000803e3d6000fd5b6003546001600160a01b031681565b4286600381518110611fef57fe5b60200260200101511161200157600080fd5b60075486516001600160a01b039091169063922fd67f908890600490811061202557fe5b60200260200101516040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561206157600080fd5b505afa158015612075573d6000803e3d6000fd5b505050506040513d602081101561208b57600080fd5b50511561209757600080fd5b60075486516001600160a01b03909116906383fa958590889060049081106120bb57fe5b60200260200101516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156120f957600080fd5b505af115801561210d573d6000803e3d6000fd5b50505050601e8561ffff161015801561212c575061016d8561ffff1611155b6121675760405162461bcd60e51b81526004018080602001828103825260258152602001806139436025913960400191505060405180910390fd5b600a546040805163e3dee23960e01b81526001600160e01b03198a16600482015290516000926001600160a01b03169163e3dee239916024808301926020929190829003018186803b1580156121bc57600080fd5b505afa1580156121d0573d6000803e3d6000fd5b505050506040513d60208110156121e657600080fd5b505190506208aa8960eb1b6001600160e01b0319891614801590612208575081155b156122be5760055487516001600160a01b039091169063d311afb49083908d908b90600190811061223557fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b031681526020018281526020019350505050600060405180830381600087803b1580156122a557600080fd5b505af11580156122b9573d6000803e3d6000fd5b505050505b6122cd87878a8c898989610e34565b6122d657600080fd5b6122e38a8a8a8a8a61325d565b50505050505050505050565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561233c57600080fd5b505afa158015612350573d6000803e3d6000fd5b505050506040513d602081101561236657600080fd5b50511561237257600080fd5b6007546040805163df09fea760e01b815260048101859052905160009283926001600160a01b039091169163df09fea79160248082019260c092909190829003018186803b1580156123c357600080fd5b505afa1580156123d7573d6000803e3d6000fd5b505050506040513d60c08110156123ed57600080fd5b5060408082015160609092015160075482516360a230e760e01b81526001600160e01b0319831660048201526024810188905292519395509093506001600160a01b0316916360a230e79160448082019260009290919082900301818387803b15801561245957600080fd5b505af115801561246d573d6000803e3d6000fd5b5050600754604080516340ebb05760e11b81526001600160a01b0387811660048301526001600160e01b0319871660248301526044820189905291519190921693506381d760ae9250606480830192600092919082900301818387803b158015611fbe57600080fd5b600061251883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613756565b90505b92915050565b6007546040805163c60781bd60e01b81526001600160a01b03848116600483015291516000936125af9360019391169163c60781bd91602480820192602092909190829003018186803b15801561257757600080fd5b505afa15801561258b573d6000803e3d6000fd5b505050506040513d60208110156125a157600080fd5b50519063ffffffff6124d616565b6007546040805163785e811160e11b81526001600160a01b038681166004830152602482018590529151939450600093919092169163f0bd0222916044808301926020929190829003018186803b15801561260957600080fd5b505afa15801561261d573d6000803e3d6000fd5b505050506040513d602081101561263357600080fd5b505160408051600480825260a082019092529192506000918291829182916060919060208201608080388339505060075460408051633310490760e21b8152600481018b905290519394506000936001600160a01b03909216925063cc41241c9160248083019286929190829003018186803b1580156126b257600080fd5b505afa1580156126c6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260608110156126ef57600080fd5b81516020830151604080850180519151939592948301929184600160201b82111561271957600080fd5b90830190602082018581111561272e57600080fd5b82518660208202830111600160201b8211171561274a57600080fd5b82525081516020918201928201910280838360005b8381101561277757818101518382015260200161275f565b505050509050016040525050509091508093508197505050600760009054906101000a90046001600160a01b03166001600160a01b0316633f77ea65886040518263ffffffff1660e01b81526004018082815260200191505060806040518083038186803b1580156127e857600080fd5b505afa1580156127fc573d6000803e3d6000fd5b505050506040513d608081101561281257600080fd5b506020808201516040808401516060909401516007548251636f3adaf560e11b81526001600160a01b038d811660048301529351949b50959950909750169263de75b5ea926024808301939192829003018186803b15801561287357600080fd5b505afa158015612887573d6000803e3d6000fd5b505050506040513d602081101561289d57600080fd5b50516128a857600080fd5b60075460408051633d8ea1f560e21b81526001600160a01b038981166004830152600060248301819052925193169263f63a87d49260448084019391929182900301818387803b1580156128fb57600080fd5b505af115801561290f573d6000803e3d6000fd5b506001925061291c915050565b6007546040805163baaa46f960e01b8152600481018b905290516001600160a01b039092169163baaa46f991602480820192602092909190829003018186803b15801561296857600080fd5b505afa15801561297c573d6000803e3d6000fd5b505050506040513d602081101561299257600080fd5b50511461299e57600080fd5b6000600460009054906101000a90046001600160a01b03166001600160a01b0316636eeeaaa56040518163ffffffff1660e01b815260040160206040518083038186803b1580156129ee57600080fd5b505afa158015612a02573d6000803e3d6000fd5b505050506040513d6020811015612a1857600080fd5b505190508a15612fde57600954604080516307569f3960e11b81526001600160a01b038a8116600483015291519190921691630ead3e7291849160248082019260009290919082900301818588803b158015612a7357600080fd5b505af1158015612a87573d6000803e3d6000fd5b50505050504283600381518110612a9a57fe5b60200260200101511115612d7a576007546001600160a01b0316636e13d8c58960026040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b158015612af957600080fd5b505af1158015612b0d573d6000803e3d6000fd5b50505050846001600160e01b0319166208aa8960eb1b1415612bda5760055483516000916001600160a01b03169085906001908110612b4857fe5b6020908102919091010151604051600081818185875af1925050503d8060008114612b8f576040519150601f19603f3d011682016040523d82523d6000602084013e612b94565b606091505b5050905080612bd45760405162461bcd60e51b81526004018080602001828103825260288152602001806139986028913960400191505060405180910390fd5b50612d12565b6006546040805163e3dee23960e01b81526001600160e01b03198816600482015290516001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b158015612c2f57600080fd5b505afa158015612c43573d6000803e3d6000fd5b505050506040513d6020811015612c5957600080fd5b505160055484519193506001600160a01b038085169263a9059cbb929091169086906001908110612c8657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612cdd57600080fd5b505af1158015612cf1573d6000803e3d6000fd5b505050506040513d6020811015612d0757600080fd5b5051612d1257600080fd5b604080518981526912d650c814185cdcd95960b21b602082015281518d1515926001600160a01b038b16927fb9a54f82713d4f0f38515b9ab47189872914a2187d882fce60b1a21519224797929081900390910190a3612d75878787868861325d565b612fd9565b6007546001600160a01b0316636e13d8c58960046040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b158015612dcb57600080fd5b505af1158015612ddf573d6000803e3d6000fd5b50505050846001600160e01b0319166208aa8960eb1b1415612e4e57866001600160a01b03166108fc84600181518110612e1557fe5b60200260200101519081150290604051600060405180830381858888f19350505050158015612e48573d6000803e3d6000fd5b50612f80565b6006546040805163e3dee23960e01b81526001600160e01b03198816600482015290516001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b158015612ea357600080fd5b505afa158015612eb7573d6000803e3d6000fd5b505050506040513d6020811015612ecd57600080fd5b505183519092506001600160a01b0383169063a9059cbb90899086906001908110612ef457fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612f4b57600080fd5b505af1158015612f5f573d6000803e3d6000fd5b505050506040513d6020811015612f7557600080fd5b5051612f8057600080fd5b604080518981526b10dbdd995c8811985a5b195960a21b602082015281518d1515926001600160a01b038b16927fb9a54f82713d4f0f38515b9ab47189872914a2187d882fce60b1a21519224797929081900390910190a35b613250565b6007546001600160a01b0316636e13d8c58960036040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b15801561302f57600080fd5b505af1158015613043573d6000803e3d6000fd5b5083925050506208aa8960eb1b6001600160e01b03198716141561308f57613088828560018151811061307257fe5b60200260200101516137ed90919063ffffffff16565b90506131c1565b6006546040805163e3dee23960e01b81526001600160e01b03198916600482015290516001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b1580156130e457600080fd5b505afa1580156130f8573d6000803e3d6000fd5b505050506040513d602081101561310e57600080fd5b505184519093506001600160a01b0384169063a9059cbb908a908790600190811061313557fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561318c57600080fd5b505af11580156131a0573d6000803e3d6000fd5b505050506040513d60208110156131b657600080fd5b50516131c157600080fd5b6040516001600160a01b0389169082156108fc029083906000818181858888f193505050501580156131f7573d6000803e3d6000fd5b50604080518a81526912d650c811985a5b195960b21b602082015281518e1515926001600160a01b038c16927fb9a54f82713d4f0f38515b9ab47189872914a2187d882fce60b1a21519224797929081900390910190a3505b5050505050505050505050565b6007546040805163067157ff60e41b815290516000926001600160a01b0316916367157ff0916004808301926020929190829003018186803b1580156132a257600080fd5b505afa1580156132b6573d6000803e3d6000fd5b505050506040513d60208110156132cc57600080fd5b505160075484519192506001600160a01b031690638ce80a6590849086906000906132f357fe5b602002602001015189888a8960018151811061330b57fe5b60200260200101518a60028151811061332057fe5b60200260200101516040518863ffffffff1660e01b8152600401808861ffff1661ffff168152602001878152602001866001600160a01b03166001600160a01b03168152602001856001600160e01b0319166001600160e01b0319168152602001846001600160a01b03166001600160a01b03168152602001838152602001828152602001975050505050505050600060405180830381600087803b1580156133c857600080fd5b505af11580156133dc573d6000803e3d6000fd5b5050505060006134956064613489600760009054906101000a90046001600160a01b03166001600160a01b031663f3ab26cc6040518163ffffffff1660e01b815260040160206040518083038186803b15801561343857600080fd5b505afa15801561344c573d6000803e3d6000fd5b505050506040513d602081101561346257600080fd5b505187518890600290811061347357fe5b602002602001015161384790919063ffffffff16565b9063ffffffff6138a016565b600354604080516340c10f1960e01b81526001600160a01b038b811660048301526024820185905291519394509116916340c10f199160448082019260009290919082900301818387803b1580156134ec57600080fd5b505af1158015613500573d6000803e3d6000fd5b50506002546040805163f075be1f60e01b81526004810186905261ffff88166024820152604481018790526001600160a01b038c81166064830152915191909216935063f075be1f9250608480830192600092919082900301818387803b15801561356a57600080fd5b505af115801561357e573d6000803e3d6000fd5b505060075486516001600160a01b03909116925063a2c6bc259150879087906000906135a657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160e01b0319166001600160e01b031916815260200182815260200192505050600060405180830381600087803b1580156135ff57600080fd5b505af1158015613613573d6000803e3d6000fd5b505060075486516001600160a01b039091169250633994b3d5915088908890889060009061363d57fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160e01b0319166001600160e01b03191681526020018281526020019350505050600060405180830381600087803b1580156136af57600080fd5b505af11580156136c3573d6000803e3d6000fd5b50506002805487516001600160a01b03909116935063899ec98f92508991889181106136eb57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561374257600080fd5b505af1158015613250573d6000803e3d6000fd5b600081848411156137e55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156137aa578181015183820152602001613792565b50505050905090810190601f1680156137d75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015612518576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000826138565750600061251b565b8282028284828161386357fe5b04146125185760405162461bcd60e51b81526004018080602001828103825260218152602001806139c06021913960400191505060405180910390fd5b600061251883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506000818361392c5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156137aa578181015183820152602001613792565b50600083858161393857fe5b049594505050505056fe51756f746174696f6e3a20436f76657220706572696f64206f7574206f6620626f756e647343616e206e6f742062757920636f76657220756e74696c20636170207265616368656420666f72203173742074696d6551756f746174696f6e3a206574686572207472616e7366657220746f20706f6f6c206661696c6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a265627a7a72315820c7418180bc393df1bb830b0032c14935978a2c7abe4111441deb74f4739dbfc464736f6c63430005110032
Deployed Bytecode
0x6080604052600436106101665760003560e01c8063a0b2d57f116100d1578063d8b2114e1161008a578063e262997411610064578063e26299741461074e578063f17a3bec14610781578063f4136f7f14610796578063f6420e15146107c057610166565b8063d8b2114e146106d4578063da3ca8b51461070f578063dce2c8611461072457610166565b8063a0b2d57f1461054d578063a3ffac0d14610562578063a77f92ff14610577578063d077e3b11461058c578063d46655f41461066e578063d817a351146106a157610166565b806365a818251161012357806365a81825146102d657806377f4b690146103cf57806379c84328146103e45780637f68a79e146104c95780638b257d3d146104f95780639be363171461053857610166565b80630e29df221461016b5780630ea9c9841461017557806316f0115b1461018a5780633bf3301c146101bb5780634a178472146101d057806362eb33e3146102c1575b600080fd5b6101736107d5565b005b34801561018157600080fd5b506101736107d7565b34801561019657600080fd5b5061019f610d6b565b604080516001600160a01b039092168252519081900360200190f35b3480156101c757600080fd5b5061019f610d7a565b3480156101dc57600080fd5b5061017360048036036101008110156101f457600080fd5b6001600160a01b0382358116926020810135909116916001600160e01b03196040830135169190810190608081016060820135600160201b81111561023857600080fd5b82018360208201111561024a57600080fd5b803590602001918460208302840111600160201b8311171561026b57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff83351693505060ff602083013516916040810135915060600135610d89565b3480156102cd57600080fd5b5061019f610e25565b3480156102e257600080fd5b506103bb600480360360e08110156102f957600080fd5b810190602081018135600160201b81111561031357600080fd5b82018360208201111561032557600080fd5b803590602001918460208302840111600160201b8311171561034657600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff8335169350506001600160e01b0319602083013516916001600160a01b03604082013516915060ff6060820135169060808101359060a00135610e34565b604080519115158252519081900360200190f35b3480156103db57600080fd5b5061019f610f2a565b3480156103f057600080fd5b50610173600480360360e081101561040757600080fd5b810190602081018135600160201b81111561042157600080fd5b82018360208201111561043357600080fd5b803590602001918460208302840111600160201b8311171561045457600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505061ffff8335169350506001600160e01b0319602083013516916001600160a01b03604082013516915060ff6060820135169060808101359060a00135610f39565b3480156104d557600080fd5b50610173600480360360408110156104ec57600080fd5b5080359060200135611105565b34801561050557600080fd5b506103bb6004803603608081101561051c57600080fd5b5080359060ff6020820135169060408101359060600135611193565b34801561054457600080fd5b5061019f611329565b34801561055957600080fd5b5061019f611338565b34801561056e57600080fd5b5061019f611347565b34801561058357600080fd5b5061019f611356565b34801561059857600080fd5b5061065c600480360360808110156105af57600080fd5b810190602081018135600160201b8111156105c957600080fd5b8201836020820111156105db57600080fd5b803590602001918460208302840111600160201b831117156105fc57600080fd5b9190808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509295505050813561ffff1692505060208101356001600160e01b03191690604001356001600160a01b0316611365565b60408051918252519081900360200190f35b34801561067a57600080fd5b506101736004803603602081101561069157600080fd5b50356001600160a01b0316611466565b3480156106ad57600080fd5b5061065c600480360360208110156106c457600080fd5b50356001600160a01b03166114ef565b3480156106e057600080fd5b50610173600480360360408110156106f757600080fd5b506001600160a01b03813516906020013515156116a5565b34801561071b57600080fd5b5061019f61182c565b34801561073057600080fd5b506103bb6004803603602081101561074757600080fd5b503561183b565b34801561075a57600080fd5b506101736004803603602081101561077157600080fd5b50356001600160a01b03166118c9565b34801561078d57600080fd5b5061019f611d41565b3480156107a257600080fd5b50610173600480360360208110156107b957600080fd5b5035611d50565b3480156107cc57600080fd5b5061019f611fd2565b565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561082257600080fd5b505afa158015610836573d6000803e3d6000fd5b505050506040513d602081101561084c57600080fd5b505161085757600080fd5b600054604080516227050b60e31b8152614d4360f01b600482015290516001600160a01b0390921691630138285891602480820192602092909190829003018186803b1580156108a657600080fd5b505afa1580156108ba573d6000803e3d6000fd5b505050506040513d60208110156108d057600080fd5b5051600880546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b8152612a2360f11b6004820152905191909216916301382858916024808301926020929190829003018186803b15801561093557600080fd5b505afa158015610949573d6000803e3d6000fd5b505050506040513d602081101561095f57600080fd5b5051600280546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261544360f01b6004820152905191909216916301382858916024808301926020929190829003018186803b1580156109c457600080fd5b505afa1580156109d8573d6000803e3d6000fd5b505050506040513d60208110156109ee57600080fd5b5051600380546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261151160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610a5357600080fd5b505afa158015610a67573d6000803e3d6000fd5b505050506040513d6020811015610a7d57600080fd5b5051600480546001600160a01b0319166001600160a01b03928316178155600054604080516227050b60e31b815261145160f21b9381019390935251921691630138285891602480820192602092909190829003018186803b158015610ae257600080fd5b505afa158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b5051600780546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261141160f21b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610b7157600080fd5b505afa158015610b85573d6000803e3d6000fd5b505050506040513d6020811015610b9b57600080fd5b5051600680546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526126a960f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610c0057600080fd5b505afa158015610c14573d6000803e3d6000fd5b505050506040513d6020811015610c2a57600080fd5b5051600980546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b81526121a960f11b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610c8f57600080fd5b505afa158015610ca3573d6000803e3d6000fd5b505050506040513d6020811015610cb957600080fd5b5051600a80546001600160a01b0319166001600160a01b03928316179055600054604080516227050b60e31b815261503160f01b6004820152905191909216916301382858916024808301926020929190829003018186803b158015610d1e57600080fd5b505afa158015610d32573d6000803e3d6000fd5b505050506040513d6020811015610d4857600080fd5b5051600580546001600160a01b0319166001600160a01b03909216919091179055565b6005546001600160a01b031681565b6002546001600160a01b031681565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b158015610dd457600080fd5b505afa158015610de8573d6000803e3d6000fd5b505050506040513d6020811015610dfe57600080fd5b5051610e0957600080fd5b610e1b88888888888888886000611fe1565b5050505050505050565b600a546001600160a01b031681565b60006001600160a01b038516610e4957600080fd5b600660009054906101000a90046001600160a01b03166001600160a01b0316634f9359456040518163ffffffff1660e01b815260040160206040518083038186803b158015610e9757600080fd5b505afa158015610eab573d6000803e3d6000fd5b505050506040513d6020811015610ec157600080fd5b5051600114610f015760405162461bcd60e51b81526004018080602001828103825260308152602001806139686030913960400191505060405180910390fd5b6000610f0f89898989611365565b9050610f1d81868686611193565b9998505050505050505050565b6006546001600160a01b031681565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b158015610f8657600080fd5b505afa158015610f9a573d6000803e3d6000fd5b505050506040513d6020811015610fb057600080fd5b505115801561103757506000546040805163288c314960e21b815233600482015290516001600160a01b039092169163a230c52491602480820192602092909190829003018186803b15801561100557600080fd5b505afa158015611019573d6000803e3d6000fd5b505050506040513d602081101561102f57600080fd5b505115156001145b61104057600080fd5b60035487516001600160a01b03909116906379cc67909033908a90600290811061106657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156110bd57600080fd5b505af11580156110d1573d6000803e3d6000fd5b505050506040513d60208110156110e757600080fd5b506110fc90503385878a8a8888886001611fe1565b50505050505050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561115057600080fd5b505afa158015611164573d6000803e3d6000fd5b505050506040513d602081101561117a57600080fd5b505161118557600080fd5b61118f82826122ef565b5050565b600060606040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a3332000000008152509050600081876040516020018083805190602001908083835b602083106112055780518252601f1990920191602091820191016111e6565b51815160209384036101000a600019018019909216911617905292019384525060408051808503815284830180835281519184019190912060009182905282860180845281905260ff8d166060870152608086018c905260a086018b90529151919650945060019360c08082019450601f19830192918290030190855afa158015611294573d6000803e3d6000fd5b505060408051601f19810151600754633a34eba760e21b835292519094506001600160a01b03909216925063e8d3ae9c916004808301926020929190829003018186803b1580156112e457600080fd5b505afa1580156112f8573d6000803e3d6000fd5b505050506040513d602081101561130e57600080fd5b50516001600160a01b03908116911614979650505050505050565b6009546001600160a01b031681565b6000546001600160a01b031681565b6004546001600160a01b031681565b6008546001600160a01b031681565b60008460008151811061137457fe5b60200260200101518385848860018151811061138c57fe5b6020026020010151896002815181106113a157fe5b60200260200101518a6003815181106113b657fe5b60200260200101518b6004815181106113cb57fe5b602090810291909101810151604080518084019a909a526001600160e01b0319989098168989015260f09690961b6001600160f01b0319166044890152606094851b6bffffffffffffffffffffffff19166046890152605a880193909352607a870191909152609a86015260ba85019290925230901b60da840152815180840360ce01815260ee909301909152815191012095945050505050565b6000546001600160a01b0316156114c3576000546001600160a01b031633146114c3576040805162461bcd60e51b815260206004820152600a6024820152692737ba1036b0b9ba32b960b11b604482015290519081900360640190fd5b600080546001600160a01b039092166001600160a01b0319928316811790915560018054909216179055565b6007546040805163c60781bd60e01b81526001600160a01b03848116600483015291516000938493169163c60781bd916024808301926020929190829003018186803b15801561153e57600080fd5b505afa158015611552573d6000803e3d6000fd5b505050506040513d602081101561156857600080fd5b505190508061157c576000199150506116a0565b6007546000906001600160a01b031663f0bd0222856115a285600163ffffffff6124d616565b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b031681526020018281526020019250505060206040518083038186803b1580156115ef57600080fd5b505afa158015611603573d6000803e3d6000fd5b505050506040513d602081101561161957600080fd5b50516007546040805163baaa46f960e01b81526004810184905290519293506001600160a01b039091169163baaa46f991602480820192602092909190829003018186803b15801561166a57600080fd5b505afa15801561167e573d6000803e3d6000fd5b505050506040513d602081101561169457600080fd5b505192506116a0915050565b919050565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b1580156116f257600080fd5b505afa158015611706573d6000803e3d6000fd5b505050506040513d602081101561171c57600080fd5b50511561172857600080fd5b600a54600160a01b900460ff1615611779576040805162461bcd60e51b815260206004820152600f60248201526e2932b2b73a3930b73a1031b0b6361760891b604482015290519081900360640190fd5b600a805460ff60a01b1916600160a01b179055600754604080516384434b9f60e01b815290516001600160a01b03909216916384434b9f91600480820192602092909190829003018186803b1580156117d157600080fd5b505afa1580156117e5573d6000803e3d6000fd5b505050506040513d60208110156117fb57600080fd5b50516001600160a01b0316331461181157600080fd5b61181b8183612521565b5050600a805460ff60a01b19169055565b6007546001600160a01b031681565b6007546040805163388d67c360e11b815260048101849052905160009267ffffffffffffffff4216926001600160a01b039091169163711acf8691602480820192602092909190829003018186803b15801561189657600080fd5b505afa1580156118aa573d6000803e3d6000fd5b505050506040513d60208110156118c057600080fd5b50511092915050565b600054604080516323c5b10760e21b815233600482015290516001600160a01b0390921691638f16c41c91602480820192602092909190829003018186803b15801561191457600080fd5b505afa158015611928573d6000803e3d6000fd5b505050506040513d602081101561193e57600080fd5b505161194957600080fd5b600a54600160a01b900460ff161561199a576040805162461bcd60e51b815260206004820152600f60248201526e2932b2b73a3930b73a1031b0b6361760891b604482015290519081900360640190fd5b600a805460ff60a01b1916600160a01b1790554760008115611a11576000839050806001600160a01b0316630e29df22846040518263ffffffff1660e01b81526004016000604051808303818588803b1580156119f657600080fd5b505af1158015611a0a573d6000803e3d6000fd5b5050505050505b6006546040805163255f504960e01b815290516000926001600160a01b03169163255f5049916004808301926020929190829003018186803b158015611a5657600080fd5b505afa158015611a6a573d6000803e3d6000fd5b505050506040513d6020811015611a8057600080fd5b5051905060015b818167ffffffffffffffff161015611d2d57600654604080516303c40b1f60e41b815267ffffffffffffffff8416600482015290516000926001600160a01b031691633c40b1f0916024808301926020929190829003018186803b158015611aee57600080fd5b505afa158015611b02573d6000803e3d6000fd5b505050506040513d6020811015611b1857600080fd5b50516006546040805163e3dee23960e01b81526001600160e01b03198416600482015290519293506000926001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b158015611b7557600080fd5b505afa158015611b89573d6000803e3d6000fd5b505050506040513d6020811015611b9f57600080fd5b5051604080516370a0823160e01b815230600482015290519196508692506000916001600160a01b038416916370a08231916024808301926020929190829003018186803b158015611bf057600080fd5b505afa158015611c04573d6000803e3d6000fd5b505050506040513d6020811015611c1a57600080fd5b50511115611d2357604080516370a0823160e01b815230600482015290516001600160a01b0387169163a9059cbb918a9184916370a0823191602480820192602092909190829003018186803b158015611c7357600080fd5b505afa158015611c87573d6000803e3d6000fd5b505050506040513d6020811015611c9d57600080fd5b5051604080516001600160e01b031960e086901b1681526001600160a01b03909316600484015260248301919091525160448083019260209291908290030181600087803b158015611cee57600080fd5b505af1158015611d02573d6000803e3d6000fd5b505050506040513d6020811015611d1857600080fd5b5051611d2357600080fd5b5050600101611a87565b5050600a805460ff60a01b19169055505050565b6001546001600160a01b031681565b611d598161183b565b8015611dde575060075460408051634b2cc0a160e01b81526004810184905290516003926001600160a01b031691634b2cc0a1916024808301926020929190829003018186803b158015611dac57600080fd5b505afa158015611dc0573d6000803e3d6000fd5b505050506040513d6020811015611dd657600080fd5b505160ff1614155b611de757600080fd5b6002546040805163217d488160e21b81526004810184905290516001600160a01b03909216916385f522049160248082019260009290919082900301818387803b158015611e3457600080fd5b505af1158015611e48573d6000803e3d6000fd5b50506007546040805163df09fea760e01b81526004810186905290516000945084935083926001600160a01b03169163df09fea79160248083019260c0929190829003018186803b158015611e9c57600080fd5b505afa158015611eb0573d6000803e3d6000fd5b505050506040513d60c0811015611ec657600080fd5b506040810151606082015160809092015191945092509050600160075460408051634b2cc0a160e01b81526004810188905290516001600160a01b0390921691634b2cc0a191602480820192602092909190829003018186803b158015611f2c57600080fd5b505afa158015611f40573d6000803e3d6000fd5b505050506040513d6020811015611f5657600080fd5b505160ff1614611f6a57611f6a84826122ef565b6007546040805163570e2f6560e11b8152600481018790526003602482015290516001600160a01b039092169163ae1c5eca9160448082019260009290919082900301818387803b158015611fbe57600080fd5b505af1158015610e1b573d6000803e3d6000fd5b6003546001600160a01b031681565b4286600381518110611fef57fe5b60200260200101511161200157600080fd5b60075486516001600160a01b039091169063922fd67f908890600490811061202557fe5b60200260200101516040518263ffffffff1660e01b81526004018082815260200191505060206040518083038186803b15801561206157600080fd5b505afa158015612075573d6000803e3d6000fd5b505050506040513d602081101561208b57600080fd5b50511561209757600080fd5b60075486516001600160a01b03909116906383fa958590889060049081106120bb57fe5b60200260200101516040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156120f957600080fd5b505af115801561210d573d6000803e3d6000fd5b50505050601e8561ffff161015801561212c575061016d8561ffff1611155b6121675760405162461bcd60e51b81526004018080602001828103825260258152602001806139436025913960400191505060405180910390fd5b600a546040805163e3dee23960e01b81526001600160e01b03198a16600482015290516000926001600160a01b03169163e3dee239916024808301926020929190829003018186803b1580156121bc57600080fd5b505afa1580156121d0573d6000803e3d6000fd5b505050506040513d60208110156121e657600080fd5b505190506208aa8960eb1b6001600160e01b0319891614801590612208575081155b156122be5760055487516001600160a01b039091169063d311afb49083908d908b90600190811061223557fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160a01b03166001600160a01b031681526020018281526020019350505050600060405180830381600087803b1580156122a557600080fd5b505af11580156122b9573d6000803e3d6000fd5b505050505b6122cd87878a8c898989610e34565b6122d657600080fd5b6122e38a8a8a8a8a61325d565b50505050505050505050565b6000809054906101000a90046001600160a01b03166001600160a01b031663ff0938a76040518163ffffffff1660e01b815260040160206040518083038186803b15801561233c57600080fd5b505afa158015612350573d6000803e3d6000fd5b505050506040513d602081101561236657600080fd5b50511561237257600080fd5b6007546040805163df09fea760e01b815260048101859052905160009283926001600160a01b039091169163df09fea79160248082019260c092909190829003018186803b1580156123c357600080fd5b505afa1580156123d7573d6000803e3d6000fd5b505050506040513d60c08110156123ed57600080fd5b5060408082015160609092015160075482516360a230e760e01b81526001600160e01b0319831660048201526024810188905292519395509093506001600160a01b0316916360a230e79160448082019260009290919082900301818387803b15801561245957600080fd5b505af115801561246d573d6000803e3d6000fd5b5050600754604080516340ebb05760e11b81526001600160a01b0387811660048301526001600160e01b0319871660248301526044820189905291519190921693506381d760ae9250606480830192600092919082900301818387803b158015611fbe57600080fd5b600061251883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250613756565b90505b92915050565b6007546040805163c60781bd60e01b81526001600160a01b03848116600483015291516000936125af9360019391169163c60781bd91602480820192602092909190829003018186803b15801561257757600080fd5b505afa15801561258b573d6000803e3d6000fd5b505050506040513d60208110156125a157600080fd5b50519063ffffffff6124d616565b6007546040805163785e811160e11b81526001600160a01b038681166004830152602482018590529151939450600093919092169163f0bd0222916044808301926020929190829003018186803b15801561260957600080fd5b505afa15801561261d573d6000803e3d6000fd5b505050506040513d602081101561263357600080fd5b505160408051600480825260a082019092529192506000918291829182916060919060208201608080388339505060075460408051633310490760e21b8152600481018b905290519394506000936001600160a01b03909216925063cc41241c9160248083019286929190829003018186803b1580156126b257600080fd5b505afa1580156126c6573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405260608110156126ef57600080fd5b81516020830151604080850180519151939592948301929184600160201b82111561271957600080fd5b90830190602082018581111561272e57600080fd5b82518660208202830111600160201b8211171561274a57600080fd5b82525081516020918201928201910280838360005b8381101561277757818101518382015260200161275f565b505050509050016040525050509091508093508197505050600760009054906101000a90046001600160a01b03166001600160a01b0316633f77ea65886040518263ffffffff1660e01b81526004018082815260200191505060806040518083038186803b1580156127e857600080fd5b505afa1580156127fc573d6000803e3d6000fd5b505050506040513d608081101561281257600080fd5b506020808201516040808401516060909401516007548251636f3adaf560e11b81526001600160a01b038d811660048301529351949b50959950909750169263de75b5ea926024808301939192829003018186803b15801561287357600080fd5b505afa158015612887573d6000803e3d6000fd5b505050506040513d602081101561289d57600080fd5b50516128a857600080fd5b60075460408051633d8ea1f560e21b81526001600160a01b038981166004830152600060248301819052925193169263f63a87d49260448084019391929182900301818387803b1580156128fb57600080fd5b505af115801561290f573d6000803e3d6000fd5b506001925061291c915050565b6007546040805163baaa46f960e01b8152600481018b905290516001600160a01b039092169163baaa46f991602480820192602092909190829003018186803b15801561296857600080fd5b505afa15801561297c573d6000803e3d6000fd5b505050506040513d602081101561299257600080fd5b50511461299e57600080fd5b6000600460009054906101000a90046001600160a01b03166001600160a01b0316636eeeaaa56040518163ffffffff1660e01b815260040160206040518083038186803b1580156129ee57600080fd5b505afa158015612a02573d6000803e3d6000fd5b505050506040513d6020811015612a1857600080fd5b505190508a15612fde57600954604080516307569f3960e11b81526001600160a01b038a8116600483015291519190921691630ead3e7291849160248082019260009290919082900301818588803b158015612a7357600080fd5b505af1158015612a87573d6000803e3d6000fd5b50505050504283600381518110612a9a57fe5b60200260200101511115612d7a576007546001600160a01b0316636e13d8c58960026040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b158015612af957600080fd5b505af1158015612b0d573d6000803e3d6000fd5b50505050846001600160e01b0319166208aa8960eb1b1415612bda5760055483516000916001600160a01b03169085906001908110612b4857fe5b6020908102919091010151604051600081818185875af1925050503d8060008114612b8f576040519150601f19603f3d011682016040523d82523d6000602084013e612b94565b606091505b5050905080612bd45760405162461bcd60e51b81526004018080602001828103825260288152602001806139986028913960400191505060405180910390fd5b50612d12565b6006546040805163e3dee23960e01b81526001600160e01b03198816600482015290516001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b158015612c2f57600080fd5b505afa158015612c43573d6000803e3d6000fd5b505050506040513d6020811015612c5957600080fd5b505160055484519193506001600160a01b038085169263a9059cbb929091169086906001908110612c8657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612cdd57600080fd5b505af1158015612cf1573d6000803e3d6000fd5b505050506040513d6020811015612d0757600080fd5b5051612d1257600080fd5b604080518981526912d650c814185cdcd95960b21b602082015281518d1515926001600160a01b038b16927fb9a54f82713d4f0f38515b9ab47189872914a2187d882fce60b1a21519224797929081900390910190a3612d75878787868861325d565b612fd9565b6007546001600160a01b0316636e13d8c58960046040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b158015612dcb57600080fd5b505af1158015612ddf573d6000803e3d6000fd5b50505050846001600160e01b0319166208aa8960eb1b1415612e4e57866001600160a01b03166108fc84600181518110612e1557fe5b60200260200101519081150290604051600060405180830381858888f19350505050158015612e48573d6000803e3d6000fd5b50612f80565b6006546040805163e3dee23960e01b81526001600160e01b03198816600482015290516001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b158015612ea357600080fd5b505afa158015612eb7573d6000803e3d6000fd5b505050506040513d6020811015612ecd57600080fd5b505183519092506001600160a01b0383169063a9059cbb90899086906001908110612ef457fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612f4b57600080fd5b505af1158015612f5f573d6000803e3d6000fd5b505050506040513d6020811015612f7557600080fd5b5051612f8057600080fd5b604080518981526b10dbdd995c8811985a5b195960a21b602082015281518d1515926001600160a01b038b16927fb9a54f82713d4f0f38515b9ab47189872914a2187d882fce60b1a21519224797929081900390910190a35b613250565b6007546001600160a01b0316636e13d8c58960036040518363ffffffff1660e01b81526004018083815260200182815260200192505050600060405180830381600087803b15801561302f57600080fd5b505af1158015613043573d6000803e3d6000fd5b5083925050506208aa8960eb1b6001600160e01b03198716141561308f57613088828560018151811061307257fe5b60200260200101516137ed90919063ffffffff16565b90506131c1565b6006546040805163e3dee23960e01b81526001600160e01b03198916600482015290516001600160a01b039092169163e3dee23991602480820192602092909190829003018186803b1580156130e457600080fd5b505afa1580156130f8573d6000803e3d6000fd5b505050506040513d602081101561310e57600080fd5b505184519093506001600160a01b0384169063a9059cbb908a908790600190811061313557fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050602060405180830381600087803b15801561318c57600080fd5b505af11580156131a0573d6000803e3d6000fd5b505050506040513d60208110156131b657600080fd5b50516131c157600080fd5b6040516001600160a01b0389169082156108fc029083906000818181858888f193505050501580156131f7573d6000803e3d6000fd5b50604080518a81526912d650c811985a5b195960b21b602082015281518e1515926001600160a01b038c16927fb9a54f82713d4f0f38515b9ab47189872914a2187d882fce60b1a21519224797929081900390910190a3505b5050505050505050505050565b6007546040805163067157ff60e41b815290516000926001600160a01b0316916367157ff0916004808301926020929190829003018186803b1580156132a257600080fd5b505afa1580156132b6573d6000803e3d6000fd5b505050506040513d60208110156132cc57600080fd5b505160075484519192506001600160a01b031690638ce80a6590849086906000906132f357fe5b602002602001015189888a8960018151811061330b57fe5b60200260200101518a60028151811061332057fe5b60200260200101516040518863ffffffff1660e01b8152600401808861ffff1661ffff168152602001878152602001866001600160a01b03166001600160a01b03168152602001856001600160e01b0319166001600160e01b0319168152602001846001600160a01b03166001600160a01b03168152602001838152602001828152602001975050505050505050600060405180830381600087803b1580156133c857600080fd5b505af11580156133dc573d6000803e3d6000fd5b5050505060006134956064613489600760009054906101000a90046001600160a01b03166001600160a01b031663f3ab26cc6040518163ffffffff1660e01b815260040160206040518083038186803b15801561343857600080fd5b505afa15801561344c573d6000803e3d6000fd5b505050506040513d602081101561346257600080fd5b505187518890600290811061347357fe5b602002602001015161384790919063ffffffff16565b9063ffffffff6138a016565b600354604080516340c10f1960e01b81526001600160a01b038b811660048301526024820185905291519394509116916340c10f199160448082019260009290919082900301818387803b1580156134ec57600080fd5b505af1158015613500573d6000803e3d6000fd5b50506002546040805163f075be1f60e01b81526004810186905261ffff88166024820152604481018790526001600160a01b038c81166064830152915191909216935063f075be1f9250608480830192600092919082900301818387803b15801561356a57600080fd5b505af115801561357e573d6000803e3d6000fd5b505060075486516001600160a01b03909116925063a2c6bc259150879087906000906135a657fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160e01b0319166001600160e01b031916815260200182815260200192505050600060405180830381600087803b1580156135ff57600080fd5b505af1158015613613573d6000803e3d6000fd5b505060075486516001600160a01b039091169250633994b3d5915088908890889060009061363d57fe5b60200260200101516040518463ffffffff1660e01b815260040180846001600160a01b03166001600160a01b03168152602001836001600160e01b0319166001600160e01b03191681526020018281526020019350505050600060405180830381600087803b1580156136af57600080fd5b505af11580156136c3573d6000803e3d6000fd5b50506002805487516001600160a01b03909116935063899ec98f92508991889181106136eb57fe5b60200260200101516040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561374257600080fd5b505af1158015613250573d6000803e3d6000fd5b600081848411156137e55760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156137aa578181015183820152602001613792565b50505050905090810190601f1680156137d75780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b600082820183811015612518576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6000826138565750600061251b565b8282028284828161386357fe5b04146125185760405162461bcd60e51b81526004018080602001828103825260218152602001806139c06021913960400191505060405180910390fd5b600061251883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506000818361392c5760405162461bcd60e51b81526020600482018181528351602484015283519092839260449091019190850190808383600083156137aa578181015183820152602001613792565b50600083858161393857fe5b049594505050505056fe51756f746174696f6e3a20436f76657220706572696f64206f7574206f6620626f756e647343616e206e6f742062757920636f76657220756e74696c20636170207265616368656420666f72203173742074696d6551756f746174696f6e3a206574686572207472616e7366657220746f20706f6f6c206661696c6564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77a265627a7a72315820c7418180bc393df1bb830b0032c14935978a2c7abe4111441deb74f4739dbfc464736f6c63430005110032
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.