diff --git a/contracts/events/ProtocolSettingsEvents.sol b/contracts/events/ProtocolSettingsEvents.sol
index 216548667..9b59133a6 100644
--- a/contracts/events/ProtocolSettingsEvents.sol
+++ b/contracts/events/ProtocolSettingsEvents.sol
@@ -83,7 +83,7 @@ contract ProtocolSettingsEvents is ModulesCommonEvents {
uint256 lendingAmount,
uint256 tradingAmount,
uint256 borrowingAmount,
- uint256 wRBTCConverted
+ uint256 wrappedNativeTokenConverted
);
event WithdrawLendingFees(
diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol
index 72639273a..482261cb1 100644
--- a/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol
+++ b/contracts/governance/FeeSharingCollector/FeeSharingCollector.sol
@@ -42,8 +42,8 @@ import "../../interfaces/IConverterAMM.sol";
*
* The protocol initially collects fees in all tokens.
* Then the FeeSharingCollector wihtdraws fees from the protocol.
- * When the fees are withdrawn all the tokens except SOV will be converted to wRBTC
- * and then transferred to wRBTC loan pool.
+ * When the fees are withdrawn all the tokens except SOV will be converted to wrappedNativeToken
+ * and then transferred to wrappedNativeToken loan pool.
* For SOV, it will be directly deposited into the feeSharingCollector from the protocol.
* */
contract FeeSharingCollector is
@@ -56,14 +56,15 @@ contract FeeSharingCollector is
using SafeERC20 for IERC20;
address constant ZERO_ADDRESS = address(0);
+ /** To support backward compatibility, we need to keep this constant variable name as it is (which is derived from rsk network) */
address public constant RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT =
address(uint160(uint256(keccak256("RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT"))));
/* Events */
- /// @notice Deprecated event after the unification between wrbtc & rbtc
+ /// @notice Deprecated event after the unification between wrappedNativeToken & nativeToken
// event FeeWithdrawn(address indexed sender, address indexed token, uint256 amount);
- event FeeWithdrawnInRBTC(address indexed sender, uint256 amount);
+ event FeeWithdrawnInNativeToken(address indexed sender, uint256 amount);
/// @notice An event emitted when tokens transferred.
event TokensTransferred(address indexed sender, address indexed token, uint256 amount);
@@ -92,7 +93,7 @@ contract FeeSharingCollector is
*
* @param sender sender who initiate the withdrawn amm fees.
* @param converter the converter address.
- * @param amount total amount of fee (Already converted to WRBTC).
+ * @param amount total amount of fee (Already converted to wrappedNativeToken).
*/
event FeeAMMWithdrawn(address indexed sender, address indexed converter, uint256 amount);
@@ -102,65 +103,62 @@ contract FeeSharingCollector is
/// @notice An event emitted when converter address has been removed from whitelist.
event UnwhitelistedConverter(address indexed sender, address converter);
- event RBTCWithdrawn(address indexed sender, address indexed receiver, uint256 amount);
+ event NativeTokenWithdrawn(address indexed sender, address indexed receiver, uint256 amount);
- event SetWrbtcToken(
+ event SetWrappedNativeToken(
address indexed sender,
- address indexed oldWrbtcToken,
- address indexed newWrbtcToken
+ address indexed oldWrappedNativeToken,
+ address indexed newWrappedNativeToken
);
- event SetLoanTokenWrbtc(
+ event SetLoanWrappedNativeToken(
address indexed sender,
- address indexed oldLoanTokenWrbtc,
- address indexed newLoanTokenWrbtc
+ address indexed oldLoanWrappedNativeToken,
+ address indexed newLoanWrappedNativeToken
);
event SetProtocolAddress(address indexed sender, address _protocolAddress);
- /* Modifier */
- modifier oneTimeExecution(bytes4 _funcSig) {
- require(
- !isFunctionExecuted[_funcSig],
- "FeeSharingCollector: function can only be called once"
- );
- _;
- isFunctionExecuted[_funcSig] = true;
- }
-
/* Functions */
- /// @dev fallback function to support rbtc transfer when unwrap the wrbtc.
+ /// @dev fallback function to support nativeToken transfer when unwrap the wrappedNativeToken.
function() external payable {}
/**
* @dev initialize function for fee sharing collector proxy
- * @param wrbtcToken wrbtc token address
- * @param loanWrbtcToken address of loan token wrbtc (IWrbtc)
+ * @param wrappedNativeToken wrappedNativeToken address
+ * @param loanWrappedNativeToken address of loan token wrappedNativeToken (IWrappedNativeToken)
*/
function initialize(
- address wrbtcToken,
- address loanWrbtcToken
+ address wrappedNativeToken,
+ address loanWrappedNativeToken
) external onlyOwner oneTimeExecution(this.initialize.selector) {
require(
- wrbtcTokenAddress == address(0) && loanTokenWrbtcAddress == address(0),
- "wrbtcToken or loanWrbtcToken has been initialized"
+ wrappedNativeTokenAddress == address(0) && loanWrappedNativeTokenAddress == address(0),
+ "wrappedNativeToken or loanWrappedNativeToken has been initialized"
);
- setWrbtcToken(wrbtcToken);
- setLoanTokenWrbtc(loanWrbtcToken);
+ setWrappedNativeToken(wrappedNativeToken);
+ setLoanWrappedNativeToken(loanWrappedNativeToken);
}
/**
- * @notice Set the wrbtc token address of fee sharing collector.
+ * @notice Set the wrappedNativeToken address of fee sharing collector.
*
* only owner can perform this action.
*
- * @param newWrbtcTokenAddress The new address of the wrbtc token.
+ * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken.
* */
- function setWrbtcToken(address newWrbtcTokenAddress) public onlyOwner {
- require(Address.isContract(newWrbtcTokenAddress), "newWrbtcTokenAddress not a contract");
- emit SetWrbtcToken(msg.sender, wrbtcTokenAddress, newWrbtcTokenAddress);
- wrbtcTokenAddress = newWrbtcTokenAddress;
+ function setWrappedNativeToken(address newWrappedNativeTokenAddress) public onlyOwner {
+ require(
+ Address.isContract(newWrappedNativeTokenAddress),
+ "newWrappedNativeTokenAddress not a contract"
+ );
+ emit SetWrappedNativeToken(
+ msg.sender,
+ wrappedNativeTokenAddress,
+ newWrappedNativeTokenAddress
+ );
+ wrappedNativeTokenAddress = newWrappedNativeTokenAddress;
}
/**
@@ -180,25 +178,29 @@ contract FeeSharingCollector is
}
/**
- * @notice Set the loan wrbtc token address of fee sharing collector.
+ * @notice Set the loan wrappedNativeToken address of fee sharing collector.
*
* only owner can perform this action.
*
- * @param newLoanTokenWrbtcAddress The new address of the loan wrbtc token.
+ * @param newLoanWrappedNativeTokenAddress The new address of the loan wrappedNativeToken.
* */
- function setLoanTokenWrbtc(address newLoanTokenWrbtcAddress) public onlyOwner {
+ function setLoanWrappedNativeToken(address newLoanWrappedNativeTokenAddress) public onlyOwner {
require(
- Address.isContract(newLoanTokenWrbtcAddress),
- "newLoanTokenWrbtcAddress not a contract"
+ Address.isContract(newLoanWrappedNativeTokenAddress),
+ "newLoanWrappedNativeTokenAddress not a contract"
);
- emit SetLoanTokenWrbtc(msg.sender, loanTokenWrbtcAddress, newLoanTokenWrbtcAddress);
- loanTokenWrbtcAddress = newLoanTokenWrbtcAddress;
+ emit SetLoanWrappedNativeToken(
+ msg.sender,
+ loanWrappedNativeTokenAddress,
+ newLoanWrappedNativeTokenAddress
+ );
+ loanWrappedNativeTokenAddress = newLoanWrappedNativeTokenAddress;
}
/**
* @notice Withdraw fees for the given token:
* lendingFee + tradingFee + borrowingFee
- * the fees (except SOV) will be converted in wRBTC form, and then will be transferred to wRBTC loan pool.
+ * the fees (except SOV) will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool.
* For SOV, it will be directly deposited into the feeSharingCollector from the protocol.
*
* @param _tokens array address of the token
@@ -211,66 +213,74 @@ contract FeeSharingCollector is
);
}
- uint256 wrbtcAmountWithdrawn = protocol.withdrawFees(_tokens, address(this));
+ uint256 wrappedNativeTokenAmountWithdrawn = protocol.withdrawFees(_tokens, address(this));
- IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);
+ IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20(
+ wrappedNativeTokenAddress
+ );
- if (wrbtcAmountWithdrawn > 0) {
- // unwrap the wrbtc to rbtc, and hold the rbtc.
- wrbtcToken.withdraw(wrbtcAmountWithdrawn);
+ if (wrappedNativeTokenAmountWithdrawn > 0) {
+ // unwrap the wrappedNativeToken to nativeToken, and hold the nativeToken.
+ wrappedNativeToken.withdraw(wrappedNativeTokenAmountWithdrawn);
/// @notice Update unprocessed amount of tokens
uint96 amount96 = safe96(
- wrbtcAmountWithdrawn,
- "FeeSharingCollector::withdrawFees: wrbtc token amount exceeds 96 bits"
+ wrappedNativeTokenAmountWithdrawn,
+ "FeeSharingCollector::withdrawFees: wrappedNativeToken amount exceeds 96 bits"
);
_addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, amount96);
}
- // note deprecated event since we unify the wrbtc & rbtc
+ // note deprecated event since we unify the wrappedNativeToken & nativeToken
// emit FeeWithdrawn(msg.sender, RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, poolTokenAmount);
// note new emitted event
- emit FeeWithdrawnInRBTC(msg.sender, wrbtcAmountWithdrawn);
+ emit FeeWithdrawnInNativeToken(msg.sender, wrappedNativeTokenAmountWithdrawn);
}
/**
* @notice Withdraw amm fees for the given converter addresses:
* protocolFee from the conversion
- * the fees will be converted in wRBTC form, and then will be transferred to wRBTC loan pool
+ * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool
*
* @param _converters array addresses of the converters
* */
function withdrawFeesAMM(address[] memory _converters) public {
- IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);
+ IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20(
+ wrappedNativeTokenAddress
+ );
// Validate
_validateWhitelistedConverter(_converters);
uint96 totalPoolTokenAmount;
for (uint256 i = 0; i < _converters.length; i++) {
- uint256 wrbtcAmountWithdrawn = IConverterAMM(_converters[i]).withdrawFees(
+ uint256 wrappedNativeTokenAmountWithdrawn = IConverterAMM(_converters[i]).withdrawFees(
address(this)
);
- if (wrbtcAmountWithdrawn > 0) {
- // unwrap wrbtc to rbtc, and hold the rbtc
- wrbtcToken.withdraw(wrbtcAmountWithdrawn);
+ if (wrappedNativeTokenAmountWithdrawn > 0) {
+ // unwrap wrappedNativeToken to nativeToken, and hold the nativeToken
+ wrappedNativeToken.withdraw(wrappedNativeTokenAmountWithdrawn);
/// @notice Update unprocessed amount of tokens
uint96 amount96 = safe96(
- wrbtcAmountWithdrawn,
- "FeeSharingCollector::withdrawFeesAMM: wrbtc token amount exceeds 96 bits"
+ wrappedNativeTokenAmountWithdrawn,
+ "FeeSharingCollector::withdrawFeesAMM: wrappedNativeToken amount exceeds 96 bits"
);
totalPoolTokenAmount = add96(
totalPoolTokenAmount,
amount96,
- "FeeSharingCollector::withdrawFeesAMM: total wrbtc token amount exceeds 96 bits"
+ "FeeSharingCollector::withdrawFeesAMM: total wrappedNativeToken amount exceeds 96 bits"
);
- emit FeeAMMWithdrawn(msg.sender, _converters[i], wrbtcAmountWithdrawn);
+ emit FeeAMMWithdrawn(
+ msg.sender,
+ _converters[i],
+ wrappedNativeTokenAmountWithdrawn
+ );
}
}
@@ -294,10 +304,12 @@ contract FeeSharingCollector is
bool success = IERC20(_token).transferFrom(address(msg.sender), address(this), _amount);
require(success, "Staking::transferTokens: token transfer failed");
- // if _token is wrbtc, need to unwrap it to rbtc
- IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);
- if (_token == address(wrbtcToken)) {
- wrbtcToken.withdraw(_amount);
+ // if _token is wrappedNativeToken, need to unwrap it to nativeToken
+ IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20(
+ wrappedNativeTokenAddress
+ );
+ if (_token == address(wrappedNativeToken)) {
+ wrappedNativeToken.withdraw(_amount);
_token = RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT;
}
@@ -307,13 +319,13 @@ contract FeeSharingCollector is
}
/**
- * @notice Transfer RBTC / native tokens to this contract.
- * @dev We just write checkpoint here (based on the rbtc value that is sent) in a separate methods
+ * @notice Transfer NativeToken / native tokens to this contract.
+ * @dev We just write checkpoint here (based on the nativeToken value that is sent) in a separate methods
* in order to prevent adding checkpoints too often.
* */
- function transferRBTC() external payable {
+ function transferNativeToken() external payable {
uint96 _amount = uint96(msg.value);
- require(_amount > 0, "FeeSharingCollector::transferRBTC: invalid value");
+ require(_amount > 0, "FeeSharingCollector::transferNativeToken: invalid value");
_addCheckpoint(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, _amount);
@@ -381,9 +393,9 @@ contract FeeSharingCollector is
}
processedCheckpoints[user][_token] = end;
- if (loanTokenWrbtcAddress == _token) {
- // We will change, so that feeSharingCollector will directly burn then loanToken (IWRBTC) to rbtc and send to the user --- by call burnToBTC function
- ILoanTokenWRBTC(_token).burnToBTC(_receiver, amount, false);
+ if (loanWrappedNativeTokenAddress == _token) {
+ // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function which is burning to a native token - to be renamed to burnedToNativeToken
+ ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false);
} else {
// Previously it directly send the loanToken to the user
require(
@@ -408,7 +420,7 @@ contract FeeSharingCollector is
*
* This function will directly burnToBTC and use the msg.sender (user) as the receiver
*
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @param _maxCheckpoints Maximum number of checkpoints to be processed. Must be positive value.
* @param _receiver The receiver of tokens or msg.sender
* */
@@ -466,15 +478,15 @@ contract FeeSharingCollector is
}
}
- function validRBTCBasedTokens(address[] memory _tokens) private view {
+ function validNativeTokenBasedTokens(address[] memory _tokens) private view {
for (uint256 i = 0; i < _tokens.length; i++) {
address _token = _tokens[i];
if (
_token != RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT &&
- _token != wrbtcTokenAddress &&
- _token != loanTokenWrbtcAddress
+ _token != wrappedNativeTokenAddress &&
+ _token != loanWrappedNativeTokenAddress
) {
- revert("only rbtc-based tokens are allowed");
+ revert("only native token based tokens are allowed");
}
}
}
@@ -509,7 +521,7 @@ contract FeeSharingCollector is
_receiver = msg.sender;
}
- uint256 rbtcAmountToSend;
+ uint256 nativeTokenAmountToSend;
for (uint256 i = 0; i < _tokens.length; i++) {
TokenWithSkippedCheckpointsWithdraw memory tokenData = _tokens[i];
@@ -526,17 +538,17 @@ contract FeeSharingCollector is
: previousProcessedUserCheckpoints;
if (
- tokenData.tokenAddress == wrbtcTokenAddress ||
- tokenData.tokenAddress == loanTokenWrbtcAddress ||
+ tokenData.tokenAddress == wrappedNativeTokenAddress ||
+ tokenData.tokenAddress == loanWrappedNativeTokenAddress ||
tokenData.tokenAddress == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
) {
- (totalAmount, endToken) = _withdrawRbtcTokenStartingFromCheckpoint(
+ (totalAmount, endToken) = _withdrawNativeTokenStartingFromCheckpoint(
tokenData.tokenAddress,
tokenData.fromCheckpoint,
_maxCheckpoints,
_receiver
);
- rbtcAmountToSend = rbtcAmountToSend.add(totalAmount);
+ nativeTokenAmountToSend = nativeTokenAmountToSend.add(totalAmount);
} else {
(, endToken) = _withdrawStartingFromCheckpoint(
tokenData.tokenAddress,
@@ -554,38 +566,38 @@ contract FeeSharingCollector is
);
}
- if (rbtcAmountToSend > 0) {
- // send all rbtc withdrawal
- (bool success, ) = _receiver.call.value(rbtcAmountToSend)("");
- require(success, "FeeSharingCollector::withdrawRBTC: Withdrawal failed");
+ if (nativeTokenAmountToSend > 0) {
+ // send all native token withdrawal
+ (bool success, ) = _receiver.call.value(nativeTokenAmountToSend)("");
+ require(success, "FeeSharingCollector: Withdrawal failed");
- emit RBTCWithdrawn(msg.sender, _receiver, rbtcAmountToSend);
+ emit NativeTokenWithdrawn(msg.sender, _receiver, nativeTokenAmountToSend);
}
}
/**
* @dev Function to wrap:
- * 1. regular withdrawal for both rbtc & non-rbtc token
- * 2. skipped checkpoints withdrawal for both rbtc & non-rbtc token
+ * 1. regular withdrawal for both native & non native token
+ * 2. skipped checkpoints withdrawal for both native token & non native token
*
- * @param _nonRbtcTokensRegularWithdraw array of non-rbtc token address with no skipped checkpoints that will be withdrawn
- * @param _rbtcTokensRegularWithdraw array of rbtc token address with no skipped checkpoints that will be withdrawn
- * @param _tokensWithSkippedCheckpoints array of rbtc & non-rbtc TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn
+ * @param _nonNativeTokensRegularWithdraw array of non native token address with no skipped checkpoints that will be withdrawn
+ * @param _nativeTokensRegularWithdraw array of native token address with no skipped checkpoints that will be withdrawn
+ * @param _tokensWithSkippedCheckpoints array of native token & non native token TokenWithSkippedCheckpointsWithdraw struct, which has skipped checkpoints that will be withdrawn
*
*/
function claimAllCollectedFees(
- address[] calldata _nonRbtcTokensRegularWithdraw,
- address[] calldata _rbtcTokensRegularWithdraw,
+ address[] calldata _nonNativeTokensRegularWithdraw,
+ address[] calldata _nativeTokensRegularWithdraw,
TokenWithSkippedCheckpointsWithdraw[] calldata _tokensWithSkippedCheckpoints,
uint32 _maxCheckpoints,
address _receiver
) external nonReentrant {
uint256 totalProcessedCheckpoints;
- /** Process normal multiple withdrawal for RBTC based tokens */
- if (_rbtcTokensRegularWithdraw.length > 0) {
- totalProcessedCheckpoints = _withdrawRbtcTokens(
- _rbtcTokensRegularWithdraw,
+ /** Process normal multiple withdrawal for NativeToken based tokens */
+ if (_nativeTokensRegularWithdraw.length > 0) {
+ totalProcessedCheckpoints = _withdrawNativeTokens(
+ _nativeTokensRegularWithdraw,
_maxCheckpoints,
_receiver
);
@@ -595,17 +607,17 @@ contract FeeSharingCollector is
);
}
- /** Process normal non-rbtc token withdrawal */
- for (uint256 i = 0; i < _nonRbtcTokensRegularWithdraw.length; i++) {
+ /** Process normal non native token withdrawal */
+ for (uint256 i = 0; i < _nonNativeTokensRegularWithdraw.length; i++) {
if (_maxCheckpoints == 0) break;
uint256 endTokenCheckpoint;
- address _nonRbtcTokenAddress = _nonRbtcTokensRegularWithdraw[i];
+ address _nonNativeTokenAddress = _nonNativeTokensRegularWithdraw[i];
/** starting checkpoint is the previous processedCheckpoints for token */
- uint256 startingCheckpoint = processedCheckpoints[msg.sender][_nonRbtcTokenAddress];
+ uint256 startingCheckpoint = processedCheckpoints[msg.sender][_nonNativeTokenAddress];
- (, endTokenCheckpoint) = _withdraw(_nonRbtcTokenAddress, _maxCheckpoints, _receiver);
+ (, endTokenCheckpoint) = _withdraw(_nonNativeTokenAddress, _maxCheckpoints, _receiver);
uint256 _previousUsedCheckpoint = endTokenCheckpoint.sub(startingCheckpoint);
if (startingCheckpoint > 0) {
@@ -647,25 +659,27 @@ contract FeeSharingCollector is
(totalAmount, endTokenCheckpoint) = _withdraw(_token, _maxCheckpoints, _receiver);
}
- function _withdrawRbtcToken(
+ function _withdrawNativeToken(
address _token,
uint32 _maxCheckpoints
) internal returns (uint256 totalAmount, uint256 endTokenCheckpoint) {
address user = msg.sender;
- IWrbtcERC20 wrbtcToken = IWrbtcERC20(wrbtcTokenAddress);
+ IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20(
+ wrappedNativeTokenAddress
+ );
- (totalAmount, endTokenCheckpoint) = _getRBTCBalance(_token, user, _maxCheckpoints);
+ (totalAmount, endTokenCheckpoint) = _getNativeTokenBalance(_token, user, _maxCheckpoints);
if (totalAmount > 0) {
processedCheckpoints[user][_token] = endTokenCheckpoint;
- if (_token == address(wrbtcToken)) {
- // unwrap the wrbtc
- wrbtcToken.withdraw(totalAmount);
- } else if (_token == loanTokenWrbtcAddress) {
- // pull out the iWRBTC to rbtc to this feeSharingCollector contract
- /** @dev will use the burned result from IWRBTC to RBTC as return total amount */
- totalAmount = ILoanTokenWRBTC(loanTokenWrbtcAddress).burnToBTC(
+ if (_token == address(wrappedNativeToken)) {
+ // unwrap the wrappedNativeToken
+ wrappedNativeToken.withdraw(totalAmount);
+ } else if (_token == loanWrappedNativeTokenAddress) {
+ // pull out the iWrappedNativeToken to nativeToken to this feeSharingCollector contract
+ /** @dev will use the burned result from IWrappedNativeToken to NativeToken as return total amount */
+ totalAmount = ILoanWrappedNativeToken(loanWrappedNativeTokenAddress).burnToBTC(
address(this),
totalAmount,
false
@@ -675,41 +689,41 @@ contract FeeSharingCollector is
}
/**
- * @dev withdraw all of the RBTC balance based on particular checkpoints
+ * @dev withdraw all of the NativeToken balance based on particular checkpoints
*
- * This function will withdraw RBTC balance which is passed as _token param, so it could be either of these:
- * - rbtc balance or
- * - wrbtc balance which will be unwrapped to rbtc or
- * - iwrbtc balance which will be unwrapped to rbtc or
+ * This function will withdraw NativeToken balance which is passed as _token param, so it could be either of these:
+ * - nativeToken balance or
+ * - wrappedNativeToken balance which will be unwrapped to nativeToken or
+ * - iWrappedNativeToken balance which will be unwrapped to nativeToken or
*
*
- * @param _tokens array of either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrbtc address or iwrbtc address
+ * @param _tokens array of either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address
* @param _maxCheckpoints Maximum number of checkpoints to be processed to workaround block gas limit
* @param _receiver An optional tokens receiver (msg.sender used if 0)
*/
- function _withdrawRbtcTokens(
+ function _withdrawNativeTokens(
address[] memory _tokens,
uint32 _maxCheckpoints,
address _receiver
) internal returns (uint256 totalProcessedCheckpoints) {
- validRBTCBasedTokens(_tokens);
+ validNativeTokenBasedTokens(_tokens);
if (_receiver == ZERO_ADDRESS) {
_receiver = msg.sender;
}
- uint256 rbtcAmountToSend;
+ uint256 nativeTokenAmountToSend;
for (uint256 i = 0; i < _tokens.length; i++) {
if (_maxCheckpoints == 0) break;
address _token = _tokens[i];
uint256 startingCheckpoint = processedCheckpoints[msg.sender][_token];
- (uint256 totalAmount, uint256 endToken) = _withdrawRbtcToken(
+ (uint256 totalAmount, uint256 endToken) = _withdrawNativeToken(
_tokens[i],
_maxCheckpoints
);
- rbtcAmountToSend = rbtcAmountToSend.add(totalAmount);
+ nativeTokenAmountToSend = nativeTokenAmountToSend.add(totalAmount);
uint256 _previousUsedCheckpoint = endToken.sub(startingCheckpoint);
if (startingCheckpoint > 0) {
@@ -723,20 +737,20 @@ contract FeeSharingCollector is
);
}
- // send all rbtc
- if (rbtcAmountToSend > 0) {
- (bool success, ) = _receiver.call.value(rbtcAmountToSend)("");
- require(success, "FeeSharingCollector::withdrawRBTC: Withdrawal failed");
+ // send all nativeToken
+ if (nativeTokenAmountToSend > 0) {
+ (bool success, ) = _receiver.call.value(nativeTokenAmountToSend)("");
+ require(success, "FeeSharingCollector::withdrawNativeToken: Withdrawal failed");
- emit RBTCWithdrawn(msg.sender, _receiver, rbtcAmountToSend);
+ emit NativeTokenWithdrawn(msg.sender, _receiver, nativeTokenAmountToSend);
}
}
/**
- * @dev Withdraw either specific RBTC related token balance or all RBTC related tokens balances.
- * RBTC related here means, it could be either rbtc, wrbtc, or iwrbtc, depends on the _token param.
+ * @dev Withdraw either specific NativeToken related token balance or all NativeToken related tokens balances.
+ * NativeToken related here means, it could be either nativeToken, wrappedNativeToken, or iWrappedNativeToken, depends on the _token param.
*/
- function _withdrawRbtcTokenStartingFromCheckpoint(
+ function _withdrawNativeTokenStartingFromCheckpoint(
address _token,
uint256 _fromCheckpoint,
uint32 _maxCheckpoints,
@@ -749,14 +763,14 @@ contract FeeSharingCollector is
if (prevFromCheckpoint > processedCheckpoints[msg.sender][_token]) {
processedCheckpoints[msg.sender][_token] = prevFromCheckpoint;
}
- return _withdrawRbtcToken(_token, _maxCheckpoints);
+ return _withdrawNativeToken(_token, _maxCheckpoints);
}
/**
* @dev Returns first user's checkpoint with weighted stake > 0
*
* @param _user The address of the user or contract.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @param _startFrom Checkpoint number to start from. If _startFrom < processedUserCheckpoints then starts from processedUserCheckpoints.
* @param _maxCheckpoints Max checkpoints to process in a row to avoid timeout error
* @return [checkpointNum: checkpoint number where user's weighted stake > 0, hasSkippedCheckpoints, hasFees]
@@ -774,7 +788,7 @@ contract FeeSharingCollector is
* @dev Returns first user's checkpoint with weighted stake > 0
*
* @param _user The address of the user or contract.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @param _startFrom Checkpoint number to start from. If _startFrom < processedUserCheckpoints then starts from processedUserCheckpoints.
* @param _maxCheckpoints Max checkpoints to process in a row to avoid timeout error
* @return [checkpointNum: checkpoint number where user's weighted stake > 0, hasSkippedCheckpoints, hasFees]
@@ -826,7 +840,7 @@ contract FeeSharingCollector is
/**
* @notice Get the accumulated loan pool fee of the message sender.
* @param _user The address of the user or contract.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @return The accumulated fee for the message sender.
* */
function getAccumulatedFees(address _user, address _token) public view returns (uint256) {
@@ -846,7 +860,7 @@ contract FeeSharingCollector is
* @dev This function is required to keep consistent with caching of weighted voting power when claiming fees
*
* @param _user The address of a user (staker) or contract.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @param _startFrom Checkpoint to start calculating fees from.
* @param _maxCheckpoints maxCheckpoints to get accumulated fees for the _user
* @return The accumulated fees rewards for the _user in the given checkpoints interval: [_startFrom, _startFrom + maxCheckpoints].
@@ -870,7 +884,7 @@ contract FeeSharingCollector is
* if there is no more fees, it will return empty array.
*
* @param _user The address of a user (staker) or contract.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @param _startFrom Checkpoint to start calculating fees from.
* @param _maxCheckpoints maxCheckpoints to get accumulated fees for the _user
* @return The next checkpoint num which is the starting point to fetch all of the fees, array of calculated fees.
@@ -909,7 +923,7 @@ contract FeeSharingCollector is
* @notice Gets accumulated fees for a user starting from a given checkpoint
*
* @param _user Address of the user's account.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of the pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of the pool token.
* @param _maxCheckpoints Max checkpoints to process at once to fit into block gas limit
* @param _startFrom Checkpoint num to start calculations from
*
@@ -972,7 +986,7 @@ contract FeeSharingCollector is
* they are not considered by the withdrawing logic (to avoid inconsistencies).
*
* @param _start Start of the range.
- * @param _token RBTC dummy to fit into existing data structure or SOV. Former address of a pool token.
+ * @param _token NativeToken dummy to fit into existing data structure or SOV. Former address of a pool token.
* @param _maxCheckpoints Checkpoint index incremental.
* */
function _getEndOfRange(
@@ -1104,142 +1118,115 @@ contract FeeSharingCollector is
}
}
- function withdrawWRBTC(address receiver, uint256 wrbtcAmount) external onlyOwner {
- IERC20 wrbtcToken = IERC20(wrbtcTokenAddress);
-
- uint256 balance = wrbtcToken.balanceOf(address(this));
- require(wrbtcAmount <= balance, "Insufficient balance");
-
- wrbtcToken.safeTransfer(receiver, wrbtcAmount);
- }
-
- /**
- * @dev This function is dedicated to recover the wrong fee allocation for the 4 year vesting contracts.
- * This function can only be called once
- * The affected tokens to be withdrawn
- * 1. RBTC
- * 2. ZUSD
- * 3. SOV
- * The amount for all of the tokens above is hardcoded
- * The withdrawn tokens will be sent to the owner.
- */
- function recoverIncorrectAllocatedFees()
- external
- oneTimeExecution(this.recoverIncorrectAllocatedFees.selector)
- onlyOwner
- {
- uint256 rbtcAmount = 878778886164898400;
- uint256 zusdAmount = 16658600400155126000000;
- uint256 sovAmount = 6275898259771202000000;
-
- address zusdToken = 0xdB107FA69E33f05180a4C2cE9c2E7CB481645C2d;
- address sovToken = 0xEFc78fc7d48b64958315949279Ba181c2114ABBd;
+ function withdrawWrappedNativeToken(
+ address receiver,
+ uint256 wrappedNativeTokenAmount
+ ) external onlyOwner {
+ IERC20 wrappedNativeToken = IERC20(wrappedNativeTokenAddress);
- // Withdraw rbtc
- (bool success, ) = owner().call.value(rbtcAmount)("");
+ uint256 balance = wrappedNativeToken.balanceOf(address(this));
require(
- success,
- "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal rbtc failed"
+ wrappedNativeTokenAmount <= balance,
+ "FeeSharingCollector::withdrawWrappedNativeToken:Insufficient balance"
);
- // Withdraw ZUSD
- IERC20(zusdToken).safeTransfer(owner(), zusdAmount);
-
- // Withdraw SOV
- IERC20(sovToken).safeTransfer(owner(), sovAmount);
+ wrappedNativeToken.safeTransfer(receiver, wrappedNativeTokenAmount);
}
/**
- * @dev view function that calculate the total RBTC that includes:
- * - RBTC
- * - WRBTC
- * - iWRBTC * iWRBTC.tokenPrice()
+ * @dev view function that calculate the total NativeToken that includes:
+ * - NativeToken
+ * - WrappedNativeToken
+ * - iWrappedNativeToken * iWrappedNativeToken.tokenPrice()
* @param _user address of the user.
- * @return rbtc balance of the given user's address.
+ * @return nativeToken balance of the given user's address.
*/
- function getAccumulatedRBTCFeeBalances(address _user) external view returns (uint256) {
+ function getAccumulatedNativeTokenFeeBalances(address _user) external view returns (uint256) {
(
- uint256 _rbtcAmount,
- uint256 _wrbtcAmount,
- uint256 _iWrbtcAmount,
+ uint256 _nativeTokenAmount,
+ uint256 _wrappedNativeTokenAmount,
+ uint256 _iWrappedNativeTokenAmount,
,
,
- ) = _getRBTCBalances(_user, 0);
- uint256 iWRBTCAmountInRBTC = _iWrbtcAmount
- .mul(ILoanTokenWRBTC(loanTokenWrbtcAddress).tokenPrice())
+ ) = _getNativeTokenBalances(_user, 0);
+ uint256 iWrappedNativeTokenAmountInNativeToken = _iWrappedNativeTokenAmount
+ .mul(ILoanWrappedNativeToken(loanWrappedNativeTokenAddress).tokenPrice())
.div(1e18);
- return _rbtcAmount.add(_wrbtcAmount).add(iWRBTCAmountInRBTC);
+ return
+ _nativeTokenAmount.add(_wrappedNativeTokenAmount).add(
+ iWrappedNativeTokenAmountInNativeToken
+ );
}
/**
- * @dev private function that responsible to calculate the user's token that has RBTC as underlying token (rbtc, wrbtc, iWrbtc)
+ * @dev private function that responsible to calculate the user's token that has NativeToken as underlying token (nativeToken, wrappedNativeToken, iWrappedNativeToken)
*
* @param _user address of the user.
* @param _maxCheckpoints maximum checkpoints.
*
- * @return _rbtcAmount rbtc amount
- * @return _wrbtcAmount wrbtc amount
- * @return _iWrbtcAmount iWrbtc (wrbtc lending pool token) amount * token price
- * @return _endRBTC end time of accumulated fee calculation for rbtc
- * @return _endWRBTC end time of accumulated fee calculation for wrbtc
- * @return _endIWRBTC end time of accumulated fee calculation for iwrbtc
+ * @return _nativeTokenAmount nativeToken amount
+ * @return _wrappedNativeTokenAmount wrappedNativeToken amount
+ * @return _iWrappedNativeTokenAmount iWrappedNativeToken (wrappedNativeToken lending pool token) amount * token price
+ * @return _endNativeToken end time of accumulated fee calculation for nativeToken
+ * @return _endWrappedNativeToken end time of accumulated fee calculation for wrappedNativeToken
+ * @return _endIWrappedNativeToken end time of accumulated fee calculation for iWrappedNativeToken
*/
- function _getRBTCBalances(
+ function _getNativeTokenBalances(
address _user,
uint32 _maxCheckpoints
)
private
view
returns (
- uint256 _rbtcAmount,
- uint256 _wrbtcAmount,
- uint256 _iWrbtcAmount,
- uint256 _endRBTC,
- uint256 _endWRBTC,
- uint256 _endIWRBTC
+ uint256 _nativeTokenAmount,
+ uint256 _wrappedNativeTokenAmount,
+ uint256 _iWrappedNativeTokenAmount,
+ uint256 _endNativeToken,
+ uint256 _endWrappedNativeToken,
+ uint256 _endIWrappedNativeToken
)
{
- (_rbtcAmount, _endRBTC) = _getAccumulatedFees({
+ (_nativeTokenAmount, _endNativeToken) = _getAccumulatedFees({
_user: _user,
_token: RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
_startFrom: 0,
_maxCheckpoints: _maxCheckpoints
});
- (_wrbtcAmount, _endWRBTC) = _getAccumulatedFees({
+ (_wrappedNativeTokenAmount, _endWrappedNativeToken) = _getAccumulatedFees({
_user: _user,
- _token: wrbtcTokenAddress,
+ _token: wrappedNativeTokenAddress,
_startFrom: 0,
_maxCheckpoints: _maxCheckpoints
});
- (_iWrbtcAmount, _endIWRBTC) = _getAccumulatedFees({
+ (_iWrappedNativeTokenAmount, _endIWrappedNativeToken) = _getAccumulatedFees({
_user: _user,
- _token: loanTokenWrbtcAddress,
+ _token: loanWrappedNativeTokenAddress,
_startFrom: 0,
_maxCheckpoints: _maxCheckpoints
});
}
/**
- * @dev private function that responsible to calculate the user's token that has RBTC as underlying token (rbtc, wrbtc, iWrbtc)
+ * @dev private function that responsible to calculate the user's token that has NativeToken as underlying token (nativeToken, wrappedNativeToken, iWrappedNativeToken)
*
- * @param _token either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrbtc address or iwrbtc address
+ * @param _token either RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT or wrappedNativeToken address or iWrappedNativeToken address
* @param _user address of the user.
* @param _maxCheckpoints maximum checkpoints.
*
- * @return _tokenAmount token (rbtc, or wrbtc, or iwrbtc) amount
- * @return _endToken end time of accumulated fee calculation for token (rbtc, or wrbtc, or iwrbtc )
+ * @return _tokenAmount token (nativeToken, or wrappedNativeToken, or iWrappedNativeToken) amount
+ * @return _endToken end time of accumulated fee calculation for token (nativeToken, or wrappedNativeToken, or iWrappedNativeToken )
*/
- function _getRBTCBalance(
+ function _getNativeTokenBalance(
address _token,
address _user,
uint32 _maxCheckpoints
) internal view returns (uint256 _tokenAmount, uint256 _endToken) {
if (
_token == RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT ||
- _token == wrbtcTokenAddress ||
- _token == loanTokenWrbtcAddress
+ _token == wrappedNativeTokenAddress ||
+ _token == loanWrappedNativeTokenAddress
) {
(_tokenAmount, _endToken) = _getAccumulatedFees({
_user: _user,
@@ -1248,7 +1235,9 @@ contract FeeSharingCollector is
_maxCheckpoints: _maxCheckpoints
});
} else {
- revert("FeeSharingCollector::_getRBTCBalance: only rbtc-based tokens are allowed");
+ revert(
+ "FeeSharingCollector::_getNativeTokenBalance: only native token based tokens are allowed"
+ );
}
}
@@ -1266,12 +1255,7 @@ contract FeeSharingCollector is
}
}
-/* Interfaces */
-interface ILoanToken {
- function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount);
-}
-
-interface ILoanTokenWRBTC {
+interface ILoanWrappedNativeToken {
function burnToBTC(
address receiver,
uint256 burnAmount,
diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol
new file mode 100644
index 000000000..a481c16b8
--- /dev/null
+++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol
@@ -0,0 +1,646 @@
+pragma solidity ^0.5.17;
+pragma experimental ABIEncoderV2;
+
+import "../Staking/SafeMath96.sol";
+import "../../openzeppelin/SafeMath.sol";
+import "../../openzeppelin/SafeERC20.sol";
+import "../../openzeppelin/Ownable.sol";
+import "../IFeeSharingCollectorMultiToken.sol";
+import "../../openzeppelin/Address.sol";
+import "./FeeSharingCollectorStorage.sol";
+import "../../interfaces/ISovrynLBFactoryDex.sol";
+import "../../interfaces/ISovrynLBPairDex.sol";
+
+/**
+ * @title The FeeSharingCollectorMultiToken contract.
+ * @notice Staking is not only granting voting rights, but also access to fee
+ * sharing according to the own voting power in relation to the total. Whenever
+ * somebody decides to collect the fees from the protocol, they get transferred
+ * to a proxy contract which invests the funds in the lending pool and keeps
+ * the pool tokens.
+ *
+ * The fee sharing proxy will be set as feesController of the protocol contract.
+ * This allows the fee sharing proxy to withdraw the fees. The fee sharing
+ * proxy holds the pool tokens and keeps track of which user owns how many
+ * tokens. In order to know how many tokens a user owns, the fee sharing proxy
+ * needs to know the user’s weighted stake in relation to the total weighted
+ * stake (aka total voting power).
+ *
+ * Because both values are subject to change, they may be different on each fee
+ * withdrawal. To be able to calculate a user’s share of tokens when he wants
+ * to withdraw, we need checkpoints.
+ *
+ * This contract is intended to be set as the protocol fee collector.
+ * Anybody can invoke the withdrawFees function which uses
+ * protocol.withdrawFees to obtain available fees from operations on a
+ * certain token. These fees are deposited in the corresponding loanPool.
+ * Also, the staking contract sends slashed tokens to this contract. When a
+ * user calls the withdraw function, the contract transfers the fee sharing
+ * rewards in proportion to the user’s weighted stake since the last withdrawal.
+ *
+ * The protocol is collecting fees in all sorts of currencies and then automatically
+ * supplies them to the respective lending pools. Therefore, all fees are
+ * generating interest for the SOV holders. If one of them withdraws fees, it will
+ * get pool tokens. It is planned to add the option to convert anything to native token
+ * before withdrawing, but not yet implemented.
+ * */
+contract FeeSharingCollectorMultiToken is
+ SafeMath96,
+ IFeeSharingCollectorMultiToken,
+ Ownable,
+ FeeSharingCollectorStorage
+{
+ using SafeMath for uint256;
+ using SafeERC20 for IERC20;
+
+ address constant ZERO_ADDRESS = address(0);
+ /** To support backward compatibility, we need to keep this constant variable name as it is (which is derived from rsk network) */
+ address public constant RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT =
+ address(uint160(uint256(keccak256("RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT"))));
+ uint16 public constant SOVRYN_DEX_COLD_PATH_PROXY_IDX = 3;
+ uint8 public constant SOVRYN_DEX_CMD_COLLECT_TREASURY_CODE = 40;
+
+ /* Events */
+
+ /// @notice An event emitted when fee get withdrawn.
+ event FeeWithdrawn(address indexed sender, address indexed token, uint256 amount);
+
+ /// @notice An event emitted when tokens transferred.
+ event TokensTransferred(address indexed sender, address indexed token, uint256 amount);
+
+ /// @notice An event emitted when checkpoint added.
+ event CheckpointAdded(address indexed sender, address indexed token, uint256 amount);
+
+ /// @notice An event emitted when user fee get withdrawn.
+ event UserFeeWithdrawn(
+ address indexed sender,
+ address indexed receiver,
+ address indexed token,
+ uint256 amount
+ );
+
+ event SetWrappedNativeToken(
+ address indexed sender,
+ address indexed oldWrappedNativeToken,
+ address indexed newWrappedNativeToken
+ );
+
+ event SetProtocolAddress(address indexed sender, address _protocolAddress);
+
+ event SetSovrynLBDexFactoryAddress(
+ address indexed sender,
+ address indexed oldSovrynLBDexFactoryAddress,
+ address indexed newSovrynLBDexFactoryAddress
+ );
+
+ /* Functions */
+ /// @dev fallback function to support nativeToken transfer when unwrap the wrappedNativeToken.
+ function() external payable {}
+
+ /**
+ * @dev initialize function for fee sharing collector proxy
+ * @param wrappedNativeToken wrappedNativeToken token address
+ * @param dexAddress wrappedNativeToken token address
+ */
+ function initialize(
+ address wrappedNativeToken,
+ address dexAddress
+ ) external onlyOwner oneTimeExecution(this.initialize.selector) {
+ setWrappedNativeToken(wrappedNativeToken);
+ setSovrynLBDexFactoryAddress(dexAddress);
+ }
+
+ /**
+ * @notice Set the wrappedNativeToken token address of fee sharing collector.
+ *
+ * only owner can perform this action.
+ *
+ * @param newWrappedNativeTokenAddress The new address of the wrappedNativeToken token.
+ * */
+ function setWrappedNativeToken(address newWrappedNativeTokenAddress) public onlyOwner {
+ require(
+ Address.isContract(newWrappedNativeTokenAddress),
+ "newWrappedNativeTokenAddress not a contract"
+ );
+ emit SetWrappedNativeToken(
+ msg.sender,
+ wrappedNativeTokenAddress,
+ newWrappedNativeTokenAddress
+ );
+ wrappedNativeTokenAddress = newWrappedNativeTokenAddress;
+ }
+
+ /**
+ * @notice Set the Protocol address if not set at initial deployment of the proxy contract
+ *
+ * only owner can perform this action.
+ *
+ * @param _protocolAddress Sovryn protocol address.
+ * */
+ function setProtocolAddress(
+ address _protocolAddress
+ ) public onlyOwner oneTimeExecution(this.setProtocolAddress.selector) {
+ require(Address.isContract(_protocolAddress), "_protocolAddress not a contract");
+ require(address(protocol) == address(0x0), "protocol address already set");
+ protocol = IProtocol(_protocolAddress);
+ emit SetProtocolAddress(msg.sender, _protocolAddress);
+ }
+
+ function setSovrynLBDexFactoryAddress(address _sovrynLBDexFactoryAddress) public onlyOwner {
+ require(
+ Address.isContract(_sovrynLBDexFactoryAddress),
+ "_sovrynLBDexFactoryAddress not a contract"
+ );
+ emit SetSovrynLBDexFactoryAddress(
+ msg.sender,
+ sovrynLBDexFactoryAddress,
+ _sovrynLBDexFactoryAddress
+ );
+ sovrynLBDexFactoryAddress = _sovrynLBDexFactoryAddress;
+ }
+
+ /**
+ * @notice Withdraw fees for the given token:
+ * lendingFee + tradingFee + borrowingFee
+ *
+ * @param _tokens array address of the token
+ * */
+ function withdrawFees(address[] memory _tokens) public {
+ for (uint256 i = 0; i < _tokens.length; i++) {
+ require(
+ Address.isContract(_tokens[i]),
+ "FeeSharingCollectorMultiToken::withdrawFees: token is not a contract"
+ );
+ }
+
+ uint256 wrappedNativeTokenAmountWithdrawn = protocol.withdrawFees(_tokens, address(this));
+ uint256 poolTokenAmount;
+
+ require(
+ wrappedNativeTokenAddress != address(0),
+ "FeeSharingCollectorMultiToken::withdrawFees: wrappedNativeTokenAddress is not set"
+ );
+
+ address loanPoolToken = protocol.underlyingToLoanPool(wrappedNativeTokenAddress);
+ require(
+ loanPoolToken != address(0),
+ "FeeSharingCollectorMultiToken::withdrawFees: loan wrappedNativeTokenAddress not found"
+ );
+
+ if (wrappedNativeTokenAmountWithdrawn > 0) {
+ /// @dev TODO can be also used - function addLiquidity(IERC20Token _reserveToken, uint256 _amount, uint256 _minReturn)
+ IERC20(wrappedNativeTokenAddress).approve(
+ loanPoolToken,
+ wrappedNativeTokenAmountWithdrawn
+ );
+ poolTokenAmount = ILoanToken(loanPoolToken).mint(
+ address(this),
+ wrappedNativeTokenAmountWithdrawn
+ );
+
+ /// @notice Update unprocessed amount of tokens
+ uint96 amount96 = safe96(
+ poolTokenAmount,
+ "FeeSharingCollectorMultiToken::withdrawFees: pool token amount exceeds 96 bits"
+ );
+
+ _addCheckpoint(loanPoolToken, amount96);
+ }
+
+ emit FeeWithdrawn(msg.sender, loanPoolToken, poolTokenAmount);
+ }
+
+ /**
+ * @notice Withdraw fees from sovryn-lb-dex
+ *
+ * the fees will be converted in wrappedNativeToken form, and then will be transferred to wrappedNativeToken loan pool
+ *
+ * */
+ function withdrawFeesFromLBDex() public {
+ /** Get all of the pairs that has > 0 protocol fee */
+ uint256 totalPairs = ISovrynLBFactoryDex(sovrynLBDexFactoryAddress).getNumberOfLBPairs();
+ for (uint256 i = 0; i < totalPairs; i++) {
+ address lbPair = ISovrynLBFactoryDex(sovrynLBDexFactoryAddress).getLBPairAtIndex(i);
+ (uint128 feeX, uint128 feeY) = ISovrynLBPairDex(lbPair).getProtocolFees();
+
+ /** Skip if lbPair contains 0 protocol fees */
+ if (feeX == 0 && feeY == 0) continue;
+
+ /** Withdraw fee */
+ ISovrynLBPairDex(lbPair).collectProtocolFees();
+
+ /** Get tokenX & tokenY */
+ address tokenX = ISovrynLBPairDex(lbPair).getTokenX();
+ address tokenY = ISovrynLBPairDex(lbPair).getTokenY();
+
+ /** Add checkpoint for the token */
+ _addCheckpoint(
+ tokenX,
+ safe96(
+ uint256(feeX),
+ "FeeSharingProxy::withdrawFeesFromLBDex: tokenFeeX amount exceeds 96 bits"
+ )
+ );
+ _addCheckpoint(
+ tokenY,
+ safe96(
+ uint256(feeY),
+ "FeeSharingProxy::withdrawFeesFromLBDex: tokenFeeY amount exceeds 96 bits"
+ )
+ );
+ }
+ }
+
+ /**
+ * @notice Transfer tokens to this contract.
+ * @dev We just update amount of tokens here and write checkpoint in a separate methods
+ * in order to prevent adding checkpoints too often.
+ * @dev the caller should take care of setting allowance for the token
+ * @param _token Address of the token.
+ * @param _amount Amount to be transferred.
+ * */
+ function transferTokens(address _token, uint96 _amount) public {
+ require(
+ _token != ZERO_ADDRESS,
+ "FeeSharingCollectorMultiToken::transferTokens: invalid address"
+ );
+ require(_amount > 0, "FeeSharingCollectorMultiToken::transferTokens: invalid amount");
+
+ /// @notice Transfer tokens from msg.sender
+ bool success = IERC20(_token).transferFrom(address(msg.sender), address(this), _amount);
+ require(success, "Staking::transferTokens: token transfer failed");
+
+ // if _token is wrappedNativeToken, need to unwrap it to nativeToken
+ IWrappedNativeTokenERC20 wrappedNativeToken = IWrappedNativeTokenERC20(
+ wrappedNativeTokenAddress
+ );
+ if (_token == address(wrappedNativeToken)) {
+ wrappedNativeToken.withdraw(_amount);
+ _token = RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT;
+ }
+
+ _addCheckpoint(_token, _amount);
+
+ emit TokensTransferred(msg.sender, _token, _amount);
+ }
+
+ /**
+ * @notice Add checkpoint with accumulated amount by function invocation.
+ * @param _token Address of the token.
+ * */
+ function _addCheckpoint(address _token, uint96 _amount) internal {
+ if (block.timestamp - lastFeeWithdrawalTime[_token] >= FEE_WITHDRAWAL_INTERVAL) {
+ lastFeeWithdrawalTime[_token] = block.timestamp;
+ uint96 amount = add96(
+ unprocessedAmount[_token],
+ _amount,
+ "FeeSharingCollectorMultiToken::_addCheckpoint: amount exceeds 96 bits"
+ );
+
+ /// @notice Reset unprocessed amount of tokens to zero.
+ unprocessedAmount[_token] = 0;
+
+ /// @notice Write a regular checkpoint.
+ _writeTokenCheckpoint(_token, amount);
+ } else {
+ unprocessedAmount[_token] = add96(
+ unprocessedAmount[_token],
+ _amount,
+ "FeeSharingCollectorMultiToken::_addCheckpoint: unprocessedAmount exceeds 96 bits"
+ );
+ }
+ }
+
+ /**
+ * @notice Withdraw accumulated fee to the message sender.
+ *
+ * The Sovryn protocol collects fees on every trade/swap and loan.
+ * These fees will be distributed to SOV stakers based on their voting
+ * power as a percentage of total voting power. Therefore, staking more
+ * SOV and/or staking for longer will increase your share of the fees
+ * generated, meaning you will earn more from staking.
+ *
+ * This function will directly burnToBTC and use the msg.sender (user) as the receiver
+ *
+ * @param _token Addresses of the pool token.
+ * @param _maxCheckpoint Maximum number of checkpoints to be processed.
+ * @param _receiver The receiver of tokens or msg.sender
+ * */
+ function withdraw(
+ address _token,
+ uint32 _maxCheckpoint,
+ address _receiver
+ ) public nonReentrant {
+ _withdraw(_token, _maxCheckpoint, _receiver);
+ }
+
+ /**
+ * @notice Withdraw accumulated fees (multiple token) to the message sender.
+ *
+ * The Sovryn protocol collects fees on every trade/swap and loan.
+ * These fees will be distributed to SOV stakers based on their voting
+ * power as a percentage of total voting power. Therefore, staking more
+ * SOV and/or staking for longer will increase your share of the fees
+ * generated, meaning you will earn more from staking.
+ *
+ * This function will directly burnToBTC and use the msg.sender (user) as the receiver
+ *
+ * @param _tokens Array of Addresses of the pool token.
+ * @param _maxCheckpoints Array of Maximum number of checkpoints to be processed.
+ * @param _receiver The receiver of tokens or msg.sender
+ * */
+ function withdrawTokens(
+ address[] memory _tokens,
+ uint32[] memory _maxCheckpoints,
+ address _receiver
+ ) public nonReentrant {
+ require(
+ _tokens.length == _maxCheckpoints.length,
+ "length mismatch _tokens <> _maxCheckpoints"
+ );
+
+ for (uint256 i = 0; i < _tokens.length; i++) {
+ address _token = _tokens[i];
+ uint32 _maxCheckpoint = _maxCheckpoints[i];
+
+ _withdraw(_token, _maxCheckpoint, _receiver);
+ }
+ }
+
+ /**
+ * @notice Internal function to withdraw accumulated fee to the message sender.
+ *
+ * The Sovryn protocol collects fees on every trade/swap and loan.
+ * These fees will be distributed to SOV stakers based on their voting
+ * power as a percentage of total voting power. Therefore, staking more
+ * SOV and/or staking for longer will increase your share of the fees
+ * generated, meaning you will earn more from staking.
+ *
+ * This function will directly burnToBTC and use the msg.sender (user) as the receiver
+ *
+ * @param _token Address of the pool token.
+ * @param _maxCheckpoint Maximum number of checkpoints to be processed.
+ * @param _receiver The receiver of tokens or msg.sender
+ * */
+ function _withdraw(address _token, uint32 _maxCheckpoint, address _receiver) internal {
+ /// @dev Prevents processing / checkpoints because of block gas limit.
+ require(
+ _maxCheckpoint > 0,
+ "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive"
+ );
+
+ address _wrappedNativeTokenAddress = wrappedNativeTokenAddress;
+ require(
+ _wrappedNativeTokenAddress != address(0),
+ "FeeSharingCollectorMultiToken::withdraw: _wrappedNativeTokenAddress is not set"
+ );
+
+ address loanWrappedNativeToken = protocol.underlyingToLoanPool(_wrappedNativeTokenAddress);
+ require(
+ loanWrappedNativeToken != address(0),
+ "FeeSharingCollectorMultiToken::withdraw: loan wrapped native token not found"
+ );
+
+ address user = msg.sender;
+ if (_receiver == address(0)) {
+ _receiver = msg.sender;
+ }
+
+ uint256 amount;
+ uint256 end;
+ (amount, end) = _getAccumulatedFees(user, _token, _maxCheckpoint);
+ require(
+ amount > 0,
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+
+ processedCheckpoints[user][_token] = end;
+
+ if (loanWrappedNativeToken == _token) {
+ // We will change, so that feeSharingCollector will directly burn then loanWrappedNativeToken (IWrappedNativeToken) to nativeToken and send to the user --- by call burnToBTC function which is burning to a native token - to be renamed to burnedToNativeToken
+ ILoanWrappedNativeToken(_token).burnToBTC(_receiver, amount, false);
+ } else {
+ require(
+ IERC20(_token).transfer(_receiver, amount),
+ "FeeSharingCollectorMultiToken::withdraw: withdrawal failed"
+ );
+ }
+
+ emit UserFeeWithdrawn(msg.sender, _receiver, _token, amount);
+ }
+
+ /**
+ * @notice Get the accumulated loan pool fee of the message sender.
+ * @param _user The address of the user or contract.
+ * @param _loanPoolToken Address of the pool token.
+ * @return The accumulated fee for the message sender.
+ * */
+ function getAccumulatedFees(
+ address _user,
+ address _loanPoolToken
+ ) public view returns (uint256) {
+ uint256 amount;
+ (amount, ) = _getAccumulatedFees(_user, _loanPoolToken, 0);
+ return amount;
+ }
+
+ /**
+ * @notice Whenever fees are withdrawn, the staking contract needs to
+ * checkpoint the block number, the number of pool tokens and the
+ * total voting power at that time (read from the staking contract).
+ * While the total voting power would not necessarily need to be
+ * checkpointed, it makes sense to save gas cost on withdrawal.
+ *
+ * When the user wants to withdraw its share of tokens, we need
+ * to iterate over all of the checkpoints since the users last
+ * withdrawal (note: remember last withdrawal block), query the
+ * user’s balance at the checkpoint blocks from the staking contract,
+ * compute his share of the checkpointed tokens and add them up.
+ * The maximum number of checkpoints to process at once should be limited.
+ *
+ * @param _user Address of the user's account.
+ * @param _loanPoolToken Loan pool token address.
+ * @param _maxCheckpoints Checkpoint index incremental.
+ * */
+ function _getAccumulatedFees(
+ address _user,
+ address _loanPoolToken,
+ uint32 _maxCheckpoints
+ ) internal view returns (uint256, uint256) {
+ if (staking.isVestingContract(_user)) {
+ return (0, 0);
+ }
+
+ uint256 start = processedCheckpoints[_user][_loanPoolToken];
+ uint256 end;
+
+ /// @dev Additional bool param can't be used because of stack too deep error.
+ if (_maxCheckpoints > 0) {
+ /// @dev withdraw -> _getAccumulatedFees
+ require(
+ start < totalTokenCheckpoints[_loanPoolToken],
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+ end = _getEndOfRange(start, _loanPoolToken, _maxCheckpoints);
+ } else {
+ /// @dev getAccumulatedFees -> _getAccumulatedFees
+ /// Don't throw error for getter invocation outside of transaction.
+ if (start >= totalTokenCheckpoints[_loanPoolToken]) {
+ return (0, totalTokenCheckpoints[_loanPoolToken]);
+ }
+ end = totalTokenCheckpoints[_loanPoolToken];
+ }
+
+ uint256 amount = 0;
+ uint256 cachedLockDate = 0;
+ uint96 cachedWeightedStake = 0;
+ for (uint256 i = start; i < end; i++) {
+ Checkpoint storage checkpoint = tokenCheckpoints[_loanPoolToken][i];
+ uint256 lockDate = staking.timestampToLockDate(checkpoint.timestamp);
+ uint96 weightedStake;
+ if (lockDate == cachedLockDate) {
+ weightedStake = cachedWeightedStake;
+ } else {
+ /// @dev We need to use "checkpoint.blockNumber - 1" here to calculate weighted stake
+ /// For the same block like we did for total voting power in _writeTokenCheckpoint
+ weightedStake = staking.getPriorWeightedStake(
+ _user,
+ checkpoint.blockNumber - 1,
+ checkpoint.timestamp
+ );
+ cachedWeightedStake = weightedStake;
+ cachedLockDate = lockDate;
+ }
+ uint256 share = uint256(checkpoint.numTokens).mul(weightedStake).div(
+ uint256(checkpoint.totalWeightedStake)
+ );
+ amount = amount.add(share);
+ }
+ return (amount, end);
+ }
+
+ /**
+ * @notice Withdrawal should only be possible for blocks which were already
+ * mined. If the fees are withdrawn in the same block as the user withdrawal
+ * they are not considered by the withdrawing logic (to avoid inconsistencies).
+ *
+ * @param start Start of the range.
+ * @param _loanPoolToken Loan pool token address.
+ * @param _maxCheckpoints Checkpoint index incremental.
+ * */
+ function _getEndOfRange(
+ uint256 start,
+ address _loanPoolToken,
+ uint32 _maxCheckpoints
+ ) internal view returns (uint256) {
+ uint256 nCheckpoints = totalTokenCheckpoints[_loanPoolToken];
+ uint256 end;
+ if (_maxCheckpoints == 0) {
+ /// @dev All checkpoints will be processed (only for getter outside of a transaction).
+ end = nCheckpoints;
+ } else {
+ if (_maxCheckpoints > MAX_CHECKPOINTS) {
+ _maxCheckpoints = MAX_CHECKPOINTS;
+ }
+ end = safe32(
+ start + _maxCheckpoints,
+ "FeeSharingCollectorMultiToken::withdraw: checkpoint index exceeds 32 bits"
+ );
+ if (end > nCheckpoints) {
+ end = nCheckpoints;
+ }
+ }
+
+ /// @dev Withdrawal should only be possible for blocks which were already mined.
+ uint32 lastBlockNumber = tokenCheckpoints[_loanPoolToken][end - 1].blockNumber;
+ if (block.number == lastBlockNumber) {
+ end--;
+ }
+ return end;
+ }
+
+ /**
+ * @notice Write a regular checkpoint w/ the foolowing data:
+ * block number, block timestamp, total weighted stake and num of tokens.
+ * @param _token The pool token address.
+ * @param _numTokens The amount of pool tokens.
+ * */
+ function _writeTokenCheckpoint(address _token, uint96 _numTokens) internal {
+ uint32 blockNumber = safe32(
+ block.number,
+ "FeeSharingCollectorMultiToken::_writeCheckpoint: block number exceeds 32 bits"
+ );
+ uint32 blockTimestamp = safe32(
+ block.timestamp,
+ "FeeSharingCollectorMultiToken::_writeCheckpoint: block timestamp exceeds 32 bits"
+ );
+ uint256 nCheckpoints = totalTokenCheckpoints[_token];
+
+ uint96 totalWeightedStake = _getVoluntaryWeightedStake(blockNumber - 1, block.timestamp);
+ require(totalWeightedStake > 0, "Invalid totalWeightedStake");
+ if (
+ nCheckpoints > 0 &&
+ tokenCheckpoints[_token][nCheckpoints - 1].blockNumber == blockNumber
+ ) {
+ tokenCheckpoints[_token][nCheckpoints - 1].totalWeightedStake = totalWeightedStake;
+ tokenCheckpoints[_token][nCheckpoints - 1].numTokens = _numTokens;
+ } else {
+ tokenCheckpoints[_token][nCheckpoints] = Checkpoint(
+ blockNumber,
+ blockTimestamp,
+ totalWeightedStake,
+ _numTokens
+ );
+ totalTokenCheckpoints[_token] = nCheckpoints + 1;
+ }
+ emit CheckpointAdded(msg.sender, _token, _numTokens);
+ }
+
+ /**
+ * Queries the total weighted stake and the weighted stake of vesting contracts and returns the difference
+ * @param blockNumber the blocknumber
+ * @param timestamp the timestamp
+ */
+ function _getVoluntaryWeightedStake(
+ uint32 blockNumber,
+ uint256 timestamp
+ ) internal view returns (uint96 totalWeightedStake) {
+ uint96 vestingWeightedStake = staking.getPriorVestingWeightedStake(blockNumber, timestamp);
+ totalWeightedStake = staking.getPriorTotalVotingPower(blockNumber, timestamp);
+ totalWeightedStake = sub96(
+ totalWeightedStake,
+ vestingWeightedStake,
+ "FeeSharingCollectorMultiToken::_getTotalVoluntaryWeightedStake: vested stake exceeds total stake"
+ );
+ }
+
+ function withdrawWrappedNativeToken(
+ address receiver,
+ uint256 wrappedNativeTokenAmount
+ ) external onlyOwner {
+ require(
+ wrappedNativeTokenAddress != address(0),
+ "FeeSharingCollectorMultiToken::withdrawFees: wrappedNativeTokenAddress is not set"
+ );
+
+ uint256 balance = IERC20(wrappedNativeTokenAddress).balanceOf(address(this));
+ require(wrappedNativeTokenAmount <= balance, "Insufficient balance");
+
+ IERC20(wrappedNativeTokenAddress).safeTransfer(receiver, wrappedNativeTokenAmount);
+ }
+}
+
+/* Interfaces */
+interface ILoanToken {
+ function mint(address receiver, uint256 depositAmount) external returns (uint256 mintAmount);
+}
+
+interface ILoanWrappedNativeToken {
+ function burnToBTC(
+ address receiver,
+ uint256 burnAmount,
+ bool useLM
+ ) external returns (uint256 loanAmountPaid);
+
+ function tokenPrice() external view returns (uint256 price);
+}
diff --git a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol
index a6881517c..ea94f919f 100644
--- a/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol
+++ b/contracts/governance/FeeSharingCollector/FeeSharingCollectorStorage.sol
@@ -6,7 +6,7 @@ import "../../interfaces/IERC20.sol";
import "../IFeeSharingCollector.sol";
import "../Staking/interfaces/IStaking.sol";
import "../../mixins/EnumerableAddressSet.sol";
-import "../../interfaces/IWrbtcERC20.sol";
+import "../../interfaces/IWrappedNativeTokenERC20.sol";
/**
* @title FeeSharingCollectorStorage contact
@@ -16,6 +16,7 @@ import "../../interfaces/IWrbtcERC20.sol";
contract FeeSharingCollectorStorage is Ownable {
using EnumerableAddressSet for EnumerableAddressSet.AddressSet;
uint256 constant FEE_WITHDRAWAL_INTERVAL = 172800;
+ uint32 constant MAX_CHECKPOINTS = 100;
IProtocol public protocol;
IStaking public staking;
@@ -79,12 +80,17 @@ contract FeeSharingCollectorStorage is Ownable {
/**
* @dev Wrapped native token address
*/
- address public wrbtcTokenAddress;
+ address public wrappedNativeTokenAddress;
/**
* @dev Wrapped native token loan token address
*/
- address public loanTokenWrbtcAddress;
+ address public loanWrappedNativeTokenAddress;
+
+ /**
+ * @dev sovryn lb dex factory address
+ */
+ address public sovrynLBDexFactoryAddress;
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
@@ -100,6 +106,16 @@ contract FeeSharingCollectorStorage is Ownable {
_;
reentrancyLock = REENTRANCY_GUARD_FREE;
}
+
+ /* Modifier */
+ modifier oneTimeExecution(bytes4 _funcSig) {
+ require(
+ !isFunctionExecuted[_funcSig],
+ "FeeSharingCollector: function can only be called once"
+ );
+ _;
+ isFunctionExecuted[_funcSig] = true;
+ }
}
/* Interfaces */
@@ -110,16 +126,16 @@ interface IProtocol {
* @param tokens The array address of the token instance.
* @param receiver The address of the withdrawal recipient.
*
- * @return The withdrawn total amount in wRBTC
+ * @return The withdrawn total amount in wrappedNativeToken
* */
function withdrawFees(
address[] calldata tokens,
address receiver
- ) external returns (uint256 totalWRBTCWithdrawn);
+ ) external returns (uint256 totalWrappedNativeTokenWithdrawn);
function underlyingToLoanPool(address token) external view returns (address);
- function wrbtcToken() external view returns (IWrbtcERC20);
+ function wrappedNativeToken() external view returns (IWrappedNativeTokenERC20);
function getSovTokenAddress() external view returns (address);
}
diff --git a/contracts/governance/IFeeSharingCollectorMultiToken.sol b/contracts/governance/IFeeSharingCollectorMultiToken.sol
new file mode 100644
index 000000000..54277b470
--- /dev/null
+++ b/contracts/governance/IFeeSharingCollectorMultiToken.sol
@@ -0,0 +1,19 @@
+pragma solidity ^0.5.17;
+
+/**
+ * @title Interface for contract governance/FeeSharingCollector/FeeSharingCollector.sol
+ * @dev Interfaces are used to cast a contract address into a callable instance.
+ * */
+interface IFeeSharingCollectorMultiToken {
+ function withdrawFees(address[] calldata _token) external;
+
+ function transferTokens(address _token, uint96 _amount) external;
+
+ function withdraw(address _token, uint32 _maxCheckpoint, address _receiver) external;
+
+ function withdrawTokens(
+ address[] calldata _tokens,
+ uint32[] calldata _maxCheckpoints,
+ address _receiver
+ ) external;
+}
diff --git a/contracts/interfaces/ISovrynLBFactoryDex.sol b/contracts/interfaces/ISovrynLBFactoryDex.sol
new file mode 100644
index 000000000..cc2cd359e
--- /dev/null
+++ b/contracts/interfaces/ISovrynLBFactoryDex.sol
@@ -0,0 +1,9 @@
+pragma solidity 0.5.17;
+
+interface ISovrynLBFactoryDex {
+ function getNumberOfLBPairs() external view returns (uint256);
+
+ function getLBPairAtIndex(uint256 id) external returns (address);
+
+ function getFeeRecipient() external view returns (address);
+}
diff --git a/contracts/interfaces/ISovrynLBPairDex.sol b/contracts/interfaces/ISovrynLBPairDex.sol
new file mode 100644
index 000000000..4df6d730e
--- /dev/null
+++ b/contracts/interfaces/ISovrynLBPairDex.sol
@@ -0,0 +1,11 @@
+pragma solidity 0.5.17;
+
+interface ISovrynLBPairDex {
+ function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY);
+
+ function collectProtocolFees() external returns (bytes32 collectedProtocolFees);
+
+ function getTokenX() external view returns (address tokenX);
+
+ function getTokenY() external view returns (address tokenY);
+}
diff --git a/contracts/interfaces/IWrappedNativeToken.sol b/contracts/interfaces/IWrappedNativeToken.sol
new file mode 100644
index 000000000..57b89fde6
--- /dev/null
+++ b/contracts/interfaces/IWrappedNativeToken.sol
@@ -0,0 +1,12 @@
+/**
+ * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0.
+ */
+
+pragma solidity >=0.5.0 <0.6.0;
+
+interface IWrappedNativeToken {
+ function deposit() external payable;
+
+ function withdraw(uint256 wad) external;
+}
diff --git a/contracts/interfaces/IWrappedNativeTokenERC20.sol b/contracts/interfaces/IWrappedNativeTokenERC20.sol
new file mode 100644
index 000000000..aca2a529c
--- /dev/null
+++ b/contracts/interfaces/IWrappedNativeTokenERC20.sol
@@ -0,0 +1,11 @@
+/**
+ * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved.
+ * Licensed under the Apache License, Version 2.0.
+ */
+
+pragma solidity >=0.5.0 <0.6.0;
+
+import "./IWrappedNativeToken.sol";
+import "./IERC20.sol";
+
+contract IWrappedNativeTokenERC20 is IWrappedNativeToken, IERC20 {}
diff --git a/contracts/mockup/FeeSharingCollectorMockup.sol b/contracts/mockup/FeeSharingCollectorMockup.sol
index 67b092663..1a39acc06 100644
--- a/contracts/mockup/FeeSharingCollectorMockup.sol
+++ b/contracts/mockup/FeeSharingCollectorMockup.sol
@@ -53,12 +53,12 @@ contract FeeSharingCollectorMockup is FeeSharingCollector {
return _getEndOfRange(0, _token, 0);
}
- function getRBTCBalance(
+ function getNativeTokenBalance(
address _token,
address _user,
uint32 _maxCheckpoints
) public view returns (uint256 _tokenAmount, uint256 _endToken) {
- return _getRBTCBalance(_token, _user, _maxCheckpoints);
+ return _getNativeTokenBalance(_token, _user, _maxCheckpoints);
}
function testWithdrawReentrancy(
diff --git a/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol b/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol
new file mode 100644
index 000000000..c0f379db3
--- /dev/null
+++ b/contracts/mockup/FeeSharingCollectorMultipleTokenMockup.sol
@@ -0,0 +1,42 @@
+pragma solidity ^0.5.17;
+
+import "../governance/FeeSharingCollector/FeeSharingCollectorMultiToken.sol";
+
+contract FeeSharingCollectorMultiTokenMockup is FeeSharingCollectorMultiToken {
+ struct TestData {
+ address loanPoolToken;
+ uint32 maxCheckpoints;
+ address receiver;
+ }
+
+ TestData public testData;
+
+ constructor(IProtocol _protocol, IStaking _staking) public {
+ protocol = _protocol;
+ staking = _staking;
+ }
+
+ function withdraw(address _token, uint32 _maxCheckpoint, address _receiver) public {
+ testData = TestData(_token, _maxCheckpoint, _receiver);
+ }
+
+ function trueWithdraw(address _token, uint32 _maxCheckpoint, address _receiver) public {
+ super.withdraw(_token, _maxCheckpoint, _receiver);
+ }
+
+ function trueWithdrawTokens(
+ address[] memory _tokens,
+ uint32[] memory _maxCheckpoints,
+ address _receiver
+ ) public {
+ super.withdrawTokens(_tokens, _maxCheckpoints, _receiver);
+ }
+
+ function addCheckPoint(address loanPoolToken, uint256 poolTokenAmount) public {
+ uint96 amount96 = safe96(
+ poolTokenAmount,
+ "FeeSharingProxy::withdrawFees: pool token amount exceeds 96 bits"
+ );
+ _addCheckpoint(loanPoolToken, amount96);
+ }
+}
diff --git a/contracts/mockup/MockSovrynLBFactory.sol b/contracts/mockup/MockSovrynLBFactory.sol
new file mode 100644
index 000000000..fef4ecf47
--- /dev/null
+++ b/contracts/mockup/MockSovrynLBFactory.sol
@@ -0,0 +1,34 @@
+pragma solidity 0.5.17;
+
+import "../interfaces/IERC20.sol";
+import "../governance/IFeeSharingCollectorMultiToken.sol";
+
+contract MockSovrynLBFactory {
+ address feeRecipient;
+ uint256 totalPairs;
+ address[] lbPairs;
+
+ constructor() public {}
+
+ function getFeeRecipient() public returns (address) {
+ return feeRecipient;
+ }
+
+ function setFeeRecipient(address _feeRecipient) public {
+ feeRecipient = _feeRecipient;
+ }
+
+ function addLbPairs(uint256 _totalPairs, address[] memory _lbPairs) public {
+ require(_totalPairs == _lbPairs.length, "mismatch lbPairs length");
+ totalPairs = _totalPairs;
+ lbPairs = _lbPairs;
+ }
+
+ function getNumberOfLBPairs() public view returns (uint256) {
+ return totalPairs;
+ }
+
+ function getLBPairAtIndex(uint256 _id) public view returns (address) {
+ return lbPairs[_id];
+ }
+}
diff --git a/contracts/mockup/MockSovrynLBPair.sol b/contracts/mockup/MockSovrynLBPair.sol
new file mode 100644
index 000000000..80423fb2a
--- /dev/null
+++ b/contracts/mockup/MockSovrynLBPair.sol
@@ -0,0 +1,53 @@
+pragma solidity 0.5.17;
+
+import "../interfaces/IERC20.sol";
+import "../governance/IFeeSharingCollectorMultiToken.sol";
+import "../interfaces/ISovrynLBFactoryDex.sol";
+
+contract MockSovrynLBPair {
+ address lbFactory;
+ address tokenX;
+ address tokenY;
+
+ uint128 amountTokenX;
+ uint128 amountTokenY;
+
+ modifier onlyFeeRecipient() {
+ require(
+ ISovrynLBFactoryDex(lbFactory).getFeeRecipient() == msg.sender,
+ "Only feeRecipient"
+ );
+ _;
+ }
+
+ function getTokenX() public returns (address _tokenX) {
+ return tokenX;
+ }
+
+ function getTokenY() public returns (address _tokenY) {
+ return tokenY;
+ }
+
+ constructor(
+ address _lbFactory,
+ address _tokenX,
+ address _tokenY,
+ uint128 _amountTokenX,
+ uint128 _amountTokenY
+ ) public {
+ tokenX = _tokenX;
+ tokenY = _tokenY;
+ amountTokenX = _amountTokenX;
+ amountTokenY = _amountTokenY;
+ lbFactory = _lbFactory;
+ }
+
+ function getProtocolFees() public view returns (uint128 protocolFeeX, uint128 protocolFeeY) {
+ return (amountTokenX, amountTokenY);
+ }
+
+ function collectProtocolFees() public onlyFeeRecipient returns (bytes32) {
+ IERC20(tokenX).transfer(msg.sender, amountTokenX);
+ IERC20(tokenY).transfer(msg.sender, amountTokenY);
+ }
+}
diff --git a/contracts/testhelpers/TestWrappedNativeToken.sol b/contracts/testhelpers/TestWrappedNativeToken.sol
new file mode 100644
index 000000000..acbb5e729
--- /dev/null
+++ b/contracts/testhelpers/TestWrappedNativeToken.sol
@@ -0,0 +1,775 @@
+// Copyright (C) 2015, 2016, 2017 Dapphub
+
+// 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 .
+
+pragma solidity 0.5.17;
+
+contract TestWrappedNativeToken {
+ string public name = "Wrapped Native Token";
+ string public symbol = "WNT";
+ uint8 public decimals = 18;
+
+ event Approval(address indexed src, address indexed guy, uint256 wad);
+ event Transfer(address indexed src, address indexed dst, uint256 wad);
+ event Deposit(address indexed dst, uint256 wad);
+ event Withdrawal(address indexed src, uint256 wad);
+
+ mapping(address => uint256) public balanceOf;
+ mapping(address => mapping(address => uint256)) public allowance;
+
+ function() external payable {
+ deposit();
+ }
+
+ function deposit() public payable {
+ balanceOf[msg.sender] += msg.value;
+ emit Deposit(msg.sender, msg.value);
+ }
+
+ function withdraw(uint256 wad) public {
+ require(balanceOf[msg.sender] >= wad);
+ balanceOf[msg.sender] -= wad;
+ msg.sender.transfer(wad);
+ emit Withdrawal(msg.sender, wad);
+ }
+
+ function totalSupply() public view returns (uint256) {
+ return address(this).balance;
+ }
+
+ function approve(address guy, uint256 wad) public returns (bool) {
+ allowance[msg.sender][guy] = wad;
+ emit Approval(msg.sender, guy, wad);
+ return true;
+ }
+
+ function transfer(address dst, uint256 wad) public returns (bool) {
+ return transferFrom(msg.sender, dst, wad);
+ }
+
+ function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
+ require(balanceOf[src] >= wad);
+
+ if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) {
+ require(allowance[src][msg.sender] >= wad);
+ allowance[src][msg.sender] -= wad;
+ }
+
+ balanceOf[src] -= wad;
+ balanceOf[dst] += wad;
+
+ emit Transfer(src, dst, wad);
+
+ return true;
+ }
+
+ /**
+ * added for local swap implementation
+ * */
+ function mint(address _to, uint256 _value) public {
+ require(_to != address(0), "no burn allowed");
+ balanceOf[_to] = balanceOf[_to] + _value;
+ emit Transfer(address(0), _to, _value);
+ }
+
+ /**
+ * added for local swap implementation
+ * */
+ function burn(address _who, uint256 _value) public {
+ require(_value <= balanceOf[_who], "balance too low");
+ // no need to require _value <= totalSupply, since that would imply the
+ // sender's balance is greater than the totalSupply, which *should* be an assertion failure
+
+ balanceOf[_who] = balanceOf[_who] - _value;
+ emit Transfer(_who, address(0), _value);
+ }
+}
+
+/*
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
+
+*/
diff --git a/contracts/testhelpers/WrappedNativeToken.sol b/contracts/testhelpers/WrappedNativeToken.sol
new file mode 100644
index 000000000..f2f960445
--- /dev/null
+++ b/contracts/testhelpers/WrappedNativeToken.sol
@@ -0,0 +1,754 @@
+// Copyright (C) 2015, 2016, 2017 Dapphub
+
+// 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 .
+
+pragma solidity 0.5.17;
+
+contract WrappedNativeToken {
+ string public name = "Wrapped Native Token";
+ string public symbol = "WNT";
+ uint8 public decimals = 18;
+
+ event Approval(address indexed src, address indexed guy, uint256 wad);
+ event Transfer(address indexed src, address indexed dst, uint256 wad);
+ event Deposit(address indexed dst, uint256 wad);
+ event Withdrawal(address indexed src, uint256 wad);
+
+ mapping(address => uint256) public balanceOf;
+ mapping(address => mapping(address => uint256)) public allowance;
+
+ function() external payable {
+ deposit();
+ }
+
+ function deposit() public payable {
+ balanceOf[msg.sender] += msg.value;
+ emit Deposit(msg.sender, msg.value);
+ }
+
+ function withdraw(uint256 wad) public {
+ require(balanceOf[msg.sender] >= wad);
+ balanceOf[msg.sender] -= wad;
+ msg.sender.transfer(wad);
+ emit Withdrawal(msg.sender, wad);
+ }
+
+ function totalSupply() public view returns (uint256) {
+ return address(this).balance;
+ }
+
+ function approve(address guy, uint256 wad) public returns (bool) {
+ allowance[msg.sender][guy] = wad;
+ emit Approval(msg.sender, guy, wad);
+ return true;
+ }
+
+ function transfer(address dst, uint256 wad) public returns (bool) {
+ return transferFrom(msg.sender, dst, wad);
+ }
+
+ function transferFrom(address src, address dst, uint256 wad) public returns (bool) {
+ require(balanceOf[src] >= wad);
+
+ if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) {
+ require(allowance[src][msg.sender] >= wad);
+ allowance[src][msg.sender] -= wad;
+ }
+
+ balanceOf[src] -= wad;
+ balanceOf[dst] += wad;
+
+ emit Transfer(src, dst, wad);
+
+ return true;
+ }
+}
+
+/*
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
+
+*/
diff --git a/deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json b/deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json
new file mode 100644
index 000000000..a704097ee
--- /dev/null
+++ b/deployment/deployments/rskSovrynMainnet/LoanToken_iNativeToken.json
@@ -0,0 +1,1517 @@
+{
+ "_format": "hh-sol-artifact-1",
+ "contractName": "LoanTokenLogicWrbtc",
+ "sourceName": "contracts/connectors/loantoken/modules/beaconLogicWRBTC/LoanTokenLogicWrbtc.sol",
+ "address":"0xa9DcDC63eaBb8a2b6f39D7fF9429d88340044a7A",
+ "implementation":"0xB6D6Ed584240308b6CF2f654aA6027A35926e129",
+ "abi": [
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "valueBefore",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "valueAfter",
+ "type": "uint256"
+ }
+ ],
+ "name": "AllowanceUpdate",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "burner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "tokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "assetAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "name": "Burn",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "borrower",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "loanToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "loanAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "FlashBorrow",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "minter",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "tokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "assetAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "name": "Mint",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "TINY_AMOUNT",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "VERSION",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "admin",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "assetBalanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "avgBorrowInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "baseRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "loanId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "withdrawAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "initialLoanDuration",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "borrower",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "name": "borrow",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "borrowInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "burnAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "burn",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "loanAmountPaid",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "burnAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "useLM",
+ "type": "bool"
+ }
+ ],
+ "name": "burnToBTC",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "loanAmountPaid",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "assetBorrow",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "assetSupply",
+ "type": "uint256"
+ }
+ ],
+ "name": "calculateSupplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "minEntryPrice",
+ "type": "uint256"
+ }
+ ],
+ "name": "checkPriceDivergence",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_user",
+ "type": "address"
+ }
+ ],
+ "name": "checkpointPrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "checkpointSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "earlyAccessToken",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "depositAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "initialLoanDuration",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getBorrowAmountForDeposit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "borrowAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "borrowAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "initialLoanDuration",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getDepositAmountForBorrow",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "depositAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getEstimatedMarginDetails",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "principal",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateral",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "interestRate",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getListFunctionSignatures",
+ "outputs": [
+ {
+ "internalType": "bytes4[]",
+ "name": "functionSignatures",
+ "type": "bytes4[]"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "moduleName",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getMaxEscrowAmount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "maxEscrowAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "initialPrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "isOwner",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "kinkLevel",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "liquidityMiningAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "loanParamsIds",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "loanTokenAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "lowUtilBaseRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "lowUtilRateMultiplier",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "loanId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "trader",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "minEntryPrice",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "loanDataBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "marginTrade",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "loanId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "trader",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "minEntryPrice",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "affiliateReferrer",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "loanDataBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "marginTradeAffiliate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "marketLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "maxScaleRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "mint",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "mintAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "bool",
+ "name": "useLM",
+ "type": "bool"
+ }
+ ],
+ "name": "mintWithBTC",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "mintAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "borrowAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "nextBorrowInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "supplyAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "nextSupplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "pauser",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "user",
+ "type": "address"
+ }
+ ],
+ "name": "profitOf",
+ "outputs": [
+ {
+ "internalType": "int256",
+ "name": "",
+ "type": "int256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "rateMultiplier",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "sovrynContractAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "string",
+ "name": "source",
+ "type": "string"
+ }
+ ],
+ "name": "stringToBytes32",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "result",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "supplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "targetLevel",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "target_",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "tokenPrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalAssetBorrow",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalAssetSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "assetSupply",
+ "type": "uint256"
+ }
+ ],
+ "name": "totalSupplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "transactionLimit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "wrbtcTokenAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+ ],
+ "bytecode": "0x6080604052600160009081556200001e6001600160e01b036200007216565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35062000076565b3390565b615fd080620000866000396000f3fe6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040",
+ "deployedBytecode": "0x6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040",
+ "linkReferences": {},
+ "deployedLinkReferences": {}
+}
diff --git a/deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json b/deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json
new file mode 100644
index 000000000..f190de69c
--- /dev/null
+++ b/deployment/deployments/rskSovrynTestnet/LoanToken_iNativeToken.json
@@ -0,0 +1,1517 @@
+{
+ "_format": "hh-sol-artifact-1",
+ "contractName": "LoanTokenLogicWrbtc",
+ "sourceName": "contracts/connectors/loantoken/modules/beaconLogicWRBTC/LoanTokenLogicWrbtc.sol",
+ "address":"0xe67Fe227e0504e8e96A34C3594795756dC26e14B",
+ "implementation":"0xACD9fe12Ee54f4a9e4EF6a08FF9ae9a5E46bC805",
+ "abi": [
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "valueBefore",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "valueAfter",
+ "type": "uint256"
+ }
+ ],
+ "name": "AllowanceUpdate",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "burner",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "tokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "assetAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "name": "Burn",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "borrower",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "target",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "address",
+ "name": "loanToken",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "loanAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "FlashBorrow",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "minter",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "tokenAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "assetAmount",
+ "type": "uint256"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "name": "Mint",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "previousOwner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "OwnershipTransferred",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "TINY_AMOUNT",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "VERSION",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "admin",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "assetBalanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "avgBorrowInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "baseRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "loanId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "withdrawAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "initialLoanDuration",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "borrower",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "name": "borrow",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "borrowInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "burnAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "burn",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "loanAmountPaid",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "burnAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bool",
+ "name": "useLM",
+ "type": "bool"
+ }
+ ],
+ "name": "burnToBTC",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "loanAmountPaid",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "assetBorrow",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "assetSupply",
+ "type": "uint256"
+ }
+ ],
+ "name": "calculateSupplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "minEntryPrice",
+ "type": "uint256"
+ }
+ ],
+ "name": "checkPriceDivergence",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_user",
+ "type": "address"
+ }
+ ],
+ "name": "checkpointPrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "checkpointSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "earlyAccessToken",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "depositAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "initialLoanDuration",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getBorrowAmountForDeposit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "borrowAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "borrowAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "initialLoanDuration",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getDepositAmountForBorrow",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "depositAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ }
+ ],
+ "name": "getEstimatedMarginDetails",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "principal",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateral",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "interestRate",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "getListFunctionSignatures",
+ "outputs": [
+ {
+ "internalType": "bytes4[]",
+ "name": "functionSignatures",
+ "type": "bytes4[]"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "moduleName",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "getMaxEscrowAmount",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "maxEscrowAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "initialPrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "isOwner",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "kinkLevel",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "liquidityMiningAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "name": "loanParamsIds",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "loanTokenAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "lowUtilBaseRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "lowUtilRateMultiplier",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "loanId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "trader",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "minEntryPrice",
+ "type": "uint256"
+ },
+ {
+ "internalType": "bytes",
+ "name": "loanDataBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "marginTrade",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "bytes32",
+ "name": "loanId",
+ "type": "bytes32"
+ },
+ {
+ "internalType": "uint256",
+ "name": "leverageAmount",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "loanTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "collateralTokenSent",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "collateralTokenAddress",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "trader",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "minEntryPrice",
+ "type": "uint256"
+ },
+ {
+ "internalType": "address",
+ "name": "affiliateReferrer",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes",
+ "name": "loanDataBytes",
+ "type": "bytes"
+ }
+ ],
+ "name": "marginTradeAffiliate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ },
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "marketLiquidity",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "maxScaleRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "depositAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "mint",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "mintAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "receiver",
+ "type": "address"
+ },
+ {
+ "internalType": "bool",
+ "name": "useLM",
+ "type": "bool"
+ }
+ ],
+ "name": "mintWithBTC",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "mintAmount",
+ "type": "uint256"
+ }
+ ],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "borrowAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "nextBorrowInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "supplyAmount",
+ "type": "uint256"
+ }
+ ],
+ "name": "nextSupplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "owner",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "pauser",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "user",
+ "type": "address"
+ }
+ ],
+ "name": "profitOf",
+ "outputs": [
+ {
+ "internalType": "int256",
+ "name": "",
+ "type": "int256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "rateMultiplier",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "sovrynContractAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "string",
+ "name": "source",
+ "type": "string"
+ }
+ ],
+ "name": "stringToBytes32",
+ "outputs": [
+ {
+ "internalType": "bytes32",
+ "name": "result",
+ "type": "bytes32"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "pure",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "supplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "targetLevel",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "target_",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "tokenPrice",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "price",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalAssetBorrow",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalAssetSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "assetSupply",
+ "type": "uint256"
+ }
+ ],
+ "name": "totalSupplyInterestRate",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "transactionLimit",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "newOwner",
+ "type": "address"
+ }
+ ],
+ "name": "transferOwnership",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "wrbtcTokenAddress",
+ "outputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+ ],
+ "bytecode": "0x6080604052600160009081556200001e6001600160e01b036200007216565b600180546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a35062000076565b3390565b615fd080620000866000396000f3fe6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040",
+ "deployedBytecode": "0x6080604052600436106103a25760003560e01c80637b7933b4116101e7578063b9fe1a8f1161010d578063eebc5081116100a0578063f851a4401161006f578063f851a440146109cd578063f8dd4f0e146109e2578063fb5f83df146109f7578063ffa1ad7414610a0a576103a2565b8063eebc508114610965578063ef2b0b3914610985578063f2fde38b1461099a578063f6b69f99146109ba576103a2565b8063d65a5021116100dc578063d65a5021146108f0578063d759dbeb14610910578063dd62ed3e14610925578063e41b07e314610945576103a2565b8063b9fe1a8f14610886578063ba0e43bf146108a6578063ca37e666146108bb578063cfb51928146108d0576103a2565b80638f32d59b116101855780639bda3a98116101545780639bda3a981461081c5780639dc29fac146108315780639fd0506d14610851578063a9059cbb14610866576103a2565b80638f32d59b146107ba5780638fb807c5146107cf57806390967de5146107e457806395d89b4114610807576103a2565b8063829b38f4116101c1578063829b38f41461075b5780638325a1c01461077b5780638da5cb5b146107905780638ee6c4e6146107a5576103a2565b80637b7933b41461071c5780637e37c08c146107315780637ff9b59614610746576103a2565b80632ea295fa116102cc57806354198ce91161026a5780636b40cd40116102395780636b40cd40146106985780636d23d1ac146106c757806370a08231146106e7578063797bf38514610707576103a2565b806354198ce91461062e57806356e07d701461064e578063612ef80b14610663578063631a3ef814610678576103a2565b80633291c11a116102a65780633291c11a146105c4578063330691ac146105e457806340c10f19146105f957806344a4a00314610619576103a2565b80632ea295fa1461057a5780632f6b600d1461058d578063313ce567146105a2576103a2565b806310e57644116103445780631f68f20a116103135780631f68f20a1461050f57806320f6d07c1461052457806323b872dd1461053957806328a02f1914610559576103a2565b806310e57644146104a357806312416898146104c557806318160ddd146104e55780631d0806ae146104fa576103a2565b806306b3efd61161038057806306b3efd61461041f57806306fdde031461043f578063095ea7b31461046157806309ec6b6b1461048e576103a2565b806304797930146103a75780630506af04146103dd57806306947a3a146103fd575b600080fd5b3480156103b357600080fd5b506103c76103c2366004615012565b610a1f565b6040516103d49190615b84565b60405180910390f35b3480156103e957600080fd5b506103c76103f8366004614c98565b610bc8565b34801561040957600080fd5b50610412610dcc565b6040516103d491906159fd565b34801561042b57600080fd5b506103c761043a366004614b75565b610ddb565b34801561044b57600080fd5b50610454610ec7565b6040516103d49190615c03565b34801561046d57600080fd5b5061048161047c366004614c68565b610f52565b6040516103d49190615b76565b34801561049a57600080fd5b506103c7610fbd565b3480156104af57600080fd5b506104c36104be366004614fa2565b610fd2565b005b3480156104d157600080fd5b506103c76104e0366004614f66565b6110ac565b3480156104f157600080fd5b506103c76110d7565b34801561050657600080fd5b506103c76110dd565b34801561051b57600080fd5b506103c76110e3565b34801561053057600080fd5b506103c76110e9565b34801561054557600080fd5b50610481610554366004614beb565b611178565b61056c610567366004614ea2565b611241565b6040516103d4929190615e14565b61056c610588366004614cf9565b6115d3565b34801561059957600080fd5b5061041261195a565b3480156105ae57600080fd5b506105b7611969565b6040516103d49190615e3d565b3480156105d057600080fd5b506103c76105df366004614f66565b611972565b3480156105f057600080fd5b506103c7611984565b34801561060557600080fd5b506103c7610614366004614c68565b61198a565b34801561062557600080fd5b506103c7611afd565b34801561063a57600080fd5b506103c7610649366004614b75565b611b0f565b34801561065a57600080fd5b506103c7611bb0565b34801561066f57600080fd5b506103c7611bb6565b34801561068457600080fd5b506103c7610693366004615012565b611be7565b3480156106a457600080fd5b506106b86106b3366004615055565b611d87565b6040516103d493929190615e22565b3480156106d357600080fd5b506103c76106e2366004614fc3565b611e98565b3480156106f357600080fd5b506103c7610702366004614b75565b611f7b565b34801561071357600080fd5b50610412611f96565b34801561072857600080fd5b506103c7611faa565b34801561073d57600080fd5b506103c7611fb0565b34801561075257600080fd5b506103c7611fb6565b34801561076757600080fd5b506103c7610776366004614f66565b611ff4565b34801561078757600080fd5b506103c7612074565b34801561079c57600080fd5b50610412612080565b3480156107b157600080fd5b5061041261208f565b3480156107c657600080fd5b5061048161209e565b3480156107db57600080fd5b506103c76120c4565b3480156107f057600080fd5b506107f96120f4565b6040516103d4929190615b56565b34801561081357600080fd5b50610454612780565b34801561082857600080fd5b506104126127db565b34801561083d57600080fd5b506103c761084c366004614c68565b6127ea565b34801561085d57600080fd5b506104126128ec565b34801561087257600080fd5b50610481610881366004614c68565b6128fb565b34801561089257600080fd5b506103c76108a1366004614f66565b61290b565b3480156108b257600080fd5b506103c7612916565b3480156108c757600080fd5b5061041261291c565b3480156108dc57600080fd5b506103c76108eb366004614f31565b61292b565b3480156108fc57600080fd5b506103c761090b366004614f66565b612949565b34801561091c57600080fd5b506103c761295c565b34801561093157600080fd5b506103c7610940366004614bb1565b612962565b34801561095157600080fd5b506103c7610960366004614b75565b61298d565b34801561097157600080fd5b506103c7610980366004614b75565b61299f565b34801561099157600080fd5b506103c76129ba565b3480156109a657600080fd5b506104c36109b5366004614b75565b6129c0565b61056c6109c8366004614dc1565b6129f0565b3480156109d957600080fd5b50610412612ac0565b3480156109ee57600080fd5b506103c7612acf565b6103c7610a05366004614c38565b612ad9565b348015610a1657600080fd5b506103c7612bb1565b60008315610bc1576001600160a01b038216610a44576017546001600160a01b031691505b600060106000846001604051602001610a5e92919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b81529294506001600160a01b039182169363e762319f936101009091049092169187918a91869163ca74a5d991610acf918a9101615b84565b60206040518083038186803b158015610ae757600080fd5b505afa158015610afb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b1f9190810190614f84565b60016040518663ffffffff1660e01b8152600401610b41959493929190615a77565b60206040518083038186803b158015610b5957600080fd5b505afa158015610b6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610b919190810190614f84565b9150610ba582610b9f6120c4565b86612bb6565b9350610bb39150612c2f9050565b821115610bbf57600091505b505b9392505050565b6000600160005414610bf55760405162461bcd60e51b8152600401610bec90615dc4565b60405180910390fd5b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610c869190810190614f84565b90508215610c9e57610c9784612c65565b9150610caa565b610ca784612e0b565b91505b8115610d1c57601754604051632e1a7d4d60e01b81526001600160a01b0390911690632e1a7d4d90610ce0908590600401615b84565b600060405180830381600087803b158015610cfa57600080fd5b505af1158015610d0e573d6000803e3d6000fd5b50505050610d1c8583612fc2565b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015610d6957600080fd5b505afa158015610d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610da19190810190614f84565b8114610dbf5760405162461bcd60e51b8152600401610bec90615c14565b5060016000559392505050565b6016546001600160a01b031681565b601c5460009081906001600160a01b031615610e7657601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690610e239030908790600401615a19565b60206040518083038186803b158015610e3b57600080fd5b505afa158015610e4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e739190810190614f84565b90505b610ebe670de0b6b3a7640000610eb2610e8d611fb6565b610ea685610e9a89611f7b565b9063ffffffff61306316565b9063ffffffff61308816565b9063ffffffff6130c216565b9150505b919050565b6002805460408051602060018416156101000260001901909316849004601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b820191906000526020600020905b815481529060010190602001808311610f2d57829003601f168201915b505050505081565b3360008181526014602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610fab908690615b84565b60405180910390a35060015b92915050565b6000610fcc6104e06000613104565b90505b90565b60165460048054604051631a51577760e21b81526000936001600160a01b03908116936369455ddc93611013936101009091049092169188918a9101615a4f565b60206040518083038186803b15801561102b57600080fd5b505afa15801561103f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506110639190810190614f84565b9050600061108385610eb284670de0b6b3a764000063ffffffff61308816565b9050828110156110a55760405162461bcd60e51b8152600401610bec90615c64565b5050505050565b6000806110b76110e9565b905080156110d1576110c98184611e98565b915050610ec2565b50919050565b60155490565b600e5481565b60055481565b6016546004805460405163250f447f60e11b81526000936001600160a01b0390811693634a1e88fe936111289330936101009092049091169101615a19565b60206040518083038186803b15801561114057600080fd5b505afa158015611154573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610fcc9190810190614f84565b60165460405163115dd4b160e01b8152600091611239918691869186916001600160a01b03169063115dd4b1906111b3903390600401615a0b565b60206040518083038186803b1580156111cb57600080fd5b505afa1580156111df573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112039190810190614cdb565b611230576001600160a01b0388166000908152601460209081526040808320338452909152902054611234565b6000195b61313e565b949350505050565b6000806001600054146112665760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156112bf57600080fd5b505af11580156112d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506112f79190810190614f84565b905061130161334e565b6001600160a01b03871661131e576017546001600160a01b031696505b6004546001600160a01b038881166101009092041614156113515760405162461bcd60e51b8152600401610bec90615cf4565b8a15806113665750336001600160a01b038716145b6113825760405162461bcd60e51b8152600401610bec90615de4565b6001600160a01b038716600090815260126020526040902054156113c5576001600160a01b0387166000908152601260205260409020548811156113c557600080fd5b60045461010090046001600160a01b0316600090815260126020526040902054156114165760045461010090046001600160a01b031660009081526012602052604090205489111561141657600080fd5b6000611423888a8c6133ce565b9050806114425760405162461bcd60e51b8152600401610bec90615d14565b61144a614a23565b611452614a4a565b3082526001600160a01b038916602080840182905260408401919091528101839052606081018c9052608081018b905261148a6135fb565b6114988d82602001516136a1565b82526020820181905260045465e35fa931a000916114c49161010090046001600160a01b0316906136f0565b116114e15760405162461bcd60e51b8152600401610bec90615da4565b6114fb6f4b3b4ca85a86c47a098a2240000000008e6130c2565b60a082018990529c506115148e60008f8d86868d613817565b9550955050505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b505afa15801561157c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506115a09190810190614f84565b81146115be5760405162461bcd60e51b8152600401610bec90615c14565b50600160005590999098509650505050505050565b6000806001600054146115f85760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561165157600080fd5b505af1158015611665573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116899190810190614f84565b9050896116a85760405162461bcd60e51b8152600401610bec90615df4565b6116b061334e565b6001600160a01b038716600090815260126020526040902054156116f3576001600160a01b0387166000908152601260205260409020548811156116f357600080fd5b3415806116ff57508734145b801561171357508715158061171357508a15155b801561173a57506001600160a01b03871615158061173057503415155b8061173a57508a15155b801561175657508a15806117565750336001600160a01b038716145b6117725760405162461bcd60e51b8152600401610bec90615ca4565b6004546001600160a01b038881166101009092041614156117a55760405162461bcd60e51b8152600401610bec90615c24565b6117ad6135fb565b6117b5614a23565b6117bd614a4a565b3082526001600160a01b03888116602080850191909152908816604084015281018c90526117f58c6117ef6000613104565b8d612bb6565b836000018460400185602001838152508381525083815250505050898160800181815250506119078d8d601660009054906101000a90046001600160a01b03166001600160a01b031663ca74a5d9601060008f600160405160200161185b92919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020546040518263ffffffff1660e01b815260040161189f9190615b84565b60206040518083038186803b1580156118b757600080fd5b505afa1580156118cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506118ef9190810190614f84565b8c868660405180602001604052806000815250613817565b94509450505073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561156857600080fd5b6017546001600160a01b031681565b60045460ff1681565b60106020526000908152604090205481565b60065481565b60006001600054146119ae5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015611a0757600080fd5b505af1158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611a3f9190810190614f84565b9050611a4b8484613a74565b91505b73ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b158015611a9b57600080fd5b505afa158015611aaf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611ad39190810190614f84565b8114611af15760405162461bcd60e51b8152600401610bec90615c14565b50600160005592915050565b6000610fcc611b0a6110e9565b613b80565b600080827f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb660001b604051602001611b489291906159a4565b604051602081830303815290604052805190602001209050610ebe8160136000866001600160a01b03166001600160a01b0316815260200190815260200160002054611b92611fb6565b6001600160a01b038716600090815260116020526040902054613bb8565b600a5481565b600080611bc36000613104565b90506000611bcf6110e9565b905080821115611be25790039050610fcf565b505090565b60008315610bc1576000611bfd85610b9f6120c4565b92505050611c09612c2f565b8111610bbf576001600160a01b038316611c2c576017546001600160a01b031692505b600060106000856001604051602001611c4692919061597e565b60408051601f19818403018152918152815160209283012083529082019290925281016000205460165460048054935163ca74a5d960e01b8152929450611d7e93600a936001600160a01b03938416936325decac09361010090930416918a918991869163ca74a5d991611cbc918c9101615b84565b60206040518083038186803b158015611cd457600080fd5b505afa158015611ce8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611d0c9190810190614f84565b60016040518663ffffffff1660e01b8152600401611d2e959493929190615a77565b60206040518083038186803b158015611d4657600080fd5b505afa158015611d5a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610e9a9190810190614f84565b92505050610bc1565b600080806001600160a01b038416611da8576017546001600160a01b031693505b6000611db58587896133ce565b9050611dc188826136a1565b9094509150611dce612c2f565b841115611de5575060009250829150819050611e8e565b611df5878563ffffffff61306316565b6016546004805460405163d67f707760e01b8152939a506001600160a01b039283169363d67f707793611e3a9361010090930416918a918d918d918a918d9101615ab9565b60206040518083038186803b158015611e5257600080fd5b505afa158015611e66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611e8a9190810190614f84565b9250505b9450945094915050565b60008215801590611ea95750828210155b15610fb757611f74701d6329f1c35ca4bfabb9f5610000000000610eb2611f5e68056bc75e2d63100000601660009054906101000a90046001600160a01b03166001600160a01b0316634699f8466040518163ffffffff1660e01b815260040160206040518083038186803b158015611f2157600080fd5b505afa158015611f35573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611f599190810190614f84565b613c12565b610ea6611f6b8888613c54565b610ea689613b80565b9050610fb7565b6001600160a01b031660009081526013602052604090205490565b60045461010090046001600160a01b031681565b600d5481565b60085481565b600f546000908190426001600160581b03908116911614611fdd57611fd9613c86565b9150505b611fee611fe982613104565b613d52565b91505090565b60008061201361016d610eb2601c600b5461308890919063ffffffff16565b9050600061203068056bc75e2d631000008363ffffffff613c1216565b9050600061204d68056bc75e2d63100000610eb284610ea6611bb6565b905061206b85610eb283670de0b6b3a764000063ffffffff61308816565b95945050505050565b6000610fcc6000613d81565b6001546001600160a01b031690565b601c546001600160a01b031681565b6001546000906001600160a01b03166120b5613dd7565b6001600160a01b031614905090565b600f546000908190426001600160581b039081169116146120eb576120e7613c86565b9150505b611fee81613104565b60408051602080825261042082019092526060916000918391808201610400803883390190505090506340c10f1960e01b8160008151811061213257fe5b6001600160e01b0319909216602092830291909101909101528051632770a7eb60e21b908290600190811061216357fe5b6001600160e01b03199092166020928302919091019091015280516317514afd60e11b908290600290811061219457fe5b6001600160e01b03199092166020928302919091019091015280516328a02f1960e01b90829060039081106121c557fe5b6001600160e01b031990921660209283029190910190910152805163f6b69f9960e01b90829060049081106121f657fe5b6001600160e01b031990921660209283029190910190910152805163a9059cbb60e01b908290600590811061222757fe5b6001600160e01b03199092166020928302919091019091015280516323b872dd60e01b908290600690811061225857fe5b6001600160e01b03199092166020928302919091019091015280516354198ce960e01b908290600790811061228957fe5b6001600160e01b0319909216602092830291909101909101528051633ffcdacb60e11b90829060089081106122ba57fe5b6001600160e01b031990921660209283029190910190910152805163eebc508160e01b90829060099081106122eb57fe5b6001600160e01b031990921660209283029190910190910152805163612ef80b60e01b908290600a90811061231c57fe5b6001600160e01b03199092166020928302919091019091015280516344a4a00360e01b908290600b90811061234d57fe5b6001600160e01b031990921660209283029190910190910152805163020c968760e61b908290600c90811061237e57fe5b6001600160e01b031990921660209283029190910190910152805163b9fe1a8f60e01b908290600d9081106123af57fe5b6001600160e01b03199092166020928302919091019091015280516309ec6b6b60e01b908290600e9081106123e057fe5b6001600160e01b031990921660209283029190910190910152805163d65a502160e01b908290600f90811061241157fe5b6001600160e01b03199092166020928302919091019091015280516302482d1360e31b908290601090811061244257fe5b6001600160e01b031990921660209283029190910190910152805163083db41f60e21b908290601190811061247357fe5b6001600160e01b0319909216602092830291909101909101528051638fb807c560e01b90829060129081106124a457fe5b6001600160e01b03199092166020928302919091019091015280516320a6ce3d60e21b90829060139081106124d557fe5b6001600160e01b0319909216602092830291909101909101528051630359f7eb60e11b908290601490811061250657fe5b6001600160e01b03199092166020928302919091019091015280516301ad033560e61b908290601590811061253757fe5b6001600160e01b0319909216602092830291909101909101528051630c6347df60e31b908290601690811061256857fe5b6001600160e01b03199092166020928302919091019091015280516247979360e41b908290601790811061259857fe5b6001600160e01b03199092166020928302919091019091015280516304395d9160e21b90829060189081106125c957fe5b6001600160e01b0319909216602092830291909101909101528051631b48f46b60e21b90829060199081106125fa57fe5b6001600160e01b031990921660209283029190910190910152805163fb5f83df60e01b908290601a90811061262b57fe5b6001600160e01b0319909216602092830291909101909101528051630141abc160e21b908290601b90811061265c57fe5b6001600160e01b031990921660209283029190910190910152805163095ea7b360e01b908290601c90811061268d57fe5b6001600160e01b03199092166020928302919091019091015280516318160ddd60e01b908290601d9081106126be57fe5b6001600160e01b03199092166020928302919091019091015280516370a0823160e01b908290601e9081106126ef57fe5b6001600160e01b0319909216602092830291909101909101528051636eb1769f60e11b908290601f90811061272057fe5b60200260200101906001600160e01b03191690816001600160e01b0319168152505080612777604051806040016040528060138152602001724c6f616e546f6b656e4c6f676963577262746360681b81525061292b565b92509250509091565b6003805460408051602060026001851615610100026000190190941693909304601f81018490048402820184019092528181529291830182828015610f4a5780601f10610f1f57610100808354040283529160200191610f4a565b6018546001600160a01b031681565b600060016000541461280e5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561286757600080fd5b505af115801561287b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061289f9190810190614f84565b90506128aa83612e0b565b91508115611a4e57611a4e600460019054906101000a90046001600160a01b03168584604051806040016040528060018152602001603560f81b815250613ddb565b601b546001600160a01b031681565b6000610bc133848460001961313e565b6000610fb782613d81565b60095481565b601a546001600160a01b031681565b80516000908290612940575060009050610ec2565b50506020015190565b6000610fb76104e083610e9a6000613104565b60075481565b6001600160a01b03918216600090815260146020908152604080832093909416825291909152205490565b60126020526000908152604090205481565b6001600160a01b031660009081526011602052604090205490565b600b5481565b6129c861209e565b6129e45760405162461bcd60e51b8152600401610bec90615d64565b6129ed81613e3b565b50565b6000806001600160a01b03851615612a675760165460405163193bbe8960e31b81526001600160a01b039091169063c9ddf44890612a34908a908990600401615a19565b600060405180830381600087803b158015612a4e57600080fd5b505af1158015612a62573d6000803e3d6000fd5b505050505b612aad8c8c8c8c8c8c8c8b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061124192505050565b915091509a509a98505050505050505050565b6019546001600160a01b031681565b65e35fa931a00081565b6000600160005414612afd5760405162461bcd60e51b8152600401610bec90615dc4565b6002600081905550600073ba10edd6abc7696eae685839217bdcc42139612b6001600160a01b031663ed04e1c36040518163ffffffff1660e01b8152600401602060405180830381600087803b158015612b5657600080fd5b505af1158015612b6a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612b8e9190810190614f84565b90508215612ba757612ba08434613ebd565b9150611a4e565b612ba08434613a74565b600681565b6000806000612bc58686613f46565b9250612c12612bfa670de0b6b3a7640000611f596b0a3098c68eb9427db8000000610eb283610ea68a8c63ffffffff61308816565b610eb288670de0b6b3a764000063ffffffff61308816565b9050612c24818763ffffffff613c1216565b915093509350939050565b600480546040516370a0823160e01b81526000926101009092046001600160a01b0316916370a0823191611128913091016159fd565b601c54604051636822955360e11b815260009182916001600160a01b039091169063d0452aa690612c9c9030903390600401615a34565b60206040518083038186803b158015612cb457600080fd5b505afa158015612cc8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612cec9190810190614f84565b905082612d08612cfb33611f7b565b839063ffffffff61306316565b1015612d265760405162461bcd60e51b8152600401610bec90615cc4565b8015612e065782811015612d9f57601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612d6890309085903390600401615b2e565b600060405180830381600087803b158015612d8257600080fd5b505af1158015612d96573d6000803e3d6000fd5b50505050612e06565b601c54604051631a4ca37b60e21b81526001600160a01b03909116906369328dec90612dd390309087903390600401615b2e565b600060405180830381600087803b158015612ded57600080fd5b505af1158015612e01573d6000803e3d6000fd5b505050505b610ebe835b600081612e2a5760405162461bcd60e51b8152600401610bec90615d74565b612e3333611f7b565b821115612e67576000198214612e5b5760405162461bcd60e51b8152600401610bec90615d34565b612e6433611f7b565b91505b612e6f6135fb565b6000612e7e611fe96000613104565b90506000612e9e670de0b6b3a7640000610eb2868563ffffffff61308816565b90506000612eaa612c2f565b905081935080841115612ecf5760405162461bcd60e51b8152600401610bec90615cd4565b601c546000906001600160a01b031615612f6857601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690612f159030903390600401615a34565b60206040518083038186803b158015612f2d57600080fd5b505afa158015612f41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250612f659190810190614f84565b90505b33600090815260136020526040812054612f88908363ffffffff61306316565b90506000612f9c828963ffffffff613c1216565b9050612faa3389898961405c565b50612fb733838389614192565b505050505050919050565b80471015612fe25760405162461bcd60e51b8152600401610bec90615cb4565b6000826001600160a01b031682604051612ffb906159f2565b60006040518083038185875af1925050503d8060008114613038576040519150601f19603f3d011682016040523d82523d6000602084013e61303d565b606091505b505090508061305e5760405162461bcd60e51b8152600401610bec90615c94565b505050565b600082820183811015610bc15760405162461bcd60e51b8152600401610bec90615c74565b60008261309757506000610fb7565b828202828482816130a457fe5b0414610bc15760405162461bcd60e51b8152600401610bec90615d54565b6000610bc183836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250614248565b6000601554600014610ec257600c548061312e5761312b6131236110e9565b610e9a612c2f565b90505b6110c9818463ffffffff61306316565b600060001982146131d7576040805180820190915260028152610c4d60f21b6020820152613175908390859063ffffffff61427f16565b6001600160a01b038616600081815260146020908152604080832033808552925291829020849055905190927f628e75c63c1873bcd3885f7aee9f58ee36f60dc789b2a6b3a978c4189bc548ba916131ce918791615e14565b60405180910390a35b6001600160a01b0384166131fd5760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b03851660009081526013602090815260408083205481518083019092526002825261189b60f11b92820192909252909190613248908390879063ffffffff61427f16565b6001600160a01b03808916600090815260136020526040808220849055918916815290812054919250613281828863ffffffff61306316565b6001600160a01b03891660009081526013602052604081208290559091506132a7611fb6565b601c549091506001600160a01b038b81169116148015906132d65750601c546001600160a01b038a8116911614155b156132f3576132e78a868684614192565b6132f389848484614192565b886001600160a01b03168a6001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8a6040516133369190615b84565b60405180910390a35060019998505050505050505050565b600080356001600160e01b0319167fd46a704bc285dbd6ff5ad3863506260b1df02812f4f857c8cc852317a6ac64f260405160200161338e9291906159ca565b60405160208183030381529060405280519060200120905060008154905080156133ca5760405162461bcd60e51b8152600401610bec90615d64565b5050565b808215610bc157600080601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561342657600080fd5b505afa15801561343a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061345e9190810190614b93565b60048054604051630a7549df60e21b81526001600160a01b03938416936329d5277c93613497938c936101009091049092169101615a19565b604080518083038186803b1580156134ae57600080fd5b505afa1580156134c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506134e69190810190614fe2565b91509150816000141580156134fa57508015155b6135165760405162461bcd60e51b8152600401610bec90615db4565b600061352c82610eb2888663ffffffff61308816565b60165460048054604051631a51577760e21b81529394506000936001600160a01b03938416936369455ddc9361356f936101009004909116918d91889101615a4f565b60206040518083038186803b15801561358757600080fd5b505afa15801561359b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506135bf9190810190614f84565b90508681146135df576135dc87610eb2848463ffffffff61308816565b91505b6135ef828663ffffffff61306316565b98975050505050505050565b600f5442906001600160581b038083169116146129ed5760165460048054604051630740ff7d60e51b81526001600160a01b039384169363e81fefa09361364b93610100900490911691016159fd565b600060405180830381600087803b15801561366557600080fd5b505af1158015613679573d6000803e3d6000fd5b5050600f80546001600160581b0385166affffffffffffffffffffff19909116179055505050565b600080806136c1670de0b6b3a7640000610eb2868863ffffffff61308816565b90506136d6816136d16000613104565b613f46565b91506136e6826224ea00836142ab565b9250509250929050565b6000806000601660009054906101000a90046001600160a01b03166001600160a01b03166378d849ed6040518163ffffffff1660e01b815260040160206040518083038186803b15801561374357600080fd5b505afa158015613757573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061377b9190810190614b93565b601754604051630a7549df60e21b81526001600160a01b03928316926329d5277c926137af928a9290911690600401615a19565b604080518083038186803b1580156137c657600080fd5b505afa1580156137da573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506137fe9190810190614fe2565b909250905061206b81610eb2868563ffffffff61308816565b60008061382261334e565b61382a612c2f565b846020015111158015613849575060208501516001600160a01b031615155b6138655760405162461bcd60e51b8152600401610bec90615ce4565b60408501516001600160a01b031661388b5760208501516001600160a01b031660408601525b60006138998787878c61430c565b90506138b68560200151866060015161306390919063ffffffff16565b606086015288156138dc5760608501516138d6908a63ffffffff613c1216565b60608601525b600089156138e8575060015b6000601060008a8460405160200161390192919061597e565b6040516020818303038152906040528051906020012060001c8152602001908152602001600020549050601660009054906101000a90046001600160a01b03166001600160a01b031663d84ca25484838f868f8e8e8e6040518963ffffffff1660e01b81526004016139799796959493929190615b92565b60408051808303818588803b15801561399157600080fd5b505af11580156139a5573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052506139ca9190810190614fe2565b6080890152602088018190526139f25760405162461bcd60e51b8152600401610bec90615d24565b601654602089015160405163f06a9c6b60e01b81526001600160a01b039092169163f06a9c6b91613a25916004016159fd565b600060405180830381600087803b158015613a3f57600080fd5b505af1158015613a53573d6000803e3d6000fd5b50505050866020015187608001519450945050505097509795505050505050565b600080613a8083614566565b601c5491935091506000906001600160a01b031615613b1e57601c54604051636822955360e11b81526001600160a01b039091169063d0452aa690613acb9030908990600401615a19565b60206040518083038186803b158015613ae357600080fd5b505afa158015613af7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613b1b9190810190614f84565b90505b6001600160a01b038516600090815260136020526040812054613b47908363ffffffff61306316565b90506000613b5b828663ffffffff61306316565b9050613b6987868887614670565b50613b7687838387614192565b5050505092915050565b60008115610ec2576000613b92613c86565b5090506110c983610eb261016d610ea68568056bc75e2d6310000063ffffffff61308816565b600081613bc757506000611239565b50835461206b81613c06670de0b6b3a7640000613bfa88613bee898963ffffffff61478016565b9063ffffffff6147c616565b9063ffffffff61483116565b9063ffffffff61489516565b6000610bc183836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f77000081525061427f565b60008215801590613c6457508115155b15610fb757611f7482610eb28568056bc75e2d6310000063ffffffff61308816565b60165460048054604051630d1979fb60e41b8152600093849384936001600160a01b039283169363d1979fb093613cc893309361010090049091169101615a19565b60c06040518083038186803b158015613ce057600080fd5b505afa158015613cf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250613d1891908101906150b6565b5091965094509250613d4b915068056bc75e2d631000009050610eb2613d3e8285613c12565b859063ffffffff61308816565b9150509091565b60155460009080613d6557600e54610ebe565b610ebe81610eb285670de0b6b3a764000063ffffffff61308816565b6000808215613dca57600f54426001600160581b03908116911614613dac57613da8613c86565b9150505b6000613dba82610e9a612c2f565b905080841115613dc8578093505b505b610ebe836136d183613104565b3390565b604051613e3590859063a9059cbb60e01b90613dfd9087908790602401615b13565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152836148db565b50505050565b6001600160a01b038116613e615760405162461bcd60e51b8152600401610bec90615c54565b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000613ec98383613a74565b601c54909150613ee59084906001600160a01b0316838061313e565b50601c546040516336305cf160e21b81526001600160a01b039091169063d8c173c490613f189086908590600401615b13565b600060405180830381600087803b158015613f3257600080fd5b505af1158015613b76573d6000803e3d6000fd5b600080613f5e613f5885610e9a6110e9565b84613c54565b600554600654600954600a54600b5494955060009485949392919082881015613f85578297505b81881115613ff857968190039668056bc75e2d6310000082900380891115613fab578098505b613fcc86610e9a68056bc75e2d63100000610eb2878a63ffffffff61308816565b9650613ff087610e9a83610eb2613fe3878d613c12565b8e9063ffffffff61308816565b99505061404e565b61401985610e9a68056bc75e2d63100000610eb28c8963ffffffff61308816565b98509395508593614030848663ffffffff61306316565b9550868910156140425786985061404e565b8589111561404e578598505b505050505050505092915050565b6040805180820182526002815261189b60f11b6020808301919091526001600160a01b038716600090815260139091529182205482916140a49190879063ffffffff61427f16565b9050600a81116140c5576140be858263ffffffff61306316565b9450600090505b6001600160a01b03861660009081526013602052604090208190556015546140f3908663ffffffff613c1216565b6015556040516001600160a01b038716907f743033787f4738ff4d6a7225ce2bd0977ee5f86b91a902a58f5e4d0b297b46449061413590889088908890615e22565b60405180910390a260006001600160a01b0316866001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60405180910390a395945050505050565b6040516000906141c89086907f37aa2b7d583612f016e4a4de4292cb015139b3d7762663d06a53964912ea2fb6906020016159a4565b604051602081830303815290604052805190602001209050600083600014156141f45760009250614225565b8415614225576001600160a01b03861660009081526011602052604090205461422290839087908690613bb8565b90505b90556001600160a01b039093166000908152601160205260409020929092555050565b600081836142695760405162461bcd60e51b8152600401610bec9190615c03565b50600083858161427557fe5b0495945050505050565b600081848411156142a35760405162461bcd60e51b8152600401610bec9190615c03565b505050900390565b6000806142c66301e13380610eb2878763ffffffff61308816565b905060006142e368056bc75e2d631000008363ffffffff613c1216565b905061430281610eb28668056bc75e2d6310000063ffffffff61308816565b9695505050505050565b60175460408401516020840151606085015160808601516000946001600160a01b039081169485949093909290918b1685141561435b5760405162461bcd60e51b8152600401610bec90615d84565b349650871561440657604051632e1a7d4d60e01b81526001600160a01b03871690632e1a7d4d90614390908b90600401615b84565b600060405180830381600087803b1580156143aa57600080fd5b505af11580156143be573d6000803e3d6000fd5b505050506143cc8489612fc2565b87831115614401576016546040805160208101909152600081526144019187916001600160a01b03909116908b870390613ddb565b61443b565b601654604080518082019091526002815261323760f01b602082015261443b9187916001600160a01b03909116908690613ddb565b801561447657601654604080518082019091526002815261064760f31b6020820152614476918d9133916001600160a01b03169085906149c6565b811561455857861580159061448b5750818710155b1561452357856001600160a01b031663d0e30db0836040518263ffffffff1660e01b81526004016000604051808303818588803b1580156144cb57600080fd5b505af11580156144df573d6000803e3d6000fd5b5050601654604080518082019091526002815261323960f01b602082015261451994508993506001600160a01b0390911691508590613ddb565b8187039650614558565b601654604080518082019091526002815261323960f01b602082015261455891879133916001600160a01b03169086906149c6565b505050505050949350505050565b600080826145865760405162461bcd60e51b8152600401610bec90615d44565b61458e6135fb565b61459b611fe96000613104565b90506145b981610eb285670de0b6b3a764000063ffffffff61308816565b915034614601576145fc600460019054906101000a90046001600160a01b031633308660405180604001604052806002815260200161062760f31b8152506149c6565b61466b565b601760009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0846040518263ffffffff1660e01b81526004016000604051808303818588803b15801561465157600080fd5b505af1158015614665573d6000803e3d6000fd5b50505050505b915091565b60006001600160a01b0385166146985760405162461bcd60e51b8152600401610bec90615c34565b6001600160a01b0385166000908152601360205260408120546146c1908663ffffffff61306316565b6001600160a01b03871660009081526013602052604090208190556015549091506146f2908663ffffffff61306316565b6015556040516001600160a01b038716907fb4c03061fb5b7fed76389d5af8f2e0ddb09f8c70d1333abbb62582835e10accb9061473490889088908890615e22565b60405180910390a2856001600160a01b031660006001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516141819190615b84565b60008183038183128015906147955750838113155b806147aa57506000831280156147aa57508381135b610bc15760405162461bcd60e51b8152600401610bec90615dd4565b6000826147d557506000610fb7565b826000191480156147e95750600160ff1b82145b156148065760405162461bcd60e51b8152600401610bec90615d94565b8282028284828161481357fe5b0514610bc15760405162461bcd60e51b8152600401610bec90615d94565b6000816148505760405162461bcd60e51b8152600401610bec90615e04565b816000191480156148645750600160ff1b83145b156148815760405162461bcd60e51b8152600401610bec90615d04565b600082848161488c57fe5b05949350505050565b60008282018183128015906148aa5750838112155b806148bf57506000831280156148bf57508381125b610bc15760405162461bcd60e51b8152600401610bec90615c84565b6148e4836149ea565b6149005760405162461bcd60e51b8152600401610bec90615c44565b60006060846001600160a01b03168460405161491c91906159e6565b6000604051808303816000865af19150503d8060008114614959576040519150601f19603f3d011682016040523d82523d6000602084013e61495e565b606091505b50915091508183906149835760405162461bcd60e51b8152600401610bec9190615c03565b508051156110a5578080602001905161499f9190810190614cdb565b83906149be5760405162461bcd60e51b8152600401610bec9190615c03565b505050505050565b6040516110a59086906323b872dd60e01b90613dfd90889088908890602401615a4f565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470818114801590611239575050151592915050565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8035610fb781615f67565b8051610fb781615f67565b8035610fb781615f7b565b8051610fb781615f7b565b8035610fb781615f84565b60008083601f840112614adf57600080fd5b50813567ffffffffffffffff811115614af757600080fd5b602083019150836001820283011115614b0f57600080fd5b9250929050565b600082601f830112614b2757600080fd5b8135614b3a614b3582615e72565b615e4b565b91508082526020830160208301858383011115614b5657600080fd5b614b61838284615eed565b50505092915050565b8051610fb781615f84565b600060208284031215614b8757600080fd5b60006112398484614a96565b600060208284031215614ba557600080fd5b60006112398484614aa1565b60008060408385031215614bc457600080fd5b6000614bd08585614a96565b9250506020614be185828601614a96565b9150509250929050565b600080600060608486031215614c0057600080fd5b6000614c0c8686614a96565b9350506020614c1d86828701614a96565b9250506040614c2e86828701614ac2565b9150509250925092565b60008060408385031215614c4b57600080fd5b6000614c578585614a96565b9250506020614be185828601614aac565b60008060408385031215614c7b57600080fd5b6000614c878585614a96565b9250506020614be185828601614ac2565b600080600060608486031215614cad57600080fd5b6000614cb98686614a96565b9350506020614cca86828701614ac2565b9250506040614c2e86828701614aac565b600060208284031215614ced57600080fd5b60006112398484614ab7565b600080600080600080600080610100898b031215614d1657600080fd5b6000614d228b8b614ac2565b9850506020614d338b828c01614ac2565b9750506040614d448b828c01614ac2565b9650506060614d558b828c01614ac2565b9550506080614d668b828c01614a96565b94505060a0614d778b828c01614a96565b93505060c0614d888b828c01614a96565b92505060e089013567ffffffffffffffff811115614da557600080fd5b614db18b828c01614b16565b9150509295985092959890939650565b6000806000806000806000806000806101208b8d031215614de157600080fd5b6000614ded8d8d614ac2565b9a50506020614dfe8d828e01614ac2565b9950506040614e0f8d828e01614ac2565b9850506060614e208d828e01614ac2565b9750506080614e318d828e01614a96565b96505060a0614e428d828e01614a96565b95505060c0614e538d828e01614ac2565b94505060e0614e648d828e01614a96565b9350506101008b013567ffffffffffffffff811115614e8257600080fd5b614e8e8d828e01614acd565b92509250509295989b9194979a5092959850565b600080600080600080600080610100898b031215614ebf57600080fd5b6000614ecb8b8b614ac2565b9850506020614edc8b828c01614ac2565b9750506040614eed8b828c01614ac2565b9650506060614efe8b828c01614ac2565b9550506080614f0f8b828c01614a96565b94505060a0614f208b828c01614a96565b93505060c0614d888b828c01614ac2565b600060208284031215614f4357600080fd5b813567ffffffffffffffff811115614f5a57600080fd5b61123984828501614b16565b600060208284031215614f7857600080fd5b60006112398484614ac2565b600060208284031215614f9657600080fd5b60006112398484614b6a565b600080600060608486031215614fb757600080fd5b6000614c0c8686614ac2565b60008060408385031215614fd657600080fd5b6000614c878585614ac2565b60008060408385031215614ff557600080fd5b60006150018585614b6a565b9250506020614be185828601614b6a565b60008060006060848603121561502757600080fd5b60006150338686614ac2565b935050602061504486828701614ac2565b9250506040614c2e86828701614a96565b6000806000806080858703121561506b57600080fd5b60006150778787614ac2565b945050602061508887828801614ac2565b935050604061509987828801614ac2565b92505060606150aa87828801614a96565b91505092959194509250565b60008060008060008060c087890312156150cf57600080fd5b60006150db8989614b6a565b96505060206150ec89828a01614b6a565b95505060406150fd89828a01614b6a565b945050606061510e89828a01614b6a565b935050608061511f89828a01614b6a565b92505060a061513089828a01614b6a565b9150509295509295509295565b60006151498383615207565b505060200190565b61515a81615edc565b82525050565b61515a81615ead565b61515a61517582615ead565b615f25565b600061518582615ea0565b61518f8185615ea4565b935061519a83615e9a565b8060005b838110156151c85781516151b2888261513d565b97506151bd83615e9a565b92505060010161519e565b509495945050505050565b61515a81615eb8565b61515a6151e882615eb8565b615f30565b61515a81610fcf565b61515a61520282610fcf565b610fcf565b61515a81615ebd565b61515a61520282615ebd565b600061522782615ea0565b6152318185615ea4565b9350615241818560208601615ef9565b61524a81615f51565b9093019392505050565b600061525f82615ea0565b6152698185610ec2565b9350615279818560208601615ef9565b9290920192915050565b6000615290601483615ea4565b733932b2b73a3930b731bc903b34b7b630ba34b7b760611b815260200192915050565b60006152c0600283615ea4565b61031360f41b815260200192915050565b60006152de600283615ea4565b61313560f01b815260200192915050565b60006152fc601e83615ea4565b7f63616c6c20746f2061206e6f6e2d636f6e747261637420616464726573730000815260200192915050565b6000615335602683615ea4565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206181526564647265737360d01b602082015260400192915050565b600061537d601d83615ea4565b7f656e7472792070726963652061626f766520746865206d696e696d756d000000815260200192915050565b60006153b6601b83615ea4565b7f536166654d6174683a206164646974696f6e206f766572666c6f770000000000815260200192915050565b60006153ef602183615ea4565b7f5369676e6564536166654d6174683a206164646974696f6e206f766572666c6f8152607760f81b602082015260400192915050565b6000615432603a83615ea4565b7f416464726573733a20756e61626c6520746f2073656e642076616c75652c207281527f6563697069656e74206d61792068617665207265766572746564000000000000602082015260400192915050565b6000615491600183615ea4565b603760f81b815260200192915050565b60006154ae601d83615ea4565b7f416464726573733a20696e73756666696369656e742062616c616e6365000000815260200192915050565b60006154e7601283615ea4565b716e6f7420656e6f7567682062616c616e636560701b815260200192915050565b6000615515600283615ea4565b61333760f01b815260200192915050565b6000615533600283615ea4565b610c8d60f21b815260200192915050565b6000615551600283615ea4565b61313160f01b815260200192915050565b600061556f602183615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206f766572666c6f8152607760f81b602082015260400192915050565b60006155b2600283615ea4565b61189960f11b815260200192915050565b60006155d0600283615ea4565b61323560f01b815260200192915050565b60006155ee600283615ea4565b61199960f11b815260200192915050565b600061560c600283615ea4565b61313760f01b815260200192915050565b600061562a602183615ea4565b7f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f8152607760f81b602082015260400192915050565b600061566d600c83615ea4565b6b1d5b985d5d1a1bdc9a5e995960a21b815260200192915050565b6000615695600283615ea4565b61313960f01b815260200192915050565b60006156b3600283615ea4565b61191b60f11b815260200192915050565b60006156d1602783615ea4565b7f5369676e6564536166654d6174683a206d756c7469706c69636174696f6e206f815266766572666c6f7760c81b602082015260400192915050565b600061571a601383615ea4565b721c1c9a5b98da5c185b081d1bdbc81cdb585b1b606a1b815260200192915050565b6000615749601d83615ea4565b7f696e76616c6964207261746520636f6c6c61746572616c20746f6b656e000000815260200192915050565b6000610fb7600083610ec2565b600061578f600c83615ea4565b6b1b9bdb9499595b9d1c985b9d60a21b815260200192915050565b60006157b7602483615ea4565b7f5369676e6564536166654d6174683a207375627472616374696f6e206f766572815263666c6f7760e01b602082015260400192915050565b60006157fd601883615ea4565b7f34303120757365206f66206578697374696e67206c6f616e0000000000000000815260200192915050565b6000615836600183615ea4565b601b60f91b815260200192915050565b6000615853602083615ea4565b7f5369676e6564536166654d6174683a206469766973696f6e206279207a65726f815260200192915050565b805160808301906158908482615160565b5060208201516158a36020850182615160565b5060408201516158b66040850182615160565b506060820151613e356060850182615160565b80516101208301906158db84826151ed565b5060208201516158ee60208501826151ed565b50604082015161590160408501826151ed565b50606082015161591460608501826151ed565b50608082015161592760808501826151ed565b5060a082015161593a60a08501826151ed565b5060c082015161594d60c08501826151ed565b5060e082015161596060e08501826151ed565b50610100820151613e356101008501826151ed565b61515a81615ed6565b600061598a8285615169565b60148201915061599a82846151dc565b5060010192915050565b60006159b08285615169565b6014820191506159c082846151f6565b5060200192915050565b60006159d68285615210565b6004820191506159c082846151f6565b6000610bc18284615254565b6000610fb782615775565b60208101610fb78284615160565b60208101610fb78284615151565b60408101615a278285615160565b610bc16020830184615160565b60408101615a428285615160565b610bc16020830184615151565b60608101615a5d8286615160565b615a6a6020830185615160565b61123960408301846151ed565b60a08101615a858288615160565b615a926020830187615160565b615a9f60408301866151ed565b615aac60608301856151ed565b61430260808301846151d3565b60c08101615ac78289615160565b615ad46020830188615160565b615ae160408301876151ed565b615aee60608301866151ed565b615afb60808301856151ed565b615b0860a08301846151ed565b979650505050505050565b60408101615b218285615160565b610bc160208301846151ed565b60608101615b3c8286615160565b615b4960208301856151ed565b6112396040830184615151565b60408082528101615b67818561517a565b9050610bc160208301846151ed565b60208101610fb782846151d3565b60208101610fb782846151ed565b6102408101615ba1828a6151ed565b615bae60208301896151ed565b615bbb60408301886151d3565b615bc860608301876151ed565b615bd5608083018661587f565b615be36101008301856158c9565b818103610220830152615bf6818461521c565b9998505050505050505050565b60208082528101610bc1818461521c565b60208082528101610fb781615283565b60208082528101610fb7816152b3565b60208082528101610fb7816152d1565b60208082528101610fb7816152ef565b60208082528101610fb781615328565b60208082528101610fb781615370565b60208082528101610fb7816153a9565b60208082528101610fb7816153e2565b60208082528101610fb781615425565b60208082528101610fb781615484565b60208082528101610fb7816154a1565b60208082528101610fb7816154da565b60208082528101610fb781615508565b60208082528101610fb781615526565b60208082528101610fb781615544565b60208082528101610fb781615562565b60208082528101610fb7816155a5565b60208082528101610fb7816155c3565b60208082528101610fb7816155e1565b60208082528101610fb7816155ff565b60208082528101610fb78161561d565b60208082528101610fb781615660565b60208082528101610fb781615688565b60208082528101610fb7816156a6565b60208082528101610fb7816156c4565b60208082528101610fb78161570d565b60208082528101610fb78161573c565b60208082528101610fb781615782565b60208082528101610fb7816157aa565b60208082528101610fb7816157f0565b60208082528101610fb781615829565b60208082528101610fb781615846565b60408101615b2182856151ed565b60608101615e3082866151ed565b615a6a60208301856151ed565b60208101610fb78284615975565b60405181810167ffffffffffffffff81118282101715615e6a57600080fd5b604052919050565b600067ffffffffffffffff821115615e8957600080fd5b506020601f91909101601f19160190565b60200190565b5190565b90815260200190565b6000610fb782615eca565b151590565b6001600160e01b03191690565b6001600160a01b031690565b60ff1690565b6000610fb7826000610fb782615ead565b82818337506000910152565b60005b83811015615f14578181015183820152602001615efc565b83811115613e355750506000910152565b6000610fb782615f3b565b6000610fb782615f46565b6000610fb782615f61565b6000610fb782615f5b565b601f01601f191690565b60f81b90565b60601b90565b615f7081615ead565b81146129ed57600080fd5b615f7081615eb8565b615f7081610fcf56fea365627a7a723158202b7d4735fd0c29c4b5fda813b3abf013dec4f7b174b9c22673fd95ecefd4aa3e6c6578706572696d656e74616cf564736f6c63430005110040",
+ "linkReferences": {},
+ "deployedLinkReferences": {}
+}
diff --git a/external/deployments/rskMainnet/WrappedNativeToken.json b/external/deployments/rskMainnet/WrappedNativeToken.json
new file mode 100644
index 000000000..655612dc2
--- /dev/null
+++ b/external/deployments/rskMainnet/WrappedNativeToken.json
@@ -0,0 +1,276 @@
+{
+ "_format": "hh-sol-artifact-1",
+ "contractName": "WrappedNativeTokenERC20",
+ "sourceName": "contracts/interfaces/WrappedNativeTokenERC20.sol",
+ "address": "0x542fda317318ebf1d3deaf76e0b632741a7e677d",
+ "abi": [
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_who",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "deposit",
+ "outputs": [],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+ ],
+ "bytecode": "0x",
+ "deployedBytecode": "0x",
+ "linkReferences": {},
+ "deployedLinkReferences": {}
+}
diff --git a/external/deployments/rskTestnet/WrappedNativeToken.json b/external/deployments/rskTestnet/WrappedNativeToken.json
new file mode 100644
index 000000000..7cfa35b85
--- /dev/null
+++ b/external/deployments/rskTestnet/WrappedNativeToken.json
@@ -0,0 +1,276 @@
+{
+ "_format": "hh-sol-artifact-1",
+ "contractName": "WrappedNativeTokenERC20",
+ "sourceName": "contracts/interfaces/WrappedNativeTokenERC20.sol",
+ "address": "0x69FE5cEC81D5eF92600c1A0dB1F11986AB3758Ab",
+ "abi": [
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "owner",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "spender",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "from",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "to",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "value",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_spender",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_who",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "deposit",
+ "outputs": [],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_from",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "_to",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_value",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+ ],
+ "bytecode": "0x",
+ "deployedBytecode": "0x",
+ "linkReferences": {},
+ "deployedLinkReferences": {}
+}
diff --git a/hardhat/tasks/feeSharingCollector.js b/hardhat/tasks/feeSharingCollector.js
index 37bb1941e..dc6bc55da 100644
--- a/hardhat/tasks/feeSharingCollector.js
+++ b/hardhat/tasks/feeSharingCollector.js
@@ -5,26 +5,29 @@ const { sendWithMultisig } = require("../../deployment/helpers/helpers");
task(
"feeSharingCollector:initialize",
- "Initialize feeSharingCollector: set WRBTC and Loan Token WRBTC addresses to the FeeSharingCollector storage"
+ "Initialize feeSharingCollector: set WrappedNativeToken and Loan Token WrappedNativeToken addresses to the FeeSharingCollector storage"
)
.addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer")
.setAction(async ({ signer }, hre) => {
await initializeFeeSharingCollector(hre, signer, true);
});
-task("feeSharingCollector:setWrtbcTokenAddress", "Set WRBTC token address in feeSharingCollector")
+task(
+ "feeSharingCollector:setWrtbcTokenAddress",
+ "Set WrappedNativeToken token address in feeSharingCollector"
+)
.addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer")
.setAction(async ({ signer }, hre) => {
- await setWrbtcTokenAddress(hre, signer, true);
+ await setWrappedNativeTokenAddress(hre, signer, true);
});
task(
"feeSharingCollector:setLoanTokenWrtbcAddress",
- "Set WRBTC loan token address in feeSharingCollector"
+ "Set WrappedNativeToken loan token address in feeSharingCollector"
)
.addOptionalParam("signer", "Signer name: 'signer' or 'deployer'", "deployer")
.setAction(async ({ signer }, hre) => {
- await setLoanTokenWrbtcAddress(hre, signer, true);
+ await setLoanWrappedNativeTokenAddress(hre, signer, true);
});
const initializeFeeSharingCollector = async (hre, signer) => {
@@ -42,16 +45,16 @@ const initializeFeeSharingCollector = async (hre, signer) => {
return;
}
- const wrbtcToken = (await get("WRBTC")).address;
- const loanWrbtcToken = (await get("LoanToken_iRBTC")).address;
+ const wrappedNativeToken = (await get("WrappedNativeToken")).address;
+ const loanWrappedNativeToken = (await get("LoanToken_iNativeToken")).address;
- if (!ethers.utils.isAddress(wrbtcToken)) {
- logger.error(`WRBTC - ${wrbtcToken} is invalid address`);
+ if (!ethers.utils.isAddress(wrappedNativeToken)) {
+ logger.error(`WrappedNativeToken - ${wrappedNativeToken} is invalid address`);
return;
}
- if (!ethers.utils.isAddress(loanWrbtcToken)) {
- logger.error(`loan token iRBTC - ${loanWrbtcToken} is invalid address`);
+ if (!ethers.utils.isAddress(loanWrappedNativeToken)) {
+ logger.error(`loan token iNativeToken- ${loanWrappedNativeToken} is invalid address`);
return;
}
@@ -60,21 +63,24 @@ const initializeFeeSharingCollector = async (hre, signer) => {
const signerAcc = (await hre.getNamedAccounts())[signer];
const targetDeploymentAddress = (await get("FeeSharingCollector")).address;
const iface = new ethers.utils.Interface([
- "function initialize(address wrbtcToken, address loanWrbtcToken)",
+ "function initialize(address wrappedNativeToken, address loanWrappedNativeToken)",
+ ]);
+ let data = await iface.encodeFunctionData("initialize", [
+ wrappedNativeToken,
+ loanWrappedNativeToken,
]);
- let data = await iface.encodeFunctionData("initialize", [wrbtcToken, loanWrbtcToken]);
await sendWithMultisig(multisigDeployment.address, targetDeploymentAddress, data, signerAcc);
};
-const setWrbtcTokenAddress = async (hre, signer) => {
+const setWrappedNativeTokenAddress = async (hre, signer) => {
const {
deployments: { get },
ethers,
} = hre;
- const wrbtcToken = (await get("WRBTC")).address;
- if (!ethers.utils.isAddress(wrbtcToken)) {
- logger.error(`wrbtcToken - ${wrbtcToken} is invalid address`);
+ const wrappedNativeToken = (await get("WrappedNativeToken")).address;
+ if (!ethers.utils.isAddress(wrappedNativeToken)) {
+ logger.error(`wrappedNativeToken - ${wrappedNativeToken} is invalid address`);
return;
}
@@ -83,21 +89,21 @@ const setWrbtcTokenAddress = async (hre, signer) => {
const signerAcc = (await hre.getNamedAccounts())[signer];
const targetDeploymentAddress = (await get("FeeSharingCollector")).address;
const iface = new ethers.utils.Interface([
- "function setWrbtcToken(address newWrbtcTokenAddress)",
+ "function setWrappedNativeToken(address newWrappedNativeTokenAddress)",
]);
- let data = await iface.encodeFunctionData("setWrbtcToken", [wrbtcToken]);
+ let data = await iface.encodeFunctionData("setWrappedNativeToken", [wrappedNativeToken]);
await sendWithMultisig(multisigDeployment.address, targetDeploymentAddress, data, signerAcc);
};
-const setLoanTokenWrbtcAddress = async (hre, signer) => {
+const setLoanWrappedNativeTokenAddress = async (hre, signer) => {
const {
deployments: { get },
ethers,
} = hre;
- const loanWrbtcToken = (await get("iRBTC")).address;
- if (!ethers.utils.isAddress(loanWrbtcToken)) {
- logger.error(`loanWrbtcToken - ${loanWrbtcToken} is invalid address`);
+ const loanWrappedNativeToken = (await get("LoanToken_iNativeToken")).address;
+ if (!ethers.utils.isAddress(loanWrappedNativeToken)) {
+ logger.error(`loanWrappedNativeToken - ${loanWrappedNativeToken} is invalid address`);
return;
}
@@ -106,8 +112,10 @@ const setLoanTokenWrbtcAddress = async (hre, signer) => {
const signerAcc = (await hre.getNamedAccounts())[signer];
const targetDeploymentAddress = (await get("FeeSharingCollector")).address;
const iface = new ethers.utils.Interface([
- "function setLoanTokenWrbtc(address newLoanTokenWrbtcAddress)",
+ "function setLoanWrappedNativeToken(address newLoanWrappedNativeTokenAddress)",
+ ]);
+ let data = await iface.encodeFunctionData("setLoanWrappedNativeToken", [
+ loanWrappedNativeToken,
]);
- let data = await iface.encodeFunctionData("setLoanTokenWrbtc", [loanWrbtcToken]);
await sendWithMultisig(multisigDeployment.address, targetDeploymentAddress, data, signerAcc);
};
diff --git a/hardhat/tasks/governance.js b/hardhat/tasks/governance.js
index 775365ce9..0dddd1c63 100644
--- a/hardhat/tasks/governance.js
+++ b/hardhat/tasks/governance.js
@@ -122,7 +122,7 @@ async function createVestings(hre, dryRun, path, multiplier, signerAcc) {
vestingCreationType = 3;
} else if (teamVesting[3] === 26) {
vestingCreationType = 1;
- } else if ([39, 22, 17, 34, 19].includes(teamVesting[3])) {
+ } else if ([39, 22, 17, 34, 19, 7, 24].includes(teamVesting[3])) {
vestingCreationType = 5;
console.log("Make sure 3 year team 2 vesting split is really expected!");
} else {
diff --git a/tests/FeeSharingCollectorMultiToken.test.js b/tests/FeeSharingCollectorMultiToken.test.js
new file mode 100644
index 000000000..a492bd806
--- /dev/null
+++ b/tests/FeeSharingCollectorMultiToken.test.js
@@ -0,0 +1,3051 @@
+const { expect } = require("chai");
+const { loadFixture, takeSnapshot, mine } = require("@nomicfoundation/hardhat-network-helpers");
+const { expectRevert, expectEvent, constants, BN } = require("@openzeppelin/test-helpers");
+
+const { ZERO_ADDRESS } = constants;
+
+const { etherMantissa, mineBlock, increaseTime } = require("./Utils/Ethereum.js");
+
+const {
+ deployAndGetIStaking,
+ replaceStakingModule,
+ getStakingModulesObject,
+ getStakingModulesAddressList,
+} = require("./Utils/initializer.js");
+
+const TestToken = artifacts.require("TestToken");
+
+const StakingProxy = artifacts.require("StakingProxy");
+const VestingLogic = artifacts.require("VestingLogicMockup");
+const Vesting = artifacts.require("TeamVesting");
+
+const ISovryn = artifacts.require("ISovryn");
+const Affiliates = artifacts.require("Affiliates");
+
+const Protocol = artifacts.require("sovrynProtocol");
+const ProtocolSettings = artifacts.require("ProtocolSettingsMockup");
+const LoanMaintenance = artifacts.require("LoanMaintenance");
+const LoanSettings = artifacts.require("LoanSettings");
+const LoanClosingsLiquidation = artifacts.require("LoanClosingsLiquidation");
+const LoanClosingsRollover = artifacts.require("LoanClosingsRollover");
+const LoanClosingsWith = artifacts.require("LoanClosingsWith");
+
+const ILoanTokenLogicProxy = artifacts.require("ILoanTokenLogicProxy");
+const ILoanTokenModules = artifacts.require("ILoanTokenModules");
+const LoanTokenLogicWrbtc = artifacts.require("LoanTokenLogicWrbtc");
+const LoanToken = artifacts.require("LoanToken");
+const LockedSOV = artifacts.require("LockedSOV");
+
+const FeeSharingCollector = artifacts.require("FeeSharingCollectorMultiToken");
+const FeeSharingCollectorProxy = artifacts.require("FeeSharingCollectorProxy");
+const FeeSharingCollectorMockup = artifacts.require("FeeSharingCollectorMultiTokenMockup");
+const MockSovrynLBFactory = artifacts.require("MockSovrynLBFactory");
+const MockSovrynLBPair = artifacts.require("MockSovrynLBPair");
+const WeightedStakingModuleMockup = artifacts.require("WeightedStakingModuleMockup");
+const IWeightedStakingModuleMockup = artifacts.require("IWeightedStakingModuleMockup");
+
+const PriceFeedsLocal = artifacts.require("PriceFeedsLocal");
+
+const VestingFactory = artifacts.require("VestingFactory");
+const VestingRegistry = artifacts.require("VestingRegistry3");
+
+const SwapsImplSovrynSwapLib = artifacts.require("SwapsImplSovrynSwapLib");
+const SwapsImplSovrynSwap = artifacts.require("SwapsImplSovrynSwapModule");
+const TestSovrynSwap = artifacts.require("TestSovrynSwap");
+const SwapsExternal = artifacts.require("SwapsExternal");
+
+const TOTAL_SUPPLY = etherMantissa(1000000000);
+
+const MAX_DURATION = new BN(24 * 60 * 60).mul(new BN(1092));
+const TWO_WEEKS = 1209600;
+
+const MAX_VOTING_WEIGHT = 10;
+
+const FEE_WITHDRAWAL_INTERVAL = 172800;
+
+const MOCK_PRIOR_WEIGHTED_STAKE = false;
+
+const wei = web3.utils.toWei;
+
+const { lend_btc_before_cashout } = require("./loan-token/helpers.js");
+
+const mutexUtils = require("../deployment/helpers/reentrancy/utils.js");
+
+let cliff = 1; // This is in 4 weeks. i.e. 1 * 4 weeks.
+let duration = 11; // This is in 4 weeks. i.e. 11 * 4 weeks.
+
+const {
+ getSUSD,
+ getRBTC,
+ getWRBTC,
+ getBZRX,
+ getLoanTokenLogic,
+ getLoanToken,
+ getLoanTokenLogicWrbtc,
+ getLoanTokenWRBTC,
+ loan_pool_setup,
+ set_demand_curve,
+ getPriceFeeds,
+ getSovryn,
+ decodeLogs,
+ getSOV,
+} = require("./Utils/initializer.js");
+
+contract("FeeSharingCollectorMultiToken:", (accounts) => {
+ const name = "Test SOVToken";
+ const symbol = "TST";
+
+ let root, account1, account2, account3, account4;
+ let SOVToken, SUSD, WrappedNativeToken, sovryn, staking;
+ let loanTokenSettings, loanTokenLogic, loanToken;
+ let feeSharingCollectorProxyObj;
+ let feeSharingCollector;
+ let feeSharingCollectorLogic;
+ let loanWrappedNativeToken;
+ let tradingFeePercent;
+ let mockPrice;
+ let sovrynLBFactory;
+
+ before(async () => {
+ [root, account1, account2, account3, account4, ...accounts] = accounts;
+
+ try {
+ /** Deploy SwapsImplSovrynSwapLib */
+ const swapsImplSovrynSwapLib = await SwapsImplSovrynSwapLib.new();
+ await LoanMaintenance.link(swapsImplSovrynSwapLib);
+ await SwapsExternal.link(swapsImplSovrynSwapLib);
+ await LoanClosingsWith.link(swapsImplSovrynSwapLib);
+ await LoanClosingsRollover.link(swapsImplSovrynSwapLib);
+ await SwapsImplSovrynSwap.link(swapsImplSovrynSwapLib);
+ } catch (err) {}
+ });
+
+ async function protocolDeploymentFixture(_wallets, _provider) {
+ // Need to deploy the mutex in the initialization. Otherwise, the global reentrancy prevention will not be working & throw an error.
+ await mutexUtils.getOrDeployMutex();
+
+ // Token
+ SOVToken = await TestToken.new(name, symbol, 18, TOTAL_SUPPLY);
+
+ // Staking
+ // Creating the Staking Instance (Staking Modules Interface).
+ const stakingProxy = await StakingProxy.new(SOVToken.address);
+ const modulesObject = await getStakingModulesObject();
+
+ staking = await deployAndGetIStaking(stakingProxy.address, modulesObject);
+
+ const weightedStakingModuleMockup = await WeightedStakingModuleMockup.new();
+ const modulesAddressList = getStakingModulesAddressList(modulesObject);
+
+ await replaceStakingModule(
+ stakingProxy.address,
+ modulesAddressList["WeightedStakingModule"],
+ weightedStakingModuleMockup.address
+ );
+
+ iWeightedStakingModuleMockup = await IWeightedStakingModuleMockup.at(staking.address);
+
+ SUSD = await getSUSD();
+ RBTC = await getRBTC();
+ WrappedNativeToken = await getWRBTC();
+ BZRX = await getBZRX();
+ priceFeeds = await getPriceFeeds(WrappedNativeToken, SUSD, RBTC, BZRX);
+
+ // Deploying sovrynProtocol w/ generic function from initializer.js
+ /// @dev Tried but no success so far. When using the getSovryn function
+ /// , contracts revert w/ "target not active" error.
+ /// The weird thing is that deployment code below is exactly the same as
+ /// the code from getSovryn function at initializer.js.
+ /// Inline code works ok, but when calling the function it does not.
+ // sovryn = await getSovryn(WRBTC, SUSD, RBTC, priceFeeds);
+ // await sovryn.setSovrynProtocolAddress(sovryn.address);
+
+ const sovrynproxy = await Protocol.new();
+ sovryn = await ISovryn.at(sovrynproxy.address);
+
+ await sovryn.replaceContract((await ProtocolSettings.new()).address);
+ await sovryn.replaceContract((await LoanSettings.new()).address);
+ await sovryn.replaceContract((await LoanMaintenance.new()).address);
+ await sovryn.replaceContract((await SwapsExternal.new()).address);
+
+ await sovryn.setWrbtcToken(WrappedNativeToken.address);
+
+ await sovryn.replaceContract((await LoanClosingsWith.new()).address);
+ await sovryn.replaceContract((await LoanClosingsLiquidation.new()).address);
+ await sovryn.replaceContract((await LoanClosingsRollover.new()).address);
+
+ await sovryn.replaceContract((await Affiliates.new()).address);
+
+ sovryn = await ProtocolSettings.at(sovryn.address);
+
+ // Loan token
+ const initLoanTokenLogic = await getLoanTokenLogic(); // function will return [LoanTokenLogicProxy, LoanTokenLogicBeacon]
+ loanTokenLogic = initLoanTokenLogic[0];
+ loanTokenLogicBeacon = initLoanTokenLogic[1];
+
+ loanToken = await LoanToken.new(
+ root,
+ loanTokenLogic.address,
+ sovryn.address,
+ WrappedNativeToken.address
+ );
+ await loanToken.initialize(SUSD.address, "iSUSD", "iSUSD");
+
+ /** Initialize the loan token logic proxy */
+ loanToken = await ILoanTokenLogicProxy.at(loanToken.address);
+ await loanToken.setBeaconAddress(loanTokenLogicBeacon.address);
+
+ /** Use interface of LoanTokenModules */
+ loanToken = await ILoanTokenModules.at(loanToken.address);
+
+ await loanToken.setAdmin(root);
+ await sovryn.setLoanPool([loanToken.address], [SUSD.address]);
+
+ // FeeSharingCollector
+ feeSharingCollectorLogic = await FeeSharingCollector.new();
+ feeSharingCollectorProxyObj = await FeeSharingCollectorProxy.new(
+ sovryn.address,
+ staking.address
+ );
+ await feeSharingCollectorProxyObj.setImplementation(feeSharingCollectorLogic.address);
+ feeSharingCollector = await FeeSharingCollector.at(feeSharingCollectorProxyObj.address);
+
+ await sovryn.setFeesController(feeSharingCollector.address);
+
+ // Set loan pool for wrappedNativeToken -- because our fee sharing proxy required the loanPool of wrappedNativeToken
+ // Loan token
+ const initLoanTokenLogicWrbtc = await getLoanTokenLogicWrbtc(); // function will return [LoanTokenLogicProxy, LoanTokenLogicBeacon]
+ loanTokenLogicWrbtc = initLoanTokenLogicWrbtc[0];
+ loanTokenLogicBeaconWrbtc = initLoanTokenLogicWrbtc[1];
+
+ loanWrappedNativeToken = await LoanToken.new(
+ root,
+ loanTokenLogicWrbtc.address,
+ sovryn.address,
+ WrappedNativeToken.address
+ );
+ await loanWrappedNativeToken.initialize(
+ WrappedNativeToken.address,
+ "iWrappedNativeToken",
+ "iWrappedNativeToken"
+ );
+
+ /** Initialize the loan token logic proxy */
+ loanWrappedNativeToken = await ILoanTokenLogicProxy.at(loanWrappedNativeToken.address);
+ await loanWrappedNativeToken.setBeaconAddress(loanTokenLogicBeaconWrbtc.address);
+
+ /** Use interface of LoanTokenModules */
+ loanWrappedNativeToken = await ILoanTokenModules.at(loanWrappedNativeToken.address);
+
+ const loanTokenAddressWrappedNativeToken = await loanWrappedNativeToken.loanTokenAddress();
+ await sovryn.setLoanPool(
+ [loanWrappedNativeToken.address],
+ [loanTokenAddressWrappedNativeToken]
+ );
+
+ await WrappedNativeToken.mint(sovryn.address, wei("500", "ether"));
+
+ await sovryn.setWrbtcToken(WrappedNativeToken.address);
+ await sovryn.setSOVTokenAddress(SOVToken.address);
+ await sovryn.setSovrynProtocolAddress(sovryn.address);
+
+ // Creating the Vesting Instance.
+ vestingLogic = await VestingLogic.new();
+ vestingFactory = await VestingFactory.new(vestingLogic.address);
+ vestingRegistry = await VestingRegistry.new(
+ vestingFactory.address,
+ SOVToken.address,
+ staking.address,
+ feeSharingCollector.address,
+ root // This should be Governance Timelock Contract.
+ );
+ vestingFactory.transferOwnership(vestingRegistry.address);
+
+ await sovryn.setLockedSOVAddress(
+ (
+ await LockedSOV.new(SOVToken.address, vestingRegistry.address, cliff, duration, [
+ root,
+ ])
+ ).address
+ );
+
+ // Set PriceFeeds
+ feeds = await PriceFeedsLocal.new(WrappedNativeToken.address, sovryn.address);
+ mockPrice = "1";
+ await feeds.setRates(SUSD.address, WrappedNativeToken.address, wei(mockPrice, "ether"));
+ const swaps = await SwapsImplSovrynSwap.new();
+ const sovrynSwapSimulator = await TestSovrynSwap.new(feeds.address);
+ await sovryn.setSovrynSwapContractRegistryAddress(sovrynSwapSimulator.address);
+ await sovryn.setSupportedTokens([SUSD.address, WrappedNativeToken.address], [true, true]);
+ await sovryn.setPriceFeedContract(
+ feeds.address // priceFeeds
+ );
+ await sovryn.setSwapsImplContract(
+ swaps.address // swapsImpl
+ );
+
+ tradingFeePercent = await sovryn.tradingFeePercent();
+ await lend_btc_before_cashout(loanWrappedNativeToken, new BN(wei("10", "ether")), root);
+
+ const maxDisagreement = new BN(wei("5", "ether"));
+ await sovryn.setMaxDisagreement(maxDisagreement);
+
+ sovrynLBFactory = await MockSovrynLBFactory.new();
+
+ await feeSharingCollector.initialize(WrappedNativeToken.address, sovrynLBFactory.address);
+
+ return sovryn;
+ }
+
+ beforeEach(async () => {
+ await loadFixture(protocolDeploymentFixture);
+ });
+
+ describe("initialization", async () => {
+ it("revert if initialize called by non-owner account", async () => {
+ await expectRevert(
+ feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ {
+ from: account3,
+ }
+ ),
+ "unauthorized"
+ );
+ });
+
+ it("revert if setWrappedNativeToken called by non-owner account", async () => {
+ await expectRevert(
+ feeSharingCollector.setWrappedNativeToken(WrappedNativeToken.address, {
+ from: account3,
+ }),
+ "unauthorized"
+ );
+ });
+
+ it("should revert if initialized more than once", async () => {
+ const wrappedNativeTokenAddress = (
+ await TestToken.new("WrappedNativeToken", "WNT", 18, 100)
+ ).address;
+ const loanWrappedNativeTokenAddress = (
+ await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100)
+ ).address;
+ await expectRevert(
+ feeSharingCollector.initialize(wrappedNativeTokenAddress, sovrynLBFactory.address),
+ "function can only be called once"
+ );
+ });
+ });
+
+ describe("FeeSharingCollectorProxy", () => {
+ before(async () => {
+ await loadFixture(protocolDeploymentFixture);
+ });
+ beforeEach(async () => {
+ snapshot = await takeSnapshot();
+ });
+ afterEach(async () => {
+ await snapshot.restore();
+ });
+
+ it("Check owner & implementation", async () => {
+ const proxyOwner = await feeSharingCollectorProxyObj.getProxyOwner();
+ const implementation = await feeSharingCollectorProxyObj.getImplementation();
+
+ expect(implementation).to.be.equal(feeSharingCollectorLogic.address);
+ expect(proxyOwner).to.be.equal(root);
+ });
+
+ it("Set new implementation", async () => {
+ const newFeeSharingCollector = await FeeSharingCollector.new();
+ await feeSharingCollectorProxyObj.setImplementation(newFeeSharingCollector.address);
+ const newImplementation = await feeSharingCollectorProxyObj.getImplementation();
+
+ expect(newImplementation).to.be.equal(newFeeSharingCollector.address);
+ });
+ });
+
+ describe("withdrawFees", () => {
+ it("Shouldn't be able to use zero token address", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.withdrawFees([ZERO_ADDRESS]),
+ "FeeSharingCollectorMultiToken::withdrawFees: token is not a contract"
+ );
+ });
+
+ it("Shouldn't be able to withdraw if wRBTC loan pool does not exist", async () => {
+ await protocolDeploymentFixture();
+ // Unset the loanPool for wRBTC
+ await sovryn.setLoanPool([loanWrappedNativeToken.address], [ZERO_ADDRESS]);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ true
+ );
+
+ await expectRevert(
+ feeSharingCollector.withdrawFees([WrappedNativeToken.address]),
+ "FeeSharingCollectorMultiToken::withdrawFees: loan wrappedNativeTokenAddress not found"
+ );
+ });
+
+ it("Shouldn't be able to withdraw zero amount", async () => {
+ await protocolDeploymentFixture();
+ const tx = await feeSharingCollector.withdrawFees([SUSD.address]);
+ expectEvent(tx, "FeeWithdrawn", {
+ sender: root,
+ token: loanWrappedNativeToken.address,
+ amount: new BN(0),
+ });
+ });
+
+ it("ProtocolSettings.withdrawFees", async () => {
+ /// @dev This test requires redeploying the protocol
+ const protocol = await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ let previousProtocolWrbtcBalance = await WrappedNativeToken.balanceOf(
+ protocol.address
+ );
+ // let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+ await protocol.setFeesController(root);
+ let tx = await protocol.withdrawFees([SUSD.address], root);
+ let latestProtocolWrbtcBalance = await WrappedNativeToken.balanceOf(protocol.address);
+
+ await checkWithdrawFee();
+
+ //check wrappedNativeToken balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let userBalance = await WrappedNativeToken.balanceOf.call(root);
+ expect(userBalance.toString()).to.be.equal(feeAmount.toString());
+
+ // wrappedNativeToken balance should remain the same
+ expect(previousProtocolWrbtcBalance.toString()).to.equal(
+ latestProtocolWrbtcBalance.toString()
+ );
+
+ expectEvent(tx, "WithdrawFees", {
+ sender: root,
+ token: SUSD.address,
+ receiver: root,
+ lendingAmount: lendingFeeTokensHeld,
+ tradingAmount: tradingFeeTokensHeld,
+ borrowingAmount: borrowingFeeTokensHeld,
+ // amountConvertedToWRBTC
+ });
+ });
+
+ it("ProtocolSettings.withdrawFees (WRBTC token)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ true
+ );
+ // let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+ await sovryn.setFeesController(root);
+ let tx = await sovryn.withdrawFees([WrappedNativeToken.address], account1);
+
+ await checkWithdrawFee(true, true, false);
+
+ //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let userBalance = await WrappedNativeToken.balanceOf.call(account1);
+ expect(userBalance.toString()).to.be.equal(feeAmount.toString());
+
+ expectEvent(tx, "WithdrawFees", {
+ sender: root,
+ token: WrappedNativeToken.address,
+ receiver: account1,
+ lendingAmount: lendingFeeTokensHeld,
+ tradingAmount: tradingFeeTokensHeld,
+ borrowingAmount: borrowingFeeTokensHeld,
+ });
+ });
+
+ /// @dev Test coverage
+ it("ProtocolSettings.withdrawFees: Revert withdrawing by no feesController", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ // mock data
+ let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+
+ await sovryn.setFeesController(root);
+
+ await expectRevert(
+ sovryn.withdrawFees([SUSD.address], account1, { from: account1 }),
+ "unauthorized"
+ );
+ });
+
+ it("Should be able to withdraw fees", async () => {
+ /// @dev This test requires redeploying the protocol
+ const protocol = await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ let previousProtocolWrbtcBalance = await WrappedNativeToken.balanceOf(
+ protocol.address
+ );
+
+ tx = await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ await checkWithdrawFee();
+
+ //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString());
+
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString());
+
+ // wrappedNativeToken balance should remain the same
+ let latestProtocolWrbtcBalance = await WrappedNativeToken.balanceOf(protocol.address);
+ expect(previousProtocolWrbtcBalance.toString()).to.equal(
+ latestProtocolWrbtcBalance.toString()
+ );
+
+ //checkpoints
+ let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ loanWrappedNativeToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(1);
+ let checkpoint = await feeSharingCollector.tokenCheckpoints.call(
+ loanWrappedNativeToken.address,
+ 0
+ );
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString());
+
+ // check lastFeeWithdrawalTime
+ let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ loanWrappedNativeToken.address
+ );
+ let block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+
+ expectEvent(tx, "FeeWithdrawn", {
+ sender: root,
+ token: loanWrappedNativeToken.address,
+ amount: feeAmount,
+ });
+ });
+
+ it("Should be able to withdraw fees (WRBTC token)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ true
+ );
+
+ tx = await feeSharingCollector.withdrawFees([WrappedNativeToken.address]);
+
+ await checkWithdrawFee();
+
+ //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString());
+
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString());
+
+ //checkpoints
+ let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ loanWrappedNativeToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(1);
+ let checkpoint = await feeSharingCollector.tokenCheckpoints.call(
+ loanWrappedNativeToken.address,
+ 0
+ );
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString());
+
+ //check lastFeeWithdrawalTime
+ let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ loanWrappedNativeToken.address
+ );
+ let block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+
+ expectEvent(tx, "FeeWithdrawn", {
+ sender: root,
+ token: loanWrappedNativeToken.address,
+ amount: feeAmount,
+ });
+ });
+
+ it("Should be able to withdraw fees (sov token)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ false,
+ true
+ );
+ tx = await feeSharingCollector.withdrawFees([SOVToken.address]);
+
+ await checkWithdrawFee(false, false, true);
+
+ //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingProxyBalance = await SOVToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString());
+
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString());
+
+ //checkpoints
+ let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ SOVToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(1);
+ let checkpoint = await feeSharingCollector.tokenCheckpoints.call(SOVToken.address, 0);
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString());
+
+ //check lastFeeWithdrawalTime
+ let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ SOVToken.address
+ );
+ let block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+
+ expectEvent(tx, "TokensTransferred", {
+ sender: sovryn.address,
+ token: SOVToken.address,
+ amount: feeAmount,
+ });
+ });
+
+ it("Should be able to withdraw fees 3 times", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(1000, root);
+
+ // [FIRST]
+ // mock data
+ let mockAmountLendingFeeTokensHeld = 0;
+ let mockAmountTradingFeeTokensHeld = 1;
+ let mockAmountBorrowingFeeTokensHeld = 2;
+ let totalMockAmount1 =
+ mockAmountLendingFeeTokensHeld +
+ mockAmountTradingFeeTokensHeld +
+ mockAmountBorrowingFeeTokensHeld;
+ let lendingFeeTokensHeld = new BN(mockAmountLendingFeeTokensHeld);
+ let tradingFeeTokensHeld = new BN(
+ wei(mockAmountTradingFeeTokensHeld.toString(), "ether")
+ );
+ let borrowingFeeTokensHeld = new BN(
+ wei(mockAmountBorrowingFeeTokensHeld.toString(), "ether")
+ );
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ let totalFeeAmount = feeAmount;
+
+ let tx = await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ await checkWithdrawFee();
+
+ // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toString()).to.be.equal(feeAmount.toString());
+
+ // checkpoints
+ let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ loanWrappedNativeToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(1);
+ let checkpoint = await feeSharingCollector.tokenCheckpoints.call(
+ loanWrappedNativeToken.address,
+ 0
+ );
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toString()).to.be.equal(feeAmount.toString());
+
+ // check lastFeeWithdrawalTime
+ let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ loanWrappedNativeToken.address
+ );
+ let block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+
+ // [SECOND]
+ // mock data
+ let mockAmountLendingFeeTokensHeld2 = 1;
+ let mockAmountTradingFeeTokensHeld2 = 0;
+ let mockAmountBorrowingFeeTokensHeld2 = 0;
+ let totalMockAmount2 =
+ mockAmountTradingFeeTokensHeld2 +
+ mockAmountBorrowingFeeTokensHeld2 +
+ mockAmountLendingFeeTokensHeld2;
+ lendingFeeTokensHeld = new BN(
+ wei(mockAmountLendingFeeTokensHeld2.toString(), "ether")
+ );
+ tradingFeeTokensHeld = new BN(mockAmountTradingFeeTokensHeld2);
+ borrowingFeeTokensHeld = new BN(mockAmountBorrowingFeeTokensHeld2);
+ totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ let unprocessedAmount = feeAmount;
+ totalFeeAmount = totalFeeAmount.add(feeAmount);
+
+ tx = await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ // Need to checkwithdrawfee manually
+ await checkWithdrawFee();
+
+ // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toString()).to.be.equal(totalFeeAmount.toString());
+
+ // [THIRD]
+ // mock data
+ let mockAmountLendingFeeTokensHeld3 = 0;
+ let mockAmountTradingFeeTokensHeld3 = 0.5;
+ let mockAmountBorrowingFeeTokensHeld3 = 0.5;
+ let totalMockAmount3 =
+ mockAmountTradingFeeTokensHeld3 +
+ mockAmountBorrowingFeeTokensHeld3 +
+ mockAmountLendingFeeTokensHeld3;
+ lendingFeeTokensHeld = new BN(mockAmountLendingFeeTokensHeld3);
+ tradingFeeTokensHeld = new BN(
+ wei(mockAmountTradingFeeTokensHeld3.toString(), "ether")
+ );
+ borrowingFeeTokensHeld = new BN(
+ wei(mockAmountBorrowingFeeTokensHeld3.toString(), "ether")
+ );
+ totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ totalFeeAmount = totalFeeAmount.add(feeAmount);
+
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ tx = await feeSharingCollector.withdrawFees([SUSD.address]);
+ // In this state the price of SUSD/WRBTC already adjusted because of previous swap, so we need to consider this in the next swapFee calculation
+ await checkWithdrawFee();
+
+ // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toString()).to.be.equal(totalFeeAmount.toString());
+
+ // checkpoints
+ totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ loanWrappedNativeToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(2);
+ checkpoint = await feeSharingCollector.tokenCheckpoints.call(
+ loanWrappedNativeToken.address,
+ 1
+ );
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toString()).to.be.equal(
+ feeAmount.add(unprocessedAmount).toString()
+ );
+
+ // check lastFeeWithdrawalTime
+ lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ loanWrappedNativeToken.address
+ );
+ block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingProxyWRBTCBalance = await WrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyWRBTCBalance.toString()).to.be.equal(new BN(0).toString());
+ });
+ });
+
+ describe("withdraw LB Dex Fees", async () => {
+ it("Should be able to withdraw Dex Fees", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ const lbPair1Amount = new BN(wei("1", "ether"));
+ const lbPair2Amount = new BN(wei("2", "ether"));
+
+ const lbPair1 = await MockSovrynLBPair.new(
+ sovrynLBFactory.address,
+ SUSD.address,
+ WrappedNativeToken.address,
+ lbPair1Amount,
+ lbPair1Amount
+ );
+ const lbPair2 = await MockSovrynLBPair.new(
+ sovrynLBFactory.address,
+ SUSD.address,
+ SOVToken.address,
+ lbPair2Amount,
+ lbPair2Amount
+ );
+
+ //mock data
+ const totalFeeAmount = lbPair1Amount.add(lbPair2Amount);
+ await SUSD.mint(lbPair1.address, wei("2000", "ether"));
+ await WrappedNativeToken.mint(lbPair1.address, wei("2000", "ether"));
+ await SUSD.mint(lbPair2.address, wei("2000", "ether"));
+ await SOVToken.mint(lbPair2.address, wei("2000", "ether"));
+
+ await sovrynLBFactory.addLbPairs(2, [lbPair1.address, lbPair2.address]);
+
+ await expectRevert(feeSharingCollector.withdrawFeesFromLBDex(), "Only feeRecipient");
+
+ await sovrynLBFactory.setFeeRecipient(feeSharingCollector.address);
+
+ let previousLBPair1BalanceSUSD = await SUSD.balanceOf(lbPair1.address);
+ let previousLBPair2BalanceSUSD = await SUSD.balanceOf(lbPair2.address);
+ let previousFeeSharingCollectorBalanceSUSD = await SUSD.balanceOf(
+ feeSharingCollector.address
+ );
+
+ let previousLBPair1BalanceWrbtc = await WrappedNativeToken.balanceOf(lbPair1.address);
+ let previousFeeSharingCollectorBalanceWrbtc = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ let previousLBPair2BalanceSov = await SOVToken.balanceOf(lbPair2.address);
+ let previousFeeSharingCollectorBalanceSov = await SOVToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ tx = await feeSharingCollector.withdrawFeesFromLBDex();
+
+ let latestLBPair1BalanceSUSD = await SUSD.balanceOf(lbPair1.address);
+ let latestLBPair2BalanceSUSD = await SUSD.balanceOf(lbPair2.address);
+ let latestFeeSharingCollectorBalanceSUSD = await SUSD.balanceOf(
+ feeSharingCollector.address
+ );
+
+ let latestLBPair1BalanceWrbtc = await WrappedNativeToken.balanceOf(lbPair1.address);
+ let latestFeeSharingCollectorBalanceWrbtc = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ let latestLBPair2BalanceSov = await SOVToken.balanceOf(lbPair2.address);
+ let latestFeeSharingCollectorBalanceSov = await SOVToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ expect(previousLBPair1BalanceSUSD.toString()).to.equal(
+ latestLBPair1BalanceSUSD.add(lbPair1Amount).toString()
+ );
+ expect(previousLBPair2BalanceSUSD.toString()).to.equal(
+ latestLBPair2BalanceSUSD.add(lbPair2Amount).toString()
+ );
+ expect(previousFeeSharingCollectorBalanceSUSD.toString()).to.equal("0");
+ expect(latestFeeSharingCollectorBalanceSUSD.toString()).to.equal(
+ totalFeeAmount.toString()
+ );
+
+ expect(previousLBPair1BalanceWrbtc.toString()).to.equal(
+ latestLBPair1BalanceWrbtc.add(lbPair1Amount).toString()
+ );
+ expect(previousFeeSharingCollectorBalanceWrbtc.toString()).to.equal("0");
+ expect(latestFeeSharingCollectorBalanceWrbtc.toString()).to.equal(
+ lbPair1Amount.toString()
+ );
+
+ expect(previousLBPair2BalanceSov.toString()).to.equal(
+ latestLBPair2BalanceSov.add(lbPair2Amount).toString()
+ );
+ expect(previousFeeSharingCollectorBalanceSov.toString()).to.equal("0");
+ expect(latestFeeSharingCollectorBalanceSov.toString()).to.equal(
+ lbPair2Amount.toString()
+ );
+ });
+ });
+
+ describe("transferTokens", () => {
+ it("Shouldn't be able to use zero token address", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.transferTokens(ZERO_ADDRESS, 1000),
+ "FeeSharingCollectorMultiToken::transferTokens: invalid address"
+ );
+ });
+
+ it("Shouldn't be able to transfer zero amount", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.transferTokens(SOVToken.address, 0),
+ "FeeSharingCollectorMultiToken::transferTokens: invalid amount"
+ );
+ });
+
+ it("Shouldn't be able to withdraw zero amount", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.transferTokens(SOVToken.address, 1000),
+ "invalid transfer"
+ );
+ });
+
+ it("Should be able to transfer tokens", async () => {
+ await protocolDeploymentFixture();
+ // stake - getPriorTotalVotingPower
+ let totalStake = 1000;
+ await stake(totalStake, root);
+
+ let amount = 1000;
+ await SOVToken.approve(feeSharingCollector.address, amount * 7);
+
+ let tx = await feeSharingCollector.transferTokens(SOVToken.address, amount);
+
+ expect(
+ await feeSharingCollector.unprocessedAmount.call(SOVToken.address)
+ ).to.be.bignumber.equal(new BN(0));
+
+ expectEvent(tx, "TokensTransferred", {
+ sender: root,
+ token: SOVToken.address,
+ amount: new BN(amount),
+ });
+
+ // checkpoints
+ let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ SOVToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(1);
+ let checkpoint = await feeSharingCollector.tokenCheckpoints.call(SOVToken.address, 0);
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toString()).to.be.equal(amount.toString());
+
+ // check lastFeeWithdrawalTime
+ let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ SOVToken.address
+ );
+ let block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+
+ expectEvent(tx, "CheckpointAdded", {
+ sender: root,
+ token: SOVToken.address,
+ amount: new BN(amount),
+ });
+
+ // second time
+ tx = await feeSharingCollector.transferTokens(SOVToken.address, amount * 2);
+
+ expect(
+ await feeSharingCollector.unprocessedAmount.call(SOVToken.address)
+ ).to.be.bignumber.equal(new BN(amount * 2));
+
+ expectEvent(tx, "TokensTransferred", {
+ sender: root,
+ token: SOVToken.address,
+ amount: new BN(amount * 2),
+ });
+
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ // third time
+ tx = await feeSharingCollector.transferTokens(SOVToken.address, amount * 4);
+
+ expect(
+ await feeSharingCollector.unprocessedAmount.call(SOVToken.address)
+ ).to.be.bignumber.equal(new BN(0));
+
+ // checkpoints
+ totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
+ SOVToken.address
+ );
+ expect(totalTokenCheckpoints.toNumber()).to.be.equal(2);
+ checkpoint = await feeSharingCollector.tokenCheckpoints.call(SOVToken.address, 1);
+ expect(checkpoint.blockNumber.toNumber()).to.be.equal(tx.receipt.blockNumber);
+ expect(checkpoint.totalWeightedStake.toNumber()).to.be.equal(
+ totalStake * MAX_VOTING_WEIGHT
+ );
+ expect(checkpoint.numTokens.toNumber()).to.be.equal(amount * 6);
+
+ // check lastFeeWithdrawalTime
+ lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
+ SOVToken.address
+ );
+ block = await web3.eth.getBlock(tx.receipt.blockNumber);
+ expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
+ });
+ });
+
+ describe("withdraw", () => {
+ it("Shouldn't be able to withdraw without checkpoints (for token pool)", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.withdraw(loanToken.address, 0, account2, { from: account1 }),
+ "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive"
+ );
+ });
+
+ it("Shouldn't be able to withdraw without checkpoints (for wRBTC pool)", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.withdraw(loanWrappedNativeToken.address, 0, account2, {
+ from: account1,
+ }),
+ "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive"
+ );
+ });
+
+ it("Shouldn't be able to withdraw zero amount (for token pool)", async () => {
+ await protocolDeploymentFixture();
+ let fees = await feeSharingCollector.getAccumulatedFees(account1, loanToken.address);
+ expect(fees).to.be.bignumber.equal("0");
+
+ await expectRevert(
+ feeSharingCollector.withdraw(loanToken.address, 10, ZERO_ADDRESS, {
+ from: account1,
+ }),
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+ });
+
+ it("Shouldn't be able to withdraw zero amount (for wRBTC pool)", async () => {
+ await protocolDeploymentFixture();
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(fees).to.be.bignumber.equal("0");
+
+ await expectRevert(
+ feeSharingCollector.withdraw(loanWrappedNativeToken.address, 10, ZERO_ADDRESS, {
+ from: account1,
+ }),
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+ });
+
+ it("Should be able to withdraw to another account", async () => {
+ await protocolDeploymentFixture();
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10)));
+
+ let tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 1000,
+ account2,
+ {
+ from: account1,
+ }
+ );
+
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account2,
+ token: loanWrappedNativeToken.address,
+ amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (token pool)", async () => {
+ await protocolDeploymentFixture();
+ // FeeSharingCollector
+ feeSharingCollector = await FeeSharingCollectorMockup.new(
+ sovryn.address,
+ staking.address
+ );
+ await sovryn.setFeesController(feeSharingCollector.address);
+
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ sovrynLBFactory.address
+ );
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // Mock (transfer loanToken to FeeSharingProxy contract)
+ const loanPoolTokenAddress = await sovryn.underlyingToLoanPool(SUSD.address);
+ const amountLend = new BN(wei("500", "ether"));
+ await SUSD.approve(loanPoolTokenAddress, amountLend);
+ await loanToken.mint(feeSharingCollector.address, amountLend);
+
+ // Check ISUSD Balance for feeSharingProxy
+ const feeSharingProxyLoanBalanceToken = await loanToken.balanceOf(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyLoanBalanceToken.toString()).to.be.equal(amountLend.toString());
+
+ // Withdraw ISUSD from feeSharingProxy
+ // const initial
+ await feeSharingCollector.addCheckPoint(loanPoolTokenAddress, amountLend.toString());
+ let tx = await feeSharingCollector.trueWithdrawTokens(
+ [loanToken.address],
+ [10],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ const updatedFeeSharingProxyLoanBalanceToken = await loanToken.balanceOf(
+ feeSharingCollector.address
+ );
+ const updatedAccount1LoanBalanceToken = await loanToken.balanceOf(account1);
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+
+ expect(updatedFeeSharingProxyLoanBalanceToken.toString()).to.be.equal(
+ ((amountLend * 7) / 10).toString()
+ );
+ expect(updatedAccount1LoanBalanceToken.toString()).to.be.equal(
+ ((amountLend * 3) / 10).toString()
+ );
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account1,
+ token: loanToken.address,
+ amount: amountLend.mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (WRBTC pool)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10)));
+
+ let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
+ let tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 10,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+
+ /// @dev To anticipate gas consumption it is required to split hardhat
+ /// behaviour into two different scenarios: coverage and regular testing.
+ /// On coverage gasPrice = 1, on regular tests gasPrice = 8000000000
+ //
+ // On coverage:
+ // Fees: 1800000000
+ // Balance: 10000000000000000000000
+ // Balance: 10000000000001799398877
+ // withdraw().gasUsed: 601123
+ // txFee: 601123
+ //
+ // On regular test:
+ // Fees: 1800000000
+ // Balance: 10000000000000000000000
+ // Balance: 9999996433281800000000
+ // withdraw().gasUsed: 445840
+ // txFee: 3566720000000000
+ let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
+ let gasPrice;
+ /// @dev A balance decrease (negative difference) corresponds to regular test case
+ if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") {
+ gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice));
+ } // regular test
+ else {
+ gasPrice = new BN(1);
+ } // coverage
+
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+ let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice);
+
+ userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee));
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ // check balances
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10);
+ let userLoanTokenBalance = await loanWrappedNativeToken.balanceOf.call(account1);
+ expect(userLoanTokenBalance.toNumber()).to.be.equal(0);
+ let userExpectedBtcBalance = userInitialBtcBalance.add(
+ feeAmount.mul(new BN(3)).div(new BN(10))
+ );
+ expect(userLatestBTCBalance.toString()).to.be.equal(userExpectedBtcBalance.toString());
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account1,
+ token: loanWrappedNativeToken.address,
+ amount: feeAmount.mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (sov pool)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ false,
+ true
+ );
+
+ await feeSharingCollector.withdrawFees([SOVToken.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address);
+ expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10)));
+
+ let userInitialISOVBalance = await SOVToken.balanceOf(account1);
+ let tx = await feeSharingCollector.withdraw(SOVToken.address, 10, ZERO_ADDRESS, {
+ from: account1,
+ });
+
+ //processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ SOVToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ //check balances
+ let feeSharingProxyBalance = await SOVToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10);
+ let userBalance = await SOVToken.balanceOf.call(account1);
+ expect(userBalance.sub(userInitialISOVBalance).toNumber()).to.be.equal(
+ (feeAmount * 3) / 10
+ );
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account1,
+ token: SOVToken.address,
+ amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (sov pool) to another account", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ false,
+ true
+ );
+
+ await feeSharingCollector.withdrawFees([SOVToken.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address);
+ expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10)));
+
+ const receiverBalanceBefore = await SOVToken.balanceOf(account2);
+ let tx = await feeSharingCollector.withdraw(SOVToken.address, 10, account2, {
+ from: account1,
+ });
+
+ //processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ SOVToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ //check balances
+ let feeSharingProxyBalance = await SOVToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10);
+ const receiverBalanceAfter = await SOVToken.balanceOf(account2);
+ const amountWithdrawn = new BN(feeAmount).mul(new BN(3)).div(new BN(10));
+ expect(receiverBalanceAfter.sub(receiverBalanceBefore).toString()).to.be.equal(
+ amountWithdrawn.toString()
+ );
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account2,
+ token: SOVToken.address,
+ amount: amountWithdrawn,
+ });
+ });
+
+ it("Should be able to withdraw using 3 checkpoints", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 900;
+ await stake(rootStake, root);
+
+ let userStake = 100;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // [FIRST]
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ let totalFeeAmount = feeAmount;
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
+ let tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 1,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+
+ /// @dev Same as above gas consumption is different on regular tests than on coverge
+ let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
+ let gasPrice;
+ /// @dev A balance decrease (negative difference) corresponds to regular test case
+ if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") {
+ gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice));
+ } // regular test
+ else {
+ gasPrice = new BN(1);
+ } // coverage
+
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+ let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice);
+
+ userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee));
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ // check balances
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((totalFeeAmount * 9) / 10);
+ let userBalance = await loanWrappedNativeToken.balanceOf.call(account1);
+ expect(userBalance.toNumber()).to.be.equal(0);
+
+ expect(userLatestBTCBalance.toString()).to.be.equal(
+ userInitialBtcBalance.add(totalFeeAmount.mul(new BN(1)).div(new BN(10))).toString()
+ );
+
+ // [SECOND]
+ // mock data
+ let lendingFeeTokensHeld2 = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld2 = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld2 = new BN(wei("3", "gwei"));
+ totalFeeTokensHeld = lendingFeeTokensHeld2
+ .add(tradingFeeTokensHeld2)
+ .add(borrowingFeeTokensHeld2);
+ feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld2,
+ tradingFeeTokensHeld2,
+ borrowingFeeTokensHeld2
+ );
+ totalFeeAmount = totalFeeAmount.add(feeAmount);
+ let totalLoanTokenWRBTCBalanceShouldBeAccount1 = feeAmount;
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ // [THIRD]
+ // mock data
+ let lendingFeeTokensHeld3 = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld3 = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld3 = new BN(wei("3", "gwei"));
+ totalFeeTokensHeld = lendingFeeTokensHeld3
+ .add(tradingFeeTokensHeld3)
+ .add(borrowingFeeTokensHeld3);
+ feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld3,
+ tradingFeeTokensHeld3,
+ borrowingFeeTokensHeld3
+ );
+ totalFeeAmount = totalFeeAmount.add(feeAmount);
+ totalLoanTokenWRBTCBalanceShouldBeAccount1 =
+ totalLoanTokenWRBTCBalanceShouldBeAccount1.add(feeAmount);
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ // [SECOND] - [THIRD]
+ userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
+ tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 2,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice));
+ console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed);
+ txFee = new BN(tx.receipt.gasUsed).mul(gasPrice);
+
+ userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee));
+
+ // processedCheckpoints
+ processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(3);
+
+ // check balances
+ feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal(
+ parseInt((totalFeeAmount * 9) / 10)
+ );
+ userBalance = await loanWrappedNativeToken.balanceOf.call(account1);
+ expect(userBalance.toNumber()).to.be.equal(0);
+
+ userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
+
+ expect(userLatestBTCBalance.toString()).to.be.equal(
+ userInitialBtcBalance
+ .add(totalLoanTokenWRBTCBalanceShouldBeAccount1.mul(new BN(1)).div(new BN(10)))
+ .toString()
+ );
+ });
+
+ it("Should be able to process 10 checkpoints", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ await stake(900, root);
+ let userStake = 100;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ await createCheckpoints(10);
+
+ let tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 1000,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 10).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(10);
+ });
+
+ it("Should be able to process 10 checkpoints and 3 withdrawals", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ await stake(900, root);
+ let userStake = 100;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ await createCheckpoints(10);
+
+ let tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 5,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 5).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(5);
+
+ tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 3,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 3).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(8);
+
+ tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 1000,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(10);
+ });
+
+ // // use for gas usage tests
+ // it("Should be able to process 30 checkpoints", async () => {
+ // // stake - getPriorTotalVotingPower
+ // await stake(900, root);
+ // let userStake = 100;
+ // if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ // await staking.MOCK_priorWeightedStake(userStake * 10);
+ // }
+ // await SOVToken.transfer(account1, userStake);
+ // await stake(userStake, account1);
+ //
+ // // mock data
+ // await createCheckpoints(30);
+ //
+ // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1});
+ // console.log("\nwithdraw(checkpoints = 30).gasUsed: " + tx.receipt.gasUsed);
+ // // processedCheckpoints
+ // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address);
+ // expect(processedCheckpoints.toNumber()).to.be.equal(30);
+ // });
+ //
+ // // use for gas usage tests
+ // it("Should be able to process 100 checkpoints", async () => {
+ // // stake - getPriorTotalVotingPower
+ // await stake(900, root);
+ // let userStake = 100;
+ // if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ // await staking.MOCK_priorWeightedStake(userStake * 10);
+ // }
+ // await SOVToken.transfer(account1, userStake);
+ // await stake(userStake, account1);
+ //
+ // // mock data
+ // await createCheckpoints(100);
+ //
+ // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1});
+ // console.log("\nwithdraw(checkpoints = 500).gasUsed: " + tx.receipt.gasUsed);
+ // // processedCheckpoints
+ // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address);
+ // expect(processedCheckpoints.toNumber()).to.be.equal(100);
+ // });
+ //
+ // // use for gas usage tests
+ // it("Should be able to withdraw when staking contains a lot of checkpoints", async () => {
+ // let checkpointCount = 1000;
+ // await stake(1000, root, checkpointCount);
+ // let afterBlock = await blockNumber();
+ // console.log(afterBlock);
+ //
+ // let kickoffTS = await staking.kickoffTS.call();
+ // let stakingDate = kickoffTS.add(new BN(MAX_DURATION));
+ //
+ // let numUserStakingCheckpoints = await staking.numUserStakingCheckpoints.call(root, stakingDate);
+ // let firstCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, 0);
+ // let lastCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, numUserStakingCheckpoints - 1);
+ // let block1 = firstCheckpoint.fromBlock.toNumber() + 1;
+ // let block2 = lastCheckpoint.fromBlock;
+ //
+ // console.log("numUserStakingCheckpoints = " + numUserStakingCheckpoints.toString());
+ // console.log("first = " + firstCheckpoint.fromBlock.toString());
+ // console.log("last = " + lastCheckpoint.fromBlock.toString());
+ //
+ // let tx = await staking.calculatePriorWeightedStake(root, block1, stakingDate);
+ // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed);
+ // tx = await staking.calculatePriorWeightedStake(root, block2, stakingDate);
+ // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed);
+ // });
+
+ it("Should be able to withdraw with staking for 78 dates", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ let kickoffTS = await staking.kickoffTS.call();
+ await SOVToken.approve(staking.address, userStake * 1000);
+ for (let i = 0; i < 77; i++) {
+ let stakingDate = kickoffTS.add(new BN(TWO_WEEKS * (i + 1)));
+ await staking.stake(userStake, stakingDate, account1, account1);
+ }
+
+ // mock data
+ await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let tx = await feeSharingCollector.withdraw(
+ loanWrappedNativeToken.address,
+ 10,
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+ });
+
+ it("should compute the weighted stake and show gas usage", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ await stake(100, root);
+ let kickoffTS = await staking.kickoffTS.call();
+ let stakingDate = kickoffTS.add(new BN(MAX_DURATION));
+ await SOVToken.approve(staking.address, 100);
+ let result = await staking.stake("100", stakingDate, root, root);
+ await mineBlock();
+
+ let tx = await iWeightedStakingModuleMockup.calculatePriorWeightedStake(
+ root,
+ result.receipt.blockNumber,
+ stakingDate
+ );
+ console.log("\ngasUsed: " + tx.receipt.gasUsed);
+ });
+ });
+
+ describe("withdrawTokens", () => {
+ it("Shouldn't be able to withdraw without checkpoints (for token pool)", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.withdrawTokens([loanToken.address], [0], account2, {
+ from: account1,
+ }),
+ "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive"
+ );
+ });
+
+ it("Shouldn't be able to withdraw without checkpoints (for wRBTC pool)", async () => {
+ await protocolDeploymentFixture();
+ await expectRevert(
+ feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [0],
+ account2,
+ { from: account1 }
+ ),
+ "FeeSharingCollectorMultiToken::withdraw: _maxCheckpoints should be positive"
+ );
+ });
+
+ it("Shouldn't be able to withdraw zero amount (for token pool)", async () => {
+ await protocolDeploymentFixture();
+ let fees = await feeSharingCollector.getAccumulatedFees(account1, loanToken.address);
+ expect(fees).to.be.bignumber.equal("0");
+
+ await expectRevert(
+ feeSharingCollector.withdrawTokens([loanToken.address], [10], ZERO_ADDRESS, {
+ from: account1,
+ }),
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+ });
+
+ it("Shouldn't be able to withdraw zero amount (for wRBTC pool)", async () => {
+ await protocolDeploymentFixture();
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(fees).to.be.bignumber.equal("0");
+
+ await expectRevert(
+ feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [10],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ ),
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+ });
+
+ it("Should be able to withdraw to another account", async () => {
+ await protocolDeploymentFixture();
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "ether"));
+ let tradingFeeTokensHeld = new BN(wei("2", "ether"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "ether"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(fees).to.be.bignumber.equal(new BN(feeAmount).mul(new BN(3)).div(new BN(10)));
+
+ let tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [1000],
+ account2,
+ {
+ from: account1,
+ }
+ );
+
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account2,
+ token: loanWrappedNativeToken.address,
+ amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (token pool)", async () => {
+ await protocolDeploymentFixture();
+ // FeeSharingCollector
+ feeSharingCollector = await FeeSharingCollectorMockup.new(
+ sovryn.address,
+ staking.address
+ );
+ await sovryn.setFeesController(feeSharingCollector.address);
+
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ sovrynLBFactory.address
+ );
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // Mock (transfer loanToken to FeeSharingProxy contract)
+ const loanPoolTokenAddress = await sovryn.underlyingToLoanPool(SUSD.address);
+ const amountLend = new BN(wei("500", "ether"));
+ await SUSD.approve(loanPoolTokenAddress, amountLend);
+ await loanToken.mint(feeSharingCollector.address, amountLend);
+
+ // Check ISUSD Balance for feeSharingProxy
+ const feeSharingProxyLoanBalanceToken = await loanToken.balanceOf(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyLoanBalanceToken.toString()).to.be.equal(amountLend.toString());
+
+ // Withdraw ISUSD from feeSharingProxy
+ // const initial
+ await feeSharingCollector.addCheckPoint(loanPoolTokenAddress, amountLend.toString());
+ let tx = await feeSharingCollector.trueWithdrawTokens(
+ [loanToken.address],
+ [10],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ const updatedFeeSharingProxyLoanBalanceToken = await loanToken.balanceOf(
+ feeSharingCollector.address
+ );
+ const updatedAccount1LoanBalanceToken = await loanToken.balanceOf(account1);
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+
+ expect(updatedFeeSharingProxyLoanBalanceToken.toString()).to.be.equal(
+ ((amountLend * 7) / 10).toString()
+ );
+ expect(updatedAccount1LoanBalanceToken.toString()).to.be.equal(
+ ((amountLend * 3) / 10).toString()
+ );
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account1,
+ token: loanToken.address,
+ amount: amountLend.mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (WRBTC pool)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10)));
+
+ let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
+ let tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [10],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+
+ /// @dev To anticipate gas consumption it is required to split hardhat
+ /// behaviour into two different scenarios: coverage and regular testing.
+ /// On coverage gasPrice = 1, on regular tests gasPrice = 8000000000
+ //
+ // On coverage:
+ // Fees: 1800000000
+ // Balance: 10000000000000000000000
+ // Balance: 10000000000001799398877
+ // withdraw().gasUsed: 601123
+ // txFee: 601123
+ //
+ // On regular test:
+ // Fees: 1800000000
+ // Balance: 10000000000000000000000
+ // Balance: 9999996433281800000000
+ // withdraw().gasUsed: 445840
+ // txFee: 3566720000000000
+ let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
+ let gasPrice;
+ /// @dev A balance decrease (negative difference) corresponds to regular test case
+ if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") {
+ gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice));
+ } // regular test
+ else {
+ gasPrice = new BN(1);
+ } // coverage
+
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+ let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice);
+
+ userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee));
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ // check balances
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10);
+ let userLoanTokenBalance = await loanWrappedNativeToken.balanceOf.call(account1);
+ expect(userLoanTokenBalance.toNumber()).to.be.equal(0);
+ let userExpectedBtcBalance = userInitialBtcBalance.add(
+ feeAmount.mul(new BN(3)).div(new BN(10))
+ );
+ expect(userLatestBTCBalance.toString()).to.be.equal(userExpectedBtcBalance.toString());
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account1,
+ token: loanWrappedNativeToken.address,
+ amount: feeAmount.mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (sov pool)", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ false,
+ true
+ );
+
+ await feeSharingCollector.withdrawFees([SOVToken.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address);
+ expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10)));
+
+ let userInitialISOVBalance = await SOVToken.balanceOf(account1);
+ let tx = await feeSharingCollector.withdrawTokens(
+ [SOVToken.address],
+ [10],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+
+ //processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ SOVToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ //check balances
+ let feeSharingProxyBalance = await SOVToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10);
+ let userBalance = await SOVToken.balanceOf.call(account1);
+ expect(userBalance.sub(userInitialISOVBalance).toNumber()).to.be.equal(
+ (feeAmount * 3) / 10
+ );
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account1,
+ token: SOVToken.address,
+ amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)),
+ });
+ });
+
+ it("Should be able to withdraw (sov pool) to another account", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ //stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ //mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld,
+ false,
+ true
+ );
+
+ await feeSharingCollector.withdrawFees([SOVToken.address]);
+
+ let fees = await feeSharingCollector.getAccumulatedFees(account1, SOVToken.address);
+ expect(fees).to.be.bignumber.equal(feeAmount.mul(new BN(3)).div(new BN(10)));
+
+ const receiverBalanceBefore = await SOVToken.balanceOf(account2);
+ let tx = await feeSharingCollector.withdrawTokens([SOVToken.address], [10], account2, {
+ from: account1,
+ });
+
+ //processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ SOVToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ //check balances
+ let feeSharingProxyBalance = await SOVToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((feeAmount * 7) / 10);
+ const receiverBalanceAfter = await SOVToken.balanceOf(account2);
+ const amountWithdrawn = new BN(feeAmount).mul(new BN(3)).div(new BN(10));
+ expect(receiverBalanceAfter.sub(receiverBalanceBefore).toString()).to.be.equal(
+ amountWithdrawn.toString()
+ );
+
+ expectEvent(tx, "UserFeeWithdrawn", {
+ sender: account1,
+ receiver: account2,
+ token: SOVToken.address,
+ amount: amountWithdrawn,
+ });
+ });
+
+ it("Should be able to withdraw using 3 checkpoints", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 900;
+ await stake(rootStake, root);
+
+ let userStake = 100;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // [FIRST]
+ // mock data
+ let lendingFeeTokensHeld = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld = new BN(wei("3", "gwei"));
+ let totalFeeTokensHeld = lendingFeeTokensHeld
+ .add(tradingFeeTokensHeld)
+ .add(borrowingFeeTokensHeld);
+ let feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld,
+ tradingFeeTokensHeld,
+ borrowingFeeTokensHeld
+ );
+ let totalFeeAmount = feeAmount;
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
+ let tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [1],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+
+ /// @dev Same as above gas consumption is different on regular tests than on coverge
+ let userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
+ let gasPrice;
+ /// @dev A balance decrease (negative difference) corresponds to regular test case
+ if (userLatestBTCBalance.sub(userInitialBtcBalance).toString()[0] == "-") {
+ gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice));
+ } // regular test
+ else {
+ gasPrice = new BN(1);
+ } // coverage
+
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+ let txFee = new BN(tx.receipt.gasUsed).mul(gasPrice);
+
+ userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee));
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(1);
+
+ // check balances
+ let feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal((totalFeeAmount * 9) / 10);
+ let userBalance = await loanWrappedNativeToken.balanceOf.call(account1);
+ expect(userBalance.toNumber()).to.be.equal(0);
+
+ expect(userLatestBTCBalance.toString()).to.be.equal(
+ userInitialBtcBalance.add(totalFeeAmount.mul(new BN(1)).div(new BN(10))).toString()
+ );
+
+ // [SECOND]
+ // mock data
+ let lendingFeeTokensHeld2 = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld2 = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld2 = new BN(wei("3", "gwei"));
+ totalFeeTokensHeld = lendingFeeTokensHeld2
+ .add(tradingFeeTokensHeld2)
+ .add(borrowingFeeTokensHeld2);
+ feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld2,
+ tradingFeeTokensHeld2,
+ borrowingFeeTokensHeld2
+ );
+ totalFeeAmount = totalFeeAmount.add(feeAmount);
+ let totalLoanTokenWRBTCBalanceShouldBeAccount1 = feeAmount;
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ // [THIRD]
+ // mock data
+ let lendingFeeTokensHeld3 = new BN(wei("1", "gwei"));
+ let tradingFeeTokensHeld3 = new BN(wei("2", "gwei"));
+ let borrowingFeeTokensHeld3 = new BN(wei("3", "gwei"));
+ totalFeeTokensHeld = lendingFeeTokensHeld3
+ .add(tradingFeeTokensHeld3)
+ .add(borrowingFeeTokensHeld3);
+ feeAmount = await setFeeTokensHeld(
+ lendingFeeTokensHeld3,
+ tradingFeeTokensHeld3,
+ borrowingFeeTokensHeld3
+ );
+ totalFeeAmount = totalFeeAmount.add(feeAmount);
+ totalLoanTokenWRBTCBalanceShouldBeAccount1 =
+ totalLoanTokenWRBTCBalanceShouldBeAccount1.add(feeAmount);
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ // [SECOND] - [THIRD]
+ userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
+ tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [2],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ gasPrice = new BN(parseInt(tx.receipt.effectiveGasPrice));
+ console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed);
+ txFee = new BN(tx.receipt.gasUsed).mul(gasPrice);
+
+ userInitialBtcBalance = userInitialBtcBalance.sub(new BN(txFee));
+
+ // processedCheckpoints
+ processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(3);
+
+ // check balances
+ feeSharingProxyBalance = await loanWrappedNativeToken.balanceOf.call(
+ feeSharingCollector.address
+ );
+ expect(feeSharingProxyBalance.toNumber()).to.be.equal(
+ parseInt((totalFeeAmount * 9) / 10)
+ );
+ userBalance = await loanWrappedNativeToken.balanceOf.call(account1);
+ expect(userBalance.toNumber()).to.be.equal(0);
+
+ userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
+
+ expect(userLatestBTCBalance.toString()).to.be.equal(
+ userInitialBtcBalance
+ .add(totalLoanTokenWRBTCBalanceShouldBeAccount1.mul(new BN(1)).div(new BN(10)))
+ .toString()
+ );
+ });
+
+ it("Should be able to process 10 checkpoints", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ await stake(900, root);
+ let userStake = 100;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ await createCheckpoints(10);
+
+ let tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [1000],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 10).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(10);
+ });
+
+ it("Should be able to process 10 checkpoints and 3 withdrawals", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ await stake(900, root);
+ let userStake = 100;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ // mock data
+ await createCheckpoints(10);
+
+ let tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [5],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 5).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(5);
+
+ tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [3],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 3).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(8);
+
+ tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [1000],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 2).gasUsed: " + tx.receipt.gasUsed);
+ // processedCheckpoints
+ processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ );
+ expect(processedCheckpoints.toNumber()).to.be.equal(10);
+ });
+
+ // // use for gas usage tests
+ // it("Should be able to process 30 checkpoints", async () => {
+ // // stake - getPriorTotalVotingPower
+ // await stake(900, root);
+ // let userStake = 100;
+ // if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ // await staking.MOCK_priorWeightedStake(userStake * 10);
+ // }
+ // await SOVToken.transfer(account1, userStake);
+ // await stake(userStake, account1);
+ //
+ // // mock data
+ // await createCheckpoints(30);
+ //
+ // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1});
+ // console.log("\nwithdraw(checkpoints = 30).gasUsed: " + tx.receipt.gasUsed);
+ // // processedCheckpoints
+ // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address);
+ // expect(processedCheckpoints.toNumber()).to.be.equal(30);
+ // });
+ //
+ // // use for gas usage tests
+ // it("Should be able to process 100 checkpoints", async () => {
+ // // stake - getPriorTotalVotingPower
+ // await stake(900, root);
+ // let userStake = 100;
+ // if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ // await staking.MOCK_priorWeightedStake(userStake * 10);
+ // }
+ // await SOVToken.transfer(account1, userStake);
+ // await stake(userStake, account1);
+ //
+ // // mock data
+ // await createCheckpoints(100);
+ //
+ // let tx = await feeSharingCollector.withdraw(loanToken.address, 1000, ZERO_ADDRESS, {from: account1});
+ // console.log("\nwithdraw(checkpoints = 500).gasUsed: " + tx.receipt.gasUsed);
+ // // processedCheckpoints
+ // let processedCheckpoints = await feeSharingCollector.processedCheckpoints.call(account1, loanToken.address);
+ // expect(processedCheckpoints.toNumber()).to.be.equal(100);
+ // });
+ //
+ // // use for gas usage tests
+ // it("Should be able to withdraw when staking contains a lot of checkpoints", async () => {
+ // let checkpointCount = 1000;
+ // await stake(1000, root, checkpointCount);
+ // let afterBlock = await blockNumber();
+ // console.log(afterBlock);
+ //
+ // let kickoffTS = await staking.kickoffTS.call();
+ // let stakingDate = kickoffTS.add(new BN(MAX_DURATION));
+ //
+ // let numUserStakingCheckpoints = await staking.numUserStakingCheckpoints.call(root, stakingDate);
+ // let firstCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, 0);
+ // let lastCheckpoint = await staking.userStakingCheckpoints.call(root, stakingDate, numUserStakingCheckpoints - 1);
+ // let block1 = firstCheckpoint.fromBlock.toNumber() + 1;
+ // let block2 = lastCheckpoint.fromBlock;
+ //
+ // console.log("numUserStakingCheckpoints = " + numUserStakingCheckpoints.toString());
+ // console.log("first = " + firstCheckpoint.fromBlock.toString());
+ // console.log("last = " + lastCheckpoint.fromBlock.toString());
+ //
+ // let tx = await staking.calculatePriorWeightedStake(root, block1, stakingDate);
+ // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed);
+ // tx = await staking.calculatePriorWeightedStake(root, block2, stakingDate);
+ // console.log("\ncalculatePriorWeightedStake(checkpoints = " + checkpointCount + ").gasUsed: " + tx.receipt.gasUsed);
+ // });
+
+ it("Should be able to withdraw with staking for 78 dates", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // stake - getPriorTotalVotingPower
+ let rootStake = 700;
+ await stake(rootStake, root);
+
+ let userStake = 300;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ let kickoffTS = await staking.kickoffTS.call();
+ await SOVToken.approve(staking.address, userStake * 1000);
+ for (let i = 0; i < 77; i++) {
+ let stakingDate = kickoffTS.add(new BN(TWO_WEEKS * (i + 1)));
+ await staking.stake(userStake, stakingDate, account1, account1);
+ }
+
+ // mock data
+ await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+
+ let tx = await feeSharingCollector.withdrawTokens(
+ [loanWrappedNativeToken.address],
+ [10],
+ ZERO_ADDRESS,
+ {
+ from: account1,
+ }
+ );
+ console.log("\nwithdraw(checkpoints = 1).gasUsed: " + tx.receipt.gasUsed);
+ });
+
+ it("should compute the weighted stake and show gas usage", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ await stake(100, root);
+ let kickoffTS = await staking.kickoffTS.call();
+ let stakingDate = kickoffTS.add(new BN(MAX_DURATION));
+ await SOVToken.approve(staking.address, 100);
+ let result = await staking.stake("100", stakingDate, root, root);
+ await mineBlock();
+
+ let tx = await iWeightedStakingModuleMockup.calculatePriorWeightedStake(
+ root,
+ result.receipt.blockNumber,
+ stakingDate
+ );
+ console.log("\ngasUsed: " + tx.receipt.gasUsed);
+ });
+ });
+
+ describe("withdraw with or considering vesting contracts", () => {
+ it("getAccumulatedFees should return 0 for vesting contracts", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ let { vestingInstance } = await createVestingContractWithSingleDate(
+ new BN(MAX_DURATION),
+ 1000,
+ root
+ );
+ await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+ let fees = await feeSharingCollector.getAccumulatedFees(
+ vestingInstance.address,
+ loanToken.address
+ );
+ expect(fees).to.be.bignumber.equal("0");
+ });
+
+ it("vesting contract should not be able to withdraw fees", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ let { vestingInstance } = await createVestingContractWithSingleDate(
+ new BN(MAX_DURATION),
+ 1000,
+ root
+ );
+ await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+ await expectRevert(
+ vestingInstance.collectDividends(loanToken.address, 5, root),
+ "FeeSharingCollectorMultiToken::withdrawFees: no tokens for a withdrawal"
+ );
+ });
+
+ it("vested stakes should be deducted from total weighted stake on share distribution", async () => {
+ /// @dev This test requires redeploying the protocol
+ await protocolDeploymentFixture();
+
+ // 50% vested 50% voluntary stakes
+ await createVestingContractWithSingleDate(new BN(MAX_DURATION), 1000, root);
+ let userStake = 1000;
+ if (MOCK_PRIOR_WEIGHTED_STAKE) {
+ await staking.MOCK_priorWeightedStake(userStake * 10);
+ }
+ await SOVToken.transfer(account1, userStake);
+ await stake(userStake, account1);
+
+ await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+ let tx = await feeSharingCollector.withdrawFees([SUSD.address]);
+ let feesWithdrawn = tx.logs[1].args.amount;
+ let userFees = await feeSharingCollector.getAccumulatedFees(
+ account1,
+ loanWrappedNativeToken.address
+ );
+
+ // 100% of the fees should go to the user -> vesting contract not considered
+ expect(feesWithdrawn).to.be.bignumber.equal(userFees);
+ });
+ });
+
+ describe("withdraw wrbtc", async () => {
+ it("Withdraw wrappedNativeToken from non owner should revert", async () => {
+ await protocolDeploymentFixture();
+ const receiver = accounts[1];
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ await expectRevert(
+ feeSharingCollector.withdrawWrappedNativeToken(receiver, 0, { from: accounts[1] }),
+ "unauthorized"
+ );
+ });
+
+ it("Withdraw 0 wrbtc", async () => {
+ await protocolDeploymentFixture();
+ const receiver = accounts[1];
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ await feeSharingCollector.withdrawWrappedNativeToken(receiver, 0);
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ expect(
+ new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString()
+ ).to.equal("0");
+ expect(latestBalanceFeeSharingProxy.toString()).to.equal("0");
+ });
+
+ it("Withdraw wrappedNativeToken more than the balance of feeSharingProxy should revert", async () => {
+ await protocolDeploymentFixture();
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
+
+ const receiver = accounts[1];
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const feeSharingProxyBalance = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+ const amount = feeSharingProxyBalance.add(new BN(100));
+ const previousBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ await expectRevert(
+ feeSharingCollector.withdrawWrappedNativeToken(receiver, amount.toString()),
+ "Insufficient balance"
+ );
+
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ expect(
+ new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString()
+ ).to.equal("0");
+ expect(latestBalanceFeeSharingProxy.toString()).to.equal(
+ previousBalanceFeeSharingProxy.toString()
+ );
+ });
+
+ it("Fully Withdraw wrbtc", async () => {
+ await protocolDeploymentFixture();
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
+
+ const receiver = accounts[1];
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const feeSharingProxyBalance = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ const tx = await feeSharingCollector.withdrawWrappedNativeToken(
+ receiver,
+ feeSharingProxyBalance.toString()
+ );
+ await expectEvent.inTransaction(
+ tx.receipt.rawLogs[0].transactionHash,
+ WrappedNativeToken,
+ "Transfer",
+ {
+ src: feeSharingCollector.address,
+ dst: receiver,
+ wad: feeSharingProxyBalance.toString(),
+ }
+ );
+
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ expect(
+ new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString()
+ ).to.equal(feeSharingProxyBalance.toString());
+ expect(latestBalanceFeeSharingProxy.toString()).to.equal("0");
+ });
+
+ it("Partially Withdraw wrbtc", async () => {
+ await protocolDeploymentFixture();
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
+
+ const receiver = accounts[1];
+ const restAmount = new BN("100"); // 100 wei
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const feeSharingProxyBalance = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+ const amount = feeSharingProxyBalance.sub(restAmount);
+ const previousBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+ expect(previousBalanceFeeSharingProxy.toString()).to.equal(wei("1", "ether"));
+
+ const tx = await feeSharingCollector.withdrawWrappedNativeToken(
+ receiver,
+ amount.toString()
+ );
+ await expectEvent.inTransaction(
+ tx.receipt.rawLogs[0].transactionHash,
+ WrappedNativeToken,
+ "Transfer",
+ {
+ src: feeSharingCollector.address,
+ dst: receiver,
+ wad: amount,
+ }
+ );
+
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+
+ expect(
+ new BN(latestBalanceReceiver).sub(new BN(previousBalanceReceiver)).toString()
+ ).to.equal(amount.toString());
+ expect(latestBalanceFeeSharingProxy.toString()).to.equal(restAmount.toString());
+
+ // try to withdraw the rest
+ const tx2 = await feeSharingCollector.withdrawWrappedNativeToken(
+ receiver,
+ latestBalanceFeeSharingProxy.toString()
+ );
+ const finalBalanceFeeSharingProxy = await WrappedNativeToken.balanceOf(
+ feeSharingCollector.address
+ );
+ const finalBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ expect(new BN(finalBalanceReceiver).toString()).to.equal(
+ previousBalanceFeeSharingProxy.toString()
+ );
+ expect(finalBalanceFeeSharingProxy.toString()).to.equal("0");
+
+ await expectEvent.inTransaction(
+ tx2.receipt.rawLogs[0].transactionHash,
+ WrappedNativeToken,
+ "Transfer",
+ {
+ src: feeSharingCollector.address,
+ dst: receiver,
+ wad: latestBalanceFeeSharingProxy.toString(),
+ }
+ );
+ });
+ });
+
+ async function stake(amount, user, checkpointCount) {
+ await SOVToken.approve(staking.address, amount);
+ let kickoffTS = await staking.kickoffTS.call();
+ let stakingDate = kickoffTS.add(new BN(MAX_DURATION));
+ let tx = await staking.stake(amount, stakingDate, user, user);
+ await mineBlock();
+
+ if (checkpointCount > 0) {
+ await increaseStake(amount, user, stakingDate, checkpointCount - 1);
+ }
+
+ return tx;
+ }
+
+ async function increaseStake(amount, user, stakingDate, checkpointCount) {
+ for (let i = 0; i < checkpointCount; i++) {
+ await SOVToken.approve(staking.address, amount);
+ await staking.increaseStake(amount, user, stakingDate);
+ }
+ }
+
+ async function setFeeTokensHeld(
+ lendingFee,
+ tradingFee,
+ borrowingFee,
+ wrbtcTokenFee = false,
+ sovTokenFee = false
+ ) {
+ let totalFeeAmount = lendingFee.add(tradingFee).add(borrowingFee);
+ let tokenFee;
+ if (wrbtcTokenFee) {
+ tokenFee = WrappedNativeToken;
+ } else {
+ tokenFee = SUSD;
+ await tokenFee.transfer(sovryn.address, totalFeeAmount);
+ }
+ await sovryn.setLendingFeeTokensHeld(tokenFee.address, lendingFee);
+ await sovryn.setTradingFeeTokensHeld(tokenFee.address, tradingFee);
+ await sovryn.setBorrowingFeeTokensHeld(tokenFee.address, borrowingFee);
+
+ if (sovTokenFee) {
+ await SOVToken.transfer(sovryn.address, totalFeeAmount);
+ await sovryn.setLendingFeeTokensHeld(SOVToken.address, lendingFee);
+ await sovryn.setTradingFeeTokensHeld(SOVToken.address, tradingFee);
+ await sovryn.setBorrowingFeeTokensHeld(SOVToken.address, borrowingFee);
+ }
+ return totalFeeAmount;
+ }
+
+ async function checkWithdrawFee(checkSUSD = true, checkWRBTC = false, checkSOV = false) {
+ if (checkSUSD) {
+ let protocolBalance = await SUSD.balanceOf(sovryn.address);
+ expect(protocolBalance.toString()).to.be.equal(new BN(0).toString());
+ let lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(SUSD.address);
+ expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ let tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(SUSD.address);
+ expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ let borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(SUSD.address);
+ expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ }
+
+ if (checkWRBTC) {
+ lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(
+ WrappedNativeToken.address
+ );
+ expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(
+ WrappedNativeToken.address
+ );
+ expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(
+ WrappedNativeToken.address
+ );
+ expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ }
+
+ if (checkSOV) {
+ protocolBalance = await SOVToken.balanceOf(sovryn.address);
+ expect(protocolBalance.toString()).to.be.equal(new BN(0).toString());
+ lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(SOVToken.address);
+ expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(SOVToken.address);
+ expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(SOVToken.address);
+ expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
+ }
+ }
+
+ async function createCheckpoints(number) {
+ for (let i = 0; i < number; i++) {
+ await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
+ await increaseTime(FEE_WITHDRAWAL_INTERVAL);
+ await feeSharingCollector.withdrawFees([SUSD.address]);
+ }
+ }
+
+ async function createVestingContractWithSingleDate(cliff, amount, tokenOwner) {
+ vestingLogic = await VestingLogic.new();
+ let vestingInstance = await Vesting.new(
+ vestingLogic.address,
+ SOVToken.address,
+ staking.address,
+ tokenOwner,
+ cliff,
+ cliff,
+ feeSharingCollector.address
+ );
+ vestingInstance = await VestingLogic.at(vestingInstance.address);
+ // important, so it's recognized as vesting contract
+ await staking.addContractCodeHash(vestingInstance.address);
+
+ await SOVToken.approve(vestingInstance.address, amount);
+ let result = await vestingInstance.stakeTokens(amount);
+ return { vestingInstance: vestingInstance, blockNumber: result.receipt.blockNumber };
+ }
+});
diff --git a/tests/FeeSharingCollectorTest.js b/tests/FeeSharingCollectorTest.js
index a511b11f2..7eed1bce3 100644
--- a/tests/FeeSharingCollectorTest.js
+++ b/tests/FeeSharingCollectorTest.js
@@ -16,7 +16,7 @@
* It didn't work.
* Update to use initializer.js SUSD.
* It works Ok.
- * Update to use WRBTC as collateral token, instead of custom testWrbtc.
+ * Update to use wrappedNativeToken as collateral token, instead of custom testWrappedNativeToken.
* It works Ok.
* Update to use initializer.js SOV.
* It didn't work.
@@ -124,12 +124,12 @@ contract("FeeSharingCollector:", (accounts) => {
let RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT;
let root, account1, account2, account3, account4;
- let SOVToken, SUSD, WRBTC, sovryn, staking;
+ let SOVToken, SUSD, WrappedNativeToken, sovryn, staking;
let loanTokenSettings, loanTokenLogic, loanToken;
let feeSharingCollectorProxyObj;
let feeSharingCollector;
let feeSharingCollectorLogic;
- let loanTokenWrbtc;
+ let loanWrappedNativeToken;
let tradingFeePercent;
let mockPrice;
let liquidityPoolV1Converter;
@@ -176,10 +176,10 @@ contract("FeeSharingCollector:", (accounts) => {
iWeightedStakingModuleMockup = await IWeightedStakingModuleMockup.at(staking.address);
SUSD = await getSUSD();
- RBTC = await getRBTC();
- WRBTC = await getWRBTC();
+ NativeToken = await getRBTC();
+ WrappedNativeToken = await getWRBTC();
BZRX = await getBZRX();
- priceFeeds = await getPriceFeeds(WRBTC, SUSD, RBTC, BZRX);
+ priceFeeds = await getPriceFeeds(WrappedNativeToken, SUSD, NativeToken, BZRX);
// Deploying sovrynProtocol w/ generic function from initializer.js
/// @dev Tried but no success so far. When using the getSovryn function
@@ -187,7 +187,7 @@ contract("FeeSharingCollector:", (accounts) => {
/// The weird thing is that deployment code below is exactly the same as
/// the code from getSovryn function at initializer.js.
/// Inline code works ok, but when calling the function it does not.
- // sovryn = await getSovryn(WRBTC, SUSD, RBTC, priceFeeds);
+ // sovryn = await getSovryn(WrappedNativeToken, SUSD, NativeToken, priceFeeds);
// await sovryn.setSovrynProtocolAddress(sovryn.address);
const sovrynproxy = await Protocol.new();
@@ -198,7 +198,7 @@ contract("FeeSharingCollector:", (accounts) => {
await sovryn.replaceContract((await LoanMaintenance.new()).address);
await sovryn.replaceContract((await SwapsExternal.new()).address);
- await sovryn.setWrbtcToken(WRBTC.address);
+ await sovryn.setWrbtcToken(WrappedNativeToken.address);
await sovryn.replaceContract((await LoanClosingsWith.new()).address);
await sovryn.replaceContract((await LoanClosingsLiquidation.new()).address);
@@ -217,7 +217,7 @@ contract("FeeSharingCollector:", (accounts) => {
root,
loanTokenLogic.address,
sovryn.address,
- WRBTC.address
+ WrappedNativeToken.address
);
await loanToken.initialize(SUSD.address, "iSUSD", "iSUSD");
@@ -241,33 +241,40 @@ contract("FeeSharingCollector:", (accounts) => {
await sovryn.setFeesController(feeSharingCollector.address);
- // Set loan pool for wRBTC -- because our fee sharing proxy required the loanPool of wRBTC
+ // Set loan pool for wrappedNativeToken -- because our fee sharing proxy required the loanPool of wrappedNativeToken
// Loan token
const initLoanTokenLogicWrbtc = await getLoanTokenLogicWrbtc(); // function will return [LoanTokenLogicProxy, LoanTokenLogicBeacon]
loanTokenLogicWrbtc = initLoanTokenLogicWrbtc[0];
loanTokenLogicBeaconWrbtc = initLoanTokenLogicWrbtc[1];
- loanTokenWrbtc = await LoanToken.new(
+ loanWrappedNativeToken = await LoanToken.new(
root,
loanTokenLogicWrbtc.address,
sovryn.address,
- WRBTC.address
+ WrappedNativeToken.address
+ );
+ await loanWrappedNativeToken.initialize(
+ WrappedNativeToken.address,
+ "iWrappedNativeToken",
+ "iWrappedNativeToken"
);
- await loanTokenWrbtc.initialize(WRBTC.address, "iWRBTC", "iWRBTC");
/** Initialize the loan token logic proxy */
- loanTokenWrbtc = await ILoanTokenLogicProxy.at(loanTokenWrbtc.address);
- await loanTokenWrbtc.setBeaconAddress(loanTokenLogicBeaconWrbtc.address);
+ loanWrappedNativeToken = await ILoanTokenLogicProxy.at(loanWrappedNativeToken.address);
+ await loanWrappedNativeToken.setBeaconAddress(loanTokenLogicBeaconWrbtc.address);
/** Use interface of LoanTokenModules */
- loanTokenWrbtc = await ILoanTokenModules.at(loanTokenWrbtc.address);
+ loanWrappedNativeToken = await ILoanTokenModules.at(loanWrappedNativeToken.address);
- const loanTokenAddressWrbtc = await loanTokenWrbtc.loanTokenAddress();
- await sovryn.setLoanPool([loanTokenWrbtc.address], [loanTokenAddressWrbtc]);
+ const loanTokenAddressWrappedNativeToken = await loanWrappedNativeToken.loanTokenAddress();
+ await sovryn.setLoanPool(
+ [loanWrappedNativeToken.address],
+ [loanTokenAddressWrappedNativeToken]
+ );
- await WRBTC.mint(sovryn.address, wei("500", "ether"));
+ await WrappedNativeToken.mint(sovryn.address, wei("500", "ether"));
- await sovryn.setWrbtcToken(WRBTC.address);
+ await sovryn.setWrbtcToken(WrappedNativeToken.address);
await sovryn.setSOVTokenAddress(SOVToken.address);
await sovryn.setSovrynProtocolAddress(sovryn.address);
@@ -292,13 +299,13 @@ contract("FeeSharingCollector:", (accounts) => {
);
// Set PriceFeeds
- feeds = await PriceFeedsLocal.new(WRBTC.address, sovryn.address);
+ feeds = await PriceFeedsLocal.new(WrappedNativeToken.address, sovryn.address);
mockPrice = "1";
- await feeds.setRates(SUSD.address, WRBTC.address, wei(mockPrice, "ether"));
+ await feeds.setRates(SUSD.address, WrappedNativeToken.address, wei(mockPrice, "ether"));
const swaps = await SwapsImplSovrynSwap.new();
const sovrynSwapSimulator = await TestSovrynSwap.new(feeds.address);
await sovryn.setSovrynSwapContractRegistryAddress(sovrynSwapSimulator.address);
- await sovryn.setSupportedTokens([SUSD.address, WRBTC.address], [true, true]);
+ await sovryn.setSupportedTokens([SUSD.address, WrappedNativeToken.address], [true, true]);
await sovryn.setPriceFeedContract(
feeds.address // priceFeeds
);
@@ -308,7 +315,7 @@ contract("FeeSharingCollector:", (accounts) => {
tradingFeePercent = await sovryn.tradingFeePercent();
- await lend_btc_before_cashout(loanTokenWrbtc, new BN(wei("10", "ether")), root);
+ await lend_btc_before_cashout(loanWrappedNativeToken, new BN(wei("10", "ether")), root);
const maxDisagreement = new BN(wei("5", "ether"));
await sovryn.setMaxDisagreement(maxDisagreement);
@@ -316,7 +323,10 @@ contract("FeeSharingCollector:", (accounts) => {
RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT =
await feeSharingCollector.RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT();
- await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address);
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ );
return sovryn;
}
@@ -326,128 +336,167 @@ contract("FeeSharingCollector:", (accounts) => {
});
describe("initialization", async () => {
- it("initialize should revert if wrbtc has been set", async () => {
+ it("initialize should revert if wrappedNativeToken has been set", async () => {
const feeSharingCollectorMock = await FeeSharingCollectorMockup.new(
sovryn.address,
staking.address
);
- await feeSharingCollectorMock.setWrbtcToken(WRBTC.address);
- expect(await feeSharingCollectorMock.wrbtcTokenAddress()).to.equal(WRBTC.address);
+ await feeSharingCollectorMock.setWrappedNativeToken(WrappedNativeToken.address);
+ expect(await feeSharingCollectorMock.wrappedNativeTokenAddress()).to.equal(
+ WrappedNativeToken.address
+ );
await expectRevert(
- feeSharingCollectorMock.initialize(WRBTC.address, loanTokenWrbtc.address),
- "wrbtcToken or loanWrbtcToken has been initialized"
+ feeSharingCollectorMock.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ ),
+ "wrappedNativeToken or loanWrappedNativeToken has been initialized"
);
});
- it("initialize should revert if iWrbtc has been set", async () => {
+ it("initialize should revert if iWrappedNativeToken has been set", async () => {
const feeSharingCollectorMock = await FeeSharingCollectorMockup.new(
sovryn.address,
staking.address
);
- await feeSharingCollectorMock.setWrbtcToken(WRBTC.address);
- expect(await feeSharingCollectorMock.wrbtcTokenAddress()).to.equal(WRBTC.address);
+ await feeSharingCollectorMock.setWrappedNativeToken(WrappedNativeToken.address);
+ expect(await feeSharingCollectorMock.wrappedNativeTokenAddress()).to.equal(
+ WrappedNativeToken.address
+ );
await expectRevert(
- feeSharingCollectorMock.initialize(WRBTC.address, loanTokenWrbtc.address),
- "wrbtcToken or loanWrbtcToken has been initialized"
+ feeSharingCollectorMock.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ ),
+ "wrappedNativeToken or loanWrappedNativeToken has been initialized"
);
});
it("revert if initialize called by non-owner account", async () => {
await expectRevert(
- feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address, {
- from: account3,
- }),
+ feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ {
+ from: account3,
+ }
+ ),
"unauthorized"
);
});
- it("revert if setWrbtcToken called by non-owner account", async () => {
+ it("revert if setWrappedNativeToken called by non-owner account", async () => {
await expectRevert(
- feeSharingCollector.setWrbtcToken(WRBTC.address, { from: account3 }),
+ feeSharingCollector.setWrappedNativeToken(WrappedNativeToken.address, {
+ from: account3,
+ }),
"unauthorized"
);
});
- it("revert if setLoanTokenWrbtc called by non-owner account", async () => {
+ it("revert if setLoanWrappedNativeToken called by non-owner account", async () => {
await expectRevert(
- feeSharingCollector.setLoanTokenWrbtc(loanTokenWrbtc.address, { from: account3 }),
+ feeSharingCollector.setLoanWrappedNativeToken(loanWrappedNativeToken.address, {
+ from: account3,
+ }),
"unauthorized"
);
});
it("should revert if initialized more than once", async () => {
- const wrbtcAddress = (await TestToken.new("WRBTC", "WRBTC", 18, 100)).address;
- const loanTokenWrbtcAddress = (await TestToken.new("IWRBTC", "IWRBTC", 18, 100))
- .address;
+ const wrappedNativeTokenAddress = (
+ await TestToken.new("WrappedNativeToken", "WNT", 18, 100)
+ ).address;
+ const loanWrappedNativeTokenAddress = (
+ await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100)
+ ).address;
await expectRevert(
- feeSharingCollector.initialize(wrbtcAddress, loanTokenWrbtcAddress),
+ feeSharingCollector.initialize(
+ wrappedNativeTokenAddress,
+ loanWrappedNativeTokenAddress
+ ),
"function can only be called once"
);
});
- it("setWrbtcToken should revert if try to set non-contract address", async () => {
- expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(WRBTC.address);
- let newInvalidWrbtcAddress = accounts[0];
+ it("setWrappedNativeToken should revert if try to set non-contract address", async () => {
+ expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal(
+ WrappedNativeToken.address
+ );
+ let newInvalidWrappedNativeTokenAddress = accounts[0];
await expectRevert(
- feeSharingCollector.setWrbtcToken(newInvalidWrbtcAddress),
- "newWrbtcTokenAddress not a contract"
+ feeSharingCollector.setWrappedNativeToken(newInvalidWrappedNativeTokenAddress),
+ "newWrappedNativeTokenAddress not a contract"
);
- newInvalidWrbtcAddress = ZERO_ADDRESS;
+ newInvalidWrappedNativeTokenAddress = ZERO_ADDRESS;
await expectRevert(
- feeSharingCollector.setWrbtcToken(newInvalidWrbtcAddress),
- "newWrbtcTokenAddress not a contract"
+ feeSharingCollector.setWrappedNativeToken(newInvalidWrappedNativeTokenAddress),
+ "newWrappedNativeTokenAddress not a contract"
+ );
+ expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal(
+ WrappedNativeToken.address
);
- expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(WRBTC.address);
});
- it("setWrbtcToken should set the wrbtc token address properly", async () => {
- expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(WRBTC.address);
- const newWrbtcAddress = (await TestToken.new("WRBTC", "WRBTC", 18, 100)).address;
- await feeSharingCollector.setWrbtcToken(newWrbtcAddress);
- expect(await feeSharingCollector.wrbtcTokenAddress()).to.equal(newWrbtcAddress);
+ it("setWrappedNativeToken should set the wrappedNativeToken token address properly", async () => {
+ expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal(
+ WrappedNativeToken.address
+ );
+ const newWrappedNativeTokenAddress = (
+ await TestToken.new("WrappedNativeToken", "WNT", 18, 100)
+ ).address;
+ await feeSharingCollector.setWrappedNativeToken(newWrappedNativeTokenAddress);
+ expect(await feeSharingCollector.wrappedNativeTokenAddress()).to.equal(
+ newWrappedNativeTokenAddress
+ );
});
- it("setLoanTokenWrbtc should revert if try to set non-contract addrerss", async () => {
- expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal(
- loanTokenWrbtc.address
+ it("setLoanWrappedNativeToken should revert if try to set non-contract addrerss", async () => {
+ expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal(
+ loanWrappedNativeToken.address
);
- let newInvalidLoanTokenWrbtcAddress = accounts[0];
+ let newInvalidLoanWrappedNativeTokenAddress = accounts[0];
await expectRevert(
- feeSharingCollector.setLoanTokenWrbtc(newInvalidLoanTokenWrbtcAddress),
- "newLoanTokenWrbtcAddress not a contract"
+ feeSharingCollector.setLoanWrappedNativeToken(
+ newInvalidLoanWrappedNativeTokenAddress
+ ),
+ "newLoanWrappedNativeTokenAddress not a contract"
);
- newInvalidLoanTokenWrbtcAddress = ZERO_ADDRESS;
+ newInvalidLoanWrappedNativeTokenAddress = ZERO_ADDRESS;
await expectRevert(
- feeSharingCollector.setLoanTokenWrbtc(newInvalidLoanTokenWrbtcAddress),
- "newLoanTokenWrbtcAddress not a contract"
+ feeSharingCollector.setLoanWrappedNativeToken(
+ newInvalidLoanWrappedNativeTokenAddress
+ ),
+ "newLoanWrappedNativeTokenAddress not a contract"
);
- expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal(
- loanTokenWrbtc.address
+ expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal(
+ loanWrappedNativeToken.address
);
});
- it("setLoanTokenWrbtc should set the wrbtc token address properly", async () => {
- expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal(
- loanTokenWrbtc.address
+ it("setLoanWrappedNativeToken should set the wrappedNativeToken token address properly", async () => {
+ expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal(
+ loanWrappedNativeToken.address
);
- const newLoanTokenWrbtcAddress = (await TestToken.new("IWRBTC", "IWRBTC", 18, 100))
- .address;
- await feeSharingCollector.setLoanTokenWrbtc(newLoanTokenWrbtcAddress);
- expect(await feeSharingCollector.loanTokenWrbtcAddress()).to.equal(
- newLoanTokenWrbtcAddress
+ const newLoanWrappedNativeTokenAddress = (
+ await TestToken.new("IWrappedNativeToken", "IWNT", 18, 100)
+ ).address;
+ await feeSharingCollector.setLoanWrappedNativeToken(newLoanWrappedNativeTokenAddress);
+ expect(await feeSharingCollector.loanWrappedNativeTokenAddress()).to.equal(
+ newLoanWrappedNativeTokenAddress
);
});
});
- describe("withdrawStartingFromCheckpoint, withdrawRBTCStartingFromCheckpoint, withdrawRbtcTokenStartingFromCheckpoint using claimAllCollectedFees(), and getNextPositiveUserCheckpoint", () => {
+ describe("withdrawStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint, withdrawNativeTokenStartingFromCheckpoint using claimAllCollectedFees(), and getNextPositiveUserCheckpoint", () => {
let snapshot;
before(async () => {
await loadFixture(protocolDeploymentFixture);
@@ -459,11 +508,11 @@ contract("FeeSharingCollector:", (accounts) => {
await snapshot.restore();
});
- // If calling withdrawStartingFromCheckpoint or withdrawRBTCStartingFromCheckpoint with _fromCheckpoint > processedCheckpoints[user][_loanPoolToken] it starts calculating the fees from _fromCheckpoint
+ // If calling withdrawStartingFromCheckpoint or withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint > processedCheckpoints[user][_loanPoolToken] it starts calculating the fees from _fromCheckpoint
it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly", async () => {
// To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -522,10 +571,10 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.equal(10);
});
- it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for rbtc & non-rbtc based tokens", async () => {
+ it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non native token based tokens", async () => {
// To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -574,12 +623,12 @@ contract("FeeSharingCollector:", (accounts) => {
fromCheckpoint: nextPositiveSOV.checkpointNum.toNumber(),
},
],
- 3, // 3 max checkpoint is enough to withdraw both RBTC & SOV Token completely
+ 3, // 3 max checkpoint is enough to withdraw both NativeToken * SOV Token completely
ZERO_ADDRESS,
{ from: account1 }
);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
amount: new BN(60),
});
@@ -608,10 +657,10 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpointsSOV.toNumber()).to.equal(10);
});
- it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for rbtc & non-rbtc based tokens (withdraw partially)", async () => {
+ it("withdrawStartingFromCheckpoint using claimAllCollectedFees() calculates fees correctly for nativeToken & non native token based tokens (withdraw partially)", async () => {
// To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -656,12 +705,12 @@ contract("FeeSharingCollector:", (accounts) => {
fromCheckpoint: nextPositiveSOV.checkpointNum.toNumber(),
},
],
- 1, // 1 max checkpoint is only enough to withdraw RBTC
+ 1, // 1 max checkpoint is only enough to withdraw NativeToken
ZERO_ADDRESS,
{ from: account1 }
);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
amount: new BN(60),
});
@@ -687,7 +736,7 @@ contract("FeeSharingCollector:", (accounts) => {
it("withdrawStartingFromCheckpoint using claimAllCollectedFees() works with large number of unprocessed token checkpoints", async () => {
// To test this, create 250 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -731,10 +780,10 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.equal(10);
});
- it("should be able to withdraw rbtc that has skipped checkpoints using claimAllCollectedFees calculates fees correctly (using zero addreses as reciever)", async () => {
- // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
+ it("should be able to withdraw nativeToken that has skipped checkpoints using claimAllCollectedFees calculates fees correctly (using zero addreses as reciever)", async () => {
+ // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -765,7 +814,7 @@ contract("FeeSharingCollector:", (accounts) => {
{ from: account1 }
);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
amount: new BN(60),
});
@@ -777,10 +826,10 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.equal(10);
});
- it("withraw rbtc token that has skipped checkpoints using claimAllCollectedFees() should calculates fees correctly (using actual address as receiver)", async () => {
- // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
+ it("withraw nativeToken token that has skipped checkpoints using claimAllCollectedFees() should calculates fees correctly (using actual address as receiver)", async () => {
+ // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -811,7 +860,7 @@ contract("FeeSharingCollector:", (accounts) => {
{ from: account1 }
);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
amount: new BN(60),
});
@@ -823,10 +872,10 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.equal(10);
});
- it("withdraw rbtc tokens that has skipped checkpoints using claimAllCollectedFees() won't be processed if passed maxCheckpoints is 0", async () => {
- // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
+ it("withdraw nativeToken tokens that has skipped checkpoints using claimAllCollectedFees() won't be processed if passed maxCheckpoints is 0", async () => {
+ // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -865,10 +914,10 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.equal(0);
});
- it("withdraw rbtc tokens that has skipped checkpoints using claimAllCollectedFees() should revert if non-rbtc token is passed", async () => {
- // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
+ it("withdraw nativeToken tokens that has skipped checkpoints using claimAllCollectedFees() should revert if non native token token is passed", async () => {
+ // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -894,14 +943,14 @@ contract("FeeSharingCollector:", (accounts) => {
ZERO_ADDRESS,
{ from: account1 }
),
- "only rbtc-based tokens are allowed"
+ "only native token based tokens are allowed"
);
});
- it("should not be able to pass non-rbtc based token as _rbtcTokensRegularWithdraw using claimAllCollectedFees() function", async () => {
- // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawRbtcTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
+ it("should not be able to pass non native token based token as _nativeTokensRegularWithdraw using claimAllCollectedFees() function", async () => {
+ // To test this, create 9 checkpoints while the user has no stake, then stake with the user, create another checkpoint and call withdrawNativeTokenStartingFromCheckpoint with _fromCheckpoint = 10 and _maxCheckpoints = 3
- /// RBTC
+ /// NativeToken
await stake(900, root);
const userStake = 100;
@@ -927,11 +976,11 @@ contract("FeeSharingCollector:", (accounts) => {
ZERO_ADDRESS,
{ from: account1 }
),
- "only rbtc-based tokens are allowed"
+ "only native token based tokens are allowed"
);
});
- it("getNextPositiveUserCheckpoint for RBTC returns the first checkpoint on which the user has a stake > 0", async () => {
+ it("getNextPositiveUserCheckpoint for NativeToken returns the first checkpoint on which the user has a stake > 0", async () => {
await stake(900, root);
const userStake = 100;
@@ -1284,7 +1333,7 @@ contract("FeeSharingCollector:", (accounts) => {
"User weighted stake should be zero at previous checkpoint"
);
- // RBTC
+ // NativeToken
for (let i = 0; i < 2; i++) {
await feeSharingCollector.addCheckPoint(
RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
@@ -1434,13 +1483,16 @@ contract("FeeSharingCollector:", (accounts) => {
]).to.eql([10, true, true]);
});
- it("getNextPositiveUserCheckpoint for RBTC returns correct [checkpointNum, hasSkippedCheckpoints, hasFees]", async () => {
+ it("getNextPositiveUserCheckpoint for NativeToken returns correct [checkpointNum, hasSkippedCheckpoints, hasFees]", async () => {
feeSharingCollector = await FeeSharingCollectorMockup.new(
sovryn.address,
staking.address
);
- await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address);
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ );
await sovryn.setFeesController(feeSharingCollector.address);
let nextCheckpoint = await feeSharingCollector.getNextPositiveUserCheckpoint(
@@ -1617,7 +1669,7 @@ contract("FeeSharingCollector:", (accounts) => {
it("Withdraw zero amount will success with the proper emitted event", async () => {
await protocolDeploymentFixture();
const tx = await feeSharingCollector.withdrawFees([SUSD.address]);
- expectEvent(tx, "FeeWithdrawnInRBTC", {
+ expectEvent(tx, "FeeWithdrawnInNativeToken", {
sender: root,
amount: new BN(0),
});
@@ -1644,21 +1696,25 @@ contract("FeeSharingCollector:", (accounts) => {
tradingFeeTokensHeld,
borrowingFeeTokensHeld
);
- let previousProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address);
+ let previousProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf(
+ protocol.address
+ );
// let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
await protocol.setFeesController(root);
let tx = await protocol.withdrawFees([SUSD.address], root);
- let latestProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address);
+ let latestProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf(
+ protocol.address
+ );
await checkWithdrawFee();
- //check wrbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let userBalance = await WRBTC.balanceOf.call(root);
+ //check wrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let userBalance = await WrappedNativeToken.balanceOf.call(root);
expect(userBalance.toString()).to.be.equal(feeAmount.toString());
- // wrbtc balance should remain the same
- expect(previousProtocolWrbtcBalance.toString()).to.equal(
- latestProtocolWrbtcBalance.toString()
+ // wrappedNativeToken balance should remain the same
+ expect(previousProtocolWrappedNativeTokenBalance.toString()).to.equal(
+ latestProtocolWrappedNativeTokenBalance.toString()
);
expectEvent(tx, "WithdrawFees", {
@@ -1668,11 +1724,11 @@ contract("FeeSharingCollector:", (accounts) => {
lendingAmount: lendingFeeTokensHeld,
tradingAmount: tradingFeeTokensHeld,
borrowingAmount: borrowingFeeTokensHeld,
- // amountConvertedToWRBTC
+ // amountConvertedToWrappedNativeToken
});
});
- it("ProtocolSettings.withdrawFees (WRBTC token)", async () => {
+ it("ProtocolSettings.withdrawFees (WrappedNativeToken token)", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -1696,22 +1752,22 @@ contract("FeeSharingCollector:", (accounts) => {
);
// let feeAmount = await setFeeTokensHeld(new BN(100), new BN(200), new BN(300));
await sovryn.setFeesController(root);
- let tx = await sovryn.withdrawFees([WRBTC.address], account1);
+ let tx = await sovryn.withdrawFees([WrappedNativeToken.address], account1);
await checkWithdrawFee(true, true, false);
- //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let userBalance = await WRBTC.balanceOf.call(account1);
+ //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let userBalance = await WrappedNativeToken.balanceOf.call(account1);
expect(userBalance.toString()).to.be.equal(feeAmount.toString());
expectEvent(tx, "WithdrawFees", {
sender: root,
- token: WRBTC.address,
+ token: WrappedNativeToken.address,
receiver: account1,
lendingAmount: lendingFeeTokensHeld,
tradingAmount: tradingFeeTokensHeld,
borrowingAmount: borrowingFeeTokensHeld,
- wRBTCConverted: new BN(feeAmount),
+ wrappedNativeTokenConverted: new BN(feeAmount),
});
});
@@ -1755,8 +1811,10 @@ contract("FeeSharingCollector:", (accounts) => {
tradingFeeTokensHeld,
borrowingFeeTokensHeld
);
- let previousProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address);
- let previousFeeSharingCollectorProxyRBTCBalance = new BN(
+ let previousProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf(
+ protocol.address
+ );
+ let previousFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
@@ -1764,34 +1822,37 @@ contract("FeeSharingCollector:", (accounts) => {
await checkWithdrawFee();
- //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ //check iNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
// feeSharingCollector no longer provides the liquidity to lending pool.
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal(
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal(
new BN(0).toString()
);
- // wrbtc balance should remain the same
- let latestProtocolWrbtcBalance = await WRBTC.balanceOf(protocol.address);
- expect(previousProtocolWrbtcBalance.toString()).to.equal(
- latestProtocolWrbtcBalance.toString()
+ // wrappedNativeToken balance should remain the same
+ let latestProtocolWrappedNativeTokenBalance = await WrappedNativeToken.balanceOf(
+ protocol.address
+ );
+ expect(previousProtocolWrappedNativeTokenBalance.toString()).to.equal(
+ latestProtocolWrappedNativeTokenBalance.toString()
);
- // rbtc balance of feeSharingCollector should be increased
- let latestFeeSharingCollectorProxyRBTCBalance = new BN(
+ // nativeToken balance of feeSharingCollector should be increased
+ let latestFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
expect(
- previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString()
- ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString());
+ previousFeeSharingCollectorProxyNativeTokenBalance
+ .add(new BN(feeAmount))
+ .toString()
+ ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString());
//checkpoints
let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
@@ -1814,13 +1875,13 @@ contract("FeeSharingCollector:", (accounts) => {
let block = await web3.eth.getBlock(tx.receipt.blockNumber);
expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
- expectEvent(tx, "FeeWithdrawnInRBTC", {
+ expectEvent(tx, "FeeWithdrawnInNativeToken", {
sender: root,
amount: feeAmount,
});
});
- it("Should be able to withdraw fees (WRBTC token)", async () => {
+ it("Should be able to withdraw fees (WrappedNativeToken token)", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -1842,35 +1903,36 @@ contract("FeeSharingCollector:", (accounts) => {
true
);
- let previousFeeSharingCollectorProxyRBTCBalance = new BN(
+ let previousFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
- tx = await feeSharingCollector.withdrawFees([WRBTC.address]);
+ tx = await feeSharingCollector.withdrawFees([WrappedNativeToken.address]);
await checkWithdrawFee();
- //check irbtc balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ //check iNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal(
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal(
new BN(0).toString()
);
- // rbtc balance of feeSharingCollector should be increased
- let latestFeeSharingCollectorProxyRBTCBalance = new BN(
+ // nativeToken balance of feeSharingCollector should be increased
+ let latestFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
expect(
- previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString()
- ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString());
+ previousFeeSharingCollectorProxyNativeTokenBalance
+ .add(new BN(feeAmount))
+ .toString()
+ ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString());
//checkpoints
let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
@@ -1894,7 +1956,7 @@ contract("FeeSharingCollector:", (accounts) => {
let block = await web3.eth.getBlock(tx.receipt.blockNumber);
expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
- expectEvent(tx, "FeeWithdrawnInRBTC", {
+ expectEvent(tx, "FeeWithdrawnInNativeToken", {
sender: root,
amount: feeAmount,
});
@@ -1923,7 +1985,7 @@ contract("FeeSharingCollector:", (accounts) => {
true
);
- let previousFeeSharingCollectorProxyRBTCBalance = new BN(
+ let previousFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
@@ -1931,26 +1993,25 @@ contract("FeeSharingCollector:", (accounts) => {
await checkWithdrawFee(false, false, true);
- //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
let feeSharingCollectorProxyBalance = await SOVToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal(feeAmount.toString());
- // special for SOV token, it won't be converted into rbtc, instead it will directly transfer SOV to feeSharingCollector.
- // so the rbtc balance should remain the same.
- let latestFeeSharingCollectorProxyRBTCBalance = new BN(
+ // special for SOV token, it won't be converted into nativeToken, instead it will directly transfer SOV to feeSharingCollector.
+ // so the nativeToken balance should remain the same.
+ let latestFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
- expect(previousFeeSharingCollectorProxyRBTCBalance.toString()).to.equal(
- latestFeeSharingCollectorProxyRBTCBalance.toString()
+ expect(previousFeeSharingCollectorProxyNativeTokenBalance.toString()).to.equal(
+ latestFeeSharingCollectorProxyNativeTokenBalance.toString()
);
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal(
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal(
new BN(0).toString()
);
@@ -2018,8 +2079,8 @@ contract("FeeSharingCollector:", (accounts) => {
await checkWithdrawFee();
- // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ // check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
@@ -2076,8 +2137,8 @@ contract("FeeSharingCollector:", (accounts) => {
// Need to checkwithdrawfee manually
await checkWithdrawFee();
- // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ // check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
@@ -2110,11 +2171,11 @@ contract("FeeSharingCollector:", (accounts) => {
await increaseTime(FEE_WITHDRAWAL_INTERVAL);
tx = await feeSharingCollector.withdrawFees([SUSD.address]);
- // In this state the price of SUSD/WRBTC already adjusted because of previous swap, so we need to consider this in the next swapFee calculation
+ // In this state the price of SUSD/WrappedNativeToken already adjusted because of previous swap, so we need to consider this in the next swapFee calculation
await checkWithdrawFee();
- // check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ // check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
@@ -2143,11 +2204,10 @@ contract("FeeSharingCollector:", (accounts) => {
block = await web3.eth.getBlock(tx.receipt.blockNumber);
expect(lastFeeWithdrawalTime.toString()).to.be.equal(block.timestamp.toString());
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal(
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal(
new BN(0).toString()
);
});
@@ -2277,10 +2337,10 @@ contract("FeeSharingCollector:", (accounts) => {
);
});
- it("Shouldn't be able to withdraw without checkpoints (for wRBTC pool)", async () => {
+ it("Shouldn't be able to withdraw without checkpoints (for wrappedNativeToken pool)", async () => {
await protocolDeploymentFixture();
await expectRevert(
- feeSharingCollector.withdraw(loanTokenWrbtc.address, 0, account2, {
+ feeSharingCollector.withdraw(loanWrappedNativeToken.address, 0, account2, {
from: account1,
}),
"FeeSharingCollector::withdraw: _maxCheckpoints should be positive"
@@ -2435,7 +2495,7 @@ contract("FeeSharingCollector:", (accounts) => {
expect(allUserFees[0]).to.equal(0);
});
- it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with 1 iteration", async () => {
+ it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with 1 iteration", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -2497,7 +2557,7 @@ contract("FeeSharingCollector:", (accounts) => {
expect(allUserFees.length).to.equal(0);
});
- it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with multiple iterations (1 maxCheckpoint)", async () => {
+ it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with multiple iterations (1 maxCheckpoint)", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -2577,7 +2637,7 @@ contract("FeeSharingCollector:", (accounts) => {
expect(allUserFees.length).to.equal(0);
});
- it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with multiple iterations (2 maxCheckpoint)", async () => {
+ it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with multiple iterations (2 maxCheckpoint)", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -2657,7 +2717,7 @@ contract("FeeSharingCollector:", (accounts) => {
expect(allUserFees.length).to.equal(0);
});
- it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - with multiple iterations (3 maxCheckpoint)", async () => {
+ it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - with multiple iterations (3 maxCheckpoint)", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -2737,7 +2797,7 @@ contract("FeeSharingCollector:", (accounts) => {
expect(allUserFees.length).to.equal(0);
});
- it("getAllUserFees should return correct fees after withdrawal (RBTC Tokens) - starting from > 0 checkpoints", async () => {
+ it("getAllUserFees should return correct fees after withdrawal (Native Tokens) - starting from > 0 checkpoints", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -2997,33 +3057,33 @@ contract("FeeSharingCollector:", (accounts) => {
);
});
- it("Shouldn't be able to withdraw zero amount (for wRBTC pool)", async () => {
+ it("Shouldn't be able to withdraw zero amount (for wrappedNativeToken pool)", async () => {
await protocolDeploymentFixture();
let fees = await feeSharingCollector.getAccumulatedFees(
account1,
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
expect(fees).to.be.bignumber.equal("0");
await expectRevert(
- feeSharingCollector.withdraw(loanTokenWrbtc.address, 10, ZERO_ADDRESS, {
+ feeSharingCollector.withdraw(loanWrappedNativeToken.address, 10, ZERO_ADDRESS, {
from: account1,
}),
"FeeSharingCollector::withdrawFees: no tokens for withdrawal"
);
});
- it("Shouldn't be able to withdraw zero amount (for wRBTC pool) - using claimAllCollectedFees()", async () => {
+ it("Shouldn't be able to withdraw zero amount (for wrappedNativeToken pool) - using claimAllCollectedFees()", async () => {
await protocolDeploymentFixture();
let fees = await feeSharingCollector.getAccumulatedFees(
account1,
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
expect(fees).to.be.bignumber.equal("0");
await expectRevert(
feeSharingCollector.claimAllCollectedFees(
- [loanTokenWrbtc.address],
+ [loanWrappedNativeToken.address],
[],
[],
10,
@@ -3036,7 +3096,7 @@ contract("FeeSharingCollector:", (accounts) => {
);
});
- it("Should not be able to pass non-rbtc based token as _rbtcTokensRegularWithdraw in claimAllCollectedFees() function", async () => {
+ it("Should not be able to pass non native token based token as _nativeTokensRegularWithdraw in claimAllCollectedFees() function", async () => {
await protocolDeploymentFixture();
// stake - getPriorTotalVotingPower
let rootStake = 700;
@@ -3081,7 +3141,7 @@ contract("FeeSharingCollector:", (accounts) => {
from: account1,
}
),
- "only rbtc-based tokens are allowed"
+ "only native token based tokens are allowed"
);
});
@@ -3131,24 +3191,30 @@ contract("FeeSharingCollector:", (accounts) => {
);
// processedCheckpoints
- let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] =
- await Promise.all([
- feeSharingCollector.processedCheckpoints.call(
- account1,
- RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
- ),
- feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address),
- feeSharingCollector.processedCheckpoints.call(
- account1,
- loanTokenWrbtc.address
- ),
- ]);
+ let [
+ processedCheckpointsNativeToken,
+ processedCheckpointsWrappedNativeToken,
+ processedCheckpointsIWrappedNativeToken,
+ ] = await Promise.all([
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ WrappedNativeToken.address
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ ),
+ ]);
- expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(0);
- expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(0);
+ expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(0);
+ expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(0);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)),
@@ -3216,7 +3282,7 @@ contract("FeeSharingCollector:", (accounts) => {
});
});
- it("Should be able to withdraw reegular rbtc token to another account using claimAllCollectedFees()", async () => {
+ it("Should be able to withdraw reegular nativeToken token to another account using claimAllCollectedFees()", async () => {
await protocolDeploymentFixture();
// stake - getPriorTotalVotingPower
let rootStake = 700;
@@ -3262,31 +3328,37 @@ contract("FeeSharingCollector:", (accounts) => {
);
// processedCheckpoints
- let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] =
- await Promise.all([
- feeSharingCollector.processedCheckpoints.call(
- account1,
- RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
- ),
- feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address),
- feeSharingCollector.processedCheckpoints.call(
- account1,
- loanTokenWrbtc.address
- ),
- ]);
+ let [
+ processedCheckpointsNativeToken,
+ processedCheckpointsWrappedNativeToken,
+ processedCheckpointsIWrappedNativeToken,
+ ] = await Promise.all([
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ WrappedNativeToken.address
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ ),
+ ]);
- expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(0);
- expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(0);
+ expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(0);
+ expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(0);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount).mul(new BN(3)).div(new BN(10)),
});
});
- it("Should be able to withdraw to another account (WRBTC) - using claimAllCollectedFees()", async () => {
+ it("Should be able to withdraw to another account (WrappedNativeToken) - using claimAllCollectedFees()", async () => {
await protocolDeploymentFixture();
// FeeSharingCollectorProxy
@@ -3295,10 +3367,13 @@ contract("FeeSharingCollector:", (accounts) => {
staking.address
);
- await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address);
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ );
await sovryn.setFeesController(feeSharingCollector.address);
- await WRBTC.mint(feeSharingCollector.address, wei("2", "ether"));
+ await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether"));
// stake - getPriorTotalVotingPower
let rootStake = 700;
@@ -3325,36 +3400,44 @@ contract("FeeSharingCollector:", (accounts) => {
borrowingFeeTokensHeld
);
- /** Add checkpoint for WRBTC */
- await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString());
+ /** Add checkpoint for WrappedNativeToken */
+ await feeSharingCollector.addCheckPoint(
+ WrappedNativeToken.address,
+ totalFeeTokensHeld.toString()
+ );
- /** Add checkpoint for iWRBTC */
+ /** Add checkpoint for iWNT */
await feeSharingCollector.addCheckPoint(
- loanTokenWrbtc.address,
+ loanWrappedNativeToken.address,
totalFeeTokensHeld.toString()
);
- await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, {
+ await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, {
value: totalFeeTokensHeld,
});
await feeSharingCollector.withdrawFees([SUSD.address]);
- const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
);
- const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call(
- account1,
- WRBTC.address
- );
- const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesWrappedNativeToken =
+ await feeSharingCollector.getAccumulatedFees.call(
+ account1,
+ WrappedNativeToken.address
+ );
+ const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString());
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString());
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesWrappedNativeToken.toString()
+ );
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesINativeToken.toString()
+ );
let fees = await feeSharingCollector.getAccumulatedFees(
account1,
@@ -3364,7 +3447,7 @@ contract("FeeSharingCollector:", (accounts) => {
new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10))
);
- /** Withdraw RBTC */
+ /** Withdraw NativeToken */
let tx1 = await feeSharingCollector.claimAllCollectedFees(
[],
[RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT],
@@ -3375,10 +3458,10 @@ contract("FeeSharingCollector:", (accounts) => {
from: account1,
}
);
- /** Withdraw WRBTC */
+ /** Withdraw WrappedNativeToken */
let tx2 = await feeSharingCollector.claimAllCollectedFees(
[],
- [WRBTC.address],
+ [WrappedNativeToken.address],
[],
1000,
account2,
@@ -3387,10 +3470,10 @@ contract("FeeSharingCollector:", (accounts) => {
}
);
- /** Withdraw IWRBTC */
+ /** Withdraw INativeToken */
let tx3 = await feeSharingCollector.claimAllCollectedFees(
[],
- [loanTokenWrbtc.address],
+ [loanWrappedNativeToken.address],
[],
1000,
account2,
@@ -3400,49 +3483,55 @@ contract("FeeSharingCollector:", (accounts) => {
);
// processedCheckpoints
- let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] =
- await Promise.all([
- feeSharingCollector.processedCheckpoints.call(
- account1,
- RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
- ),
- feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address),
- feeSharingCollector.processedCheckpoints.call(
- account1,
- loanTokenWrbtc.address
- ),
- ]);
+ let [
+ processedCheckpointsNativeToken,
+ processedCheckpointsWrappedNativeToken,
+ processedCheckpointsIWrappedNativeToken,
+ ] = await Promise.all([
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ WrappedNativeToken.address
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ ),
+ ]);
- expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1);
- expectEvent(tx1, "RBTCWithdrawn", {
+ expectEvent(tx1, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount)
.mul(new BN(userStakePercentage).mul(new BN(1)))
- .div(new BN(10)), // need multiple by 1 only since we only withdraw RBTC in tx1
+ .div(new BN(10)), // need multiple by 1 only since we only withdraw NativeToken in tx1
});
- expectEvent(tx2, "RBTCWithdrawn", {
+ expectEvent(tx2, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount)
.mul(new BN(userStakePercentage).mul(new BN(1)))
- .div(new BN(10)), // need multiple by 1 only since we only withdraw WRBTC in tx2
+ .div(new BN(10)), // need multiple by 1 only since we only withdraw WrappedNativeToken in tx2
});
- expectEvent(tx3, "RBTCWithdrawn", {
+ expectEvent(tx3, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount)
.mul(new BN(userStakePercentage).mul(new BN(1)))
- .div(new BN(10)), // need multiple by 1 only since we only withdraw IWRBTC in tx3
+ .div(new BN(10)), // need multiple by 1 only since we only withdraw INativeToken in tx3
});
});
- it("Should be able to withdraw to another account (WRBTC) - using claimAllCollectedFees() - Within 1 transaction", async () => {
+ it("Should be able to withdraw to another account (WrappedNativeToken) - using claimAllCollectedFees() - Within 1 transaction", async () => {
await protocolDeploymentFixture();
// FeeSharingCollectorProxy
@@ -3451,10 +3540,13 @@ contract("FeeSharingCollector:", (accounts) => {
staking.address
);
- await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address);
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ );
await sovryn.setFeesController(feeSharingCollector.address);
- await WRBTC.mint(feeSharingCollector.address, wei("2", "ether"));
+ await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether"));
// stake - getPriorTotalVotingPower
let rootStake = 700;
@@ -3481,36 +3573,44 @@ contract("FeeSharingCollector:", (accounts) => {
borrowingFeeTokensHeld
);
- /** Add checkpoint for WRBTC */
- await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString());
+ /** Add checkpoint for WrappedNativeToken */
+ await feeSharingCollector.addCheckPoint(
+ WrappedNativeToken.address,
+ totalFeeTokensHeld.toString()
+ );
- /** Add checkpoint for iWRBTC */
+ /** Add checkpoint for iWNT */
await feeSharingCollector.addCheckPoint(
- loanTokenWrbtc.address,
+ loanWrappedNativeToken.address,
totalFeeTokensHeld.toString()
);
- await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, {
+ await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, {
value: totalFeeTokensHeld,
});
await feeSharingCollector.withdrawFees([SUSD.address]);
- const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
);
- const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call(
- account1,
- WRBTC.address
- );
- const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesWrappedNativeToken =
+ await feeSharingCollector.getAccumulatedFees.call(
+ account1,
+ WrappedNativeToken.address
+ );
+ const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString());
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString());
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesWrappedNativeToken.toString()
+ );
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesINativeToken.toString()
+ );
let fees = await feeSharingCollector.getAccumulatedFees(
account1,
@@ -3520,10 +3620,14 @@ contract("FeeSharingCollector:", (accounts) => {
new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10))
);
- /** Withdraw RBTC WRBTC & IWRBTC */
+ /** Withdraw NativeToken WrappedNativeToken & INativeToken */
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
1000,
account2,
@@ -3533,24 +3637,30 @@ contract("FeeSharingCollector:", (accounts) => {
);
// processedCheckpoints
- let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] =
- await Promise.all([
- feeSharingCollector.processedCheckpoints.call(
- account1,
- RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
- ),
- feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address),
- feeSharingCollector.processedCheckpoints.call(
- account1,
- loanTokenWrbtc.address
- ),
- ]);
+ let [
+ processedCheckpointsNativeToken,
+ processedCheckpointsWrappedNativeToken,
+ processedCheckpointsIWrappedNativeToken,
+ ] = await Promise.all([
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ WrappedNativeToken.address
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ ),
+ ]);
- expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount)
@@ -3559,7 +3669,7 @@ contract("FeeSharingCollector:", (accounts) => {
});
});
- it("Should be able to withdraw rbtc token related - using withdraw() function", async () => {
+ it("Should be able to withdraw nativeToken token related - using withdraw() function", async () => {
await protocolDeploymentFixture();
// FeeSharingCollectorProxy
@@ -3568,10 +3678,13 @@ contract("FeeSharingCollector:", (accounts) => {
staking.address
);
- await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address);
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ );
await sovryn.setFeesController(feeSharingCollector.address);
- await WRBTC.mint(feeSharingCollector.address, wei("2", "ether"));
+ await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether"));
// stake - getPriorTotalVotingPower
let rootStake = 700;
@@ -3598,36 +3711,44 @@ contract("FeeSharingCollector:", (accounts) => {
borrowingFeeTokensHeld
);
- /** Add checkpoint for WRBTC */
- await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString());
+ /** Add checkpoint for WrappedNativeToken */
+ await feeSharingCollector.addCheckPoint(
+ WrappedNativeToken.address,
+ totalFeeTokensHeld.toString()
+ );
- /** Add checkpoint for iWRBTC */
+ /** Add checkpoint for iWNT */
await feeSharingCollector.addCheckPoint(
- loanTokenWrbtc.address,
+ loanWrappedNativeToken.address,
totalFeeTokensHeld.toString()
);
- await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, {
+ await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, {
value: totalFeeTokensHeld,
});
await feeSharingCollector.withdrawFees([SUSD.address]);
- const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
);
- const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call(
- account1,
- WRBTC.address
- );
- const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesWrappedNativeToken =
+ await feeSharingCollector.getAccumulatedFees.call(
+ account1,
+ WrappedNativeToken.address
+ );
+ const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString());
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString());
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesWrappedNativeToken.toString()
+ );
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesINativeToken.toString()
+ );
let fees = await feeSharingCollector.getAccumulatedFees(
account1,
@@ -3637,14 +3758,19 @@ contract("FeeSharingCollector:", (accounts) => {
new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10))
);
- /** Withdraw WRBTC */
- let tx2 = await feeSharingCollector.trueWithdraw(WRBTC.address, 1000, account2, {
- from: account1,
- });
+ /** Withdraw WrappedNativeToken */
+ let tx2 = await feeSharingCollector.trueWithdraw(
+ WrappedNativeToken.address,
+ 1000,
+ account2,
+ {
+ from: account1,
+ }
+ );
- /** Withdraw IWRBTC */
+ /** Withdraw INativeToken */
let tx3 = await feeSharingCollector.trueWithdraw(
- loanTokenWrbtc.address,
+ loanWrappedNativeToken.address,
1000,
account2,
{
@@ -3653,18 +3779,25 @@ contract("FeeSharingCollector:", (accounts) => {
);
// processedCheckpoints
- let [processedCheckpointsWRBTC, processedCheckpointsIWRBTC] = await Promise.all([
- feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address),
- feeSharingCollector.processedCheckpoints.call(account1, loanTokenWrbtc.address),
- ]);
+ let [processedCheckpointsWrappedNativeToken, processedCheckpointsIWrappedNativeToken] =
+ await Promise.all([
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ WrappedNativeToken.address
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ ),
+ ]);
- expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1);
expectEvent(tx2, "UserFeeWithdrawn", {
sender: account1,
receiver: account2,
- token: WRBTC.address,
+ token: WrappedNativeToken.address,
amount: new BN(feeAmount)
.mul(new BN(userStakePercentage).mul(new BN(1)))
.div(new BN(10)),
@@ -3673,14 +3806,14 @@ contract("FeeSharingCollector:", (accounts) => {
expectEvent(tx3, "UserFeeWithdrawn", {
sender: account1,
receiver: account2,
- token: loanTokenWrbtc.address,
+ token: loanWrappedNativeToken.address,
amount: new BN(feeAmount)
.mul(new BN(userStakePercentage).mul(new BN(1)))
.div(new BN(10)),
});
});
- it("Should be able to withdraw to another account (WRBTC) - using withdrawRbtcToken() - Within 1 transaction partially", async () => {
+ it("Should be able to withdraw to another account (WrappedNativeToken) - using withdrawNativeToken() - Within 1 transaction partially", async () => {
await protocolDeploymentFixture();
// FeeSharingCollectorProxy
@@ -3689,10 +3822,13 @@ contract("FeeSharingCollector:", (accounts) => {
staking.address
);
- await feeSharingCollector.initialize(WRBTC.address, loanTokenWrbtc.address);
+ await feeSharingCollector.initialize(
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address
+ );
await sovryn.setFeesController(feeSharingCollector.address);
- await WRBTC.mint(feeSharingCollector.address, wei("2", "ether"));
+ await WrappedNativeToken.mint(feeSharingCollector.address, wei("2", "ether"));
// stake - getPriorTotalVotingPower
let rootStake = 700;
@@ -3719,36 +3855,44 @@ contract("FeeSharingCollector:", (accounts) => {
borrowingFeeTokensHeld
);
- /** Add checkpoint for WRBTC */
- await feeSharingCollector.addCheckPoint(WRBTC.address, totalFeeTokensHeld.toString());
+ /** Add checkpoint for WrappedNativeToken */
+ await feeSharingCollector.addCheckPoint(
+ WrappedNativeToken.address,
+ totalFeeTokensHeld.toString()
+ );
- /** Add checkpoint for iWRBTC */
+ /** Add checkpoint for iWNT */
await feeSharingCollector.addCheckPoint(
- loanTokenWrbtc.address,
+ loanWrappedNativeToken.address,
totalFeeTokensHeld.toString()
);
- await loanTokenWrbtc.mintWithBTC(feeSharingCollector.address, false, {
+ await loanWrappedNativeToken.mintWithBTC(feeSharingCollector.address, false, {
value: totalFeeTokensHeld,
});
await feeSharingCollector.withdrawFees([SUSD.address]);
- const accumulatedFeesRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesNativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
);
- const accumulatedFeesWRBTC = await feeSharingCollector.getAccumulatedFees.call(
- account1,
- WRBTC.address
- );
- const accumulatedFeesIRBTC = await feeSharingCollector.getAccumulatedFees.call(
+ const accumulatedFeesWrappedNativeToken =
+ await feeSharingCollector.getAccumulatedFees.call(
+ account1,
+ WrappedNativeToken.address
+ );
+ const accumulatedFeesINativeToken = await feeSharingCollector.getAccumulatedFees.call(
account1,
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesWRBTC.toString());
- expect(accumulatedFeesRBTC.toString()).to.equal(accumulatedFeesIRBTC.toString());
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesWrappedNativeToken.toString()
+ );
+ expect(accumulatedFeesNativeToken.toString()).to.equal(
+ accumulatedFeesINativeToken.toString()
+ );
let fees = await feeSharingCollector.getAccumulatedFees(
account1,
@@ -3758,12 +3902,16 @@ contract("FeeSharingCollector:", (accounts) => {
new BN(feeAmount).mul(new BN(userStakePercentage)).div(new BN(10))
);
- /** Withdraw RBTC WRBTC & IWRBTC */
- /** @note IWRBTC won't be withdrawn here because we only pass 2 as max checkpoints */
- /** Only RBTC & WRBTC will be withdrawn */
+ /** Withdraw NativeToken WrappedNativeToken & INativeToken */
+ /** @note INativeToken won't be withdrawn here because we only pass 2 as max checkpoints */
+ /** Only NativeToken * WrappedNativeToken will be withdrawn */
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
2,
account2,
@@ -3772,10 +3920,14 @@ contract("FeeSharingCollector:", (accounts) => {
}
);
- /** In this tx, it will withdraw IWRBTC only, since RBTC & WRBTC has no more checkpoints to be withdrawn */
+ /** In this tx, it will withdraw INativeToken only, since NativeToken * WrappedNativeToken has no more checkpoints to be withdrawn */
let tx2 = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
1,
account2,
@@ -3785,24 +3937,30 @@ contract("FeeSharingCollector:", (accounts) => {
);
// processedCheckpoints
- let [processedCheckpointsRBTC, processedCheckpointsWRBTC, processedCheckpointsIWRBTC] =
- await Promise.all([
- feeSharingCollector.processedCheckpoints.call(
- account1,
- RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
- ),
- feeSharingCollector.processedCheckpoints.call(account1, WRBTC.address),
- feeSharingCollector.processedCheckpoints.call(
- account1,
- loanTokenWrbtc.address
- ),
- ]);
+ let [
+ processedCheckpointsNativeToken,
+ processedCheckpointsWrappedNativeToken,
+ processedCheckpointsIWrappedNativeToken,
+ ] = await Promise.all([
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ WrappedNativeToken.address
+ ),
+ feeSharingCollector.processedCheckpoints.call(
+ account1,
+ loanWrappedNativeToken.address
+ ),
+ ]);
- expect(processedCheckpointsRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsWRBTC.toNumber()).to.be.equal(1);
- expect(processedCheckpointsIWRBTC.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsWrappedNativeToken.toNumber()).to.be.equal(1);
+ expect(processedCheckpointsIWrappedNativeToken.toNumber()).to.be.equal(1);
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount)
@@ -3810,7 +3968,7 @@ contract("FeeSharingCollector:", (accounts) => {
.div(new BN(10)),
});
- expectEvent(tx2, "RBTCWithdrawn", {
+ expectEvent(tx2, "NativeTokenWithdrawn", {
sender: account1,
receiver: account2,
amount: new BN(feeAmount)
@@ -3819,7 +3977,7 @@ contract("FeeSharingCollector:", (accounts) => {
});
});
- it("Should be able to withdraw (WRBTC pool) using claimAllCollectedFees()", async () => {
+ it("Should be able to withdraw (WrappedNativeToken pool) using claimAllCollectedFees()", async () => {
/// @dev This test requires redeploying the protocol
await protocolDeploymentFixture();
@@ -3858,7 +4016,11 @@ contract("FeeSharingCollector:", (accounts) => {
let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
30,
ZERO_ADDRESS,
@@ -3906,19 +4068,19 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.be.equal(1);
// check balances
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toNumber()).to.be.equal(0);
- let userLoanTokenBalance = await loanTokenWrbtc.balanceOf.call(account1);
+ let userLoanTokenBalance = await loanWrappedNativeToken.balanceOf.call(account1);
expect(userLoanTokenBalance.toNumber()).to.be.equal(0);
let userExpectedBtcBalance = userInitialBtcBalance.add(
feeAmount.mul(new BN(3)).div(new BN(10))
);
expect(userLatestBTCBalance.toString()).to.be.equal(userExpectedBtcBalance.toString());
- expectEvent(tx, "RBTCWithdrawn", {
+ expectEvent(tx, "NativeTokenWithdrawn", {
sender: account1,
receiver: account1,
amount: feeAmount.mul(new BN(3)).div(new BN(10)),
@@ -4237,7 +4399,11 @@ contract("FeeSharingCollector:", (accounts) => {
let userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
1,
ZERO_ADDRESS,
@@ -4269,11 +4435,11 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.be.equal(1);
// check balances
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toNumber()).to.be.equal(0);
- let userBalance = await loanTokenWrbtc.balanceOf.call(account1);
+ let userBalance = await loanWrappedNativeToken.balanceOf.call(account1);
expect(userBalance.toNumber()).to.be.equal(0);
expect(userLatestBTCBalance.toString()).to.be.equal(
@@ -4294,7 +4460,7 @@ contract("FeeSharingCollector:", (accounts) => {
borrowingFeeTokensHeld2
);
totalFeeAmount = totalFeeAmount.add(feeAmount);
- let totalLoanTokenWRBTCBalanceShouldBeAccount1 = feeAmount;
+ let totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1 = feeAmount;
await increaseTime(FEE_WITHDRAWAL_INTERVAL);
await feeSharingCollector.withdrawFees([SUSD.address]);
@@ -4312,8 +4478,8 @@ contract("FeeSharingCollector:", (accounts) => {
borrowingFeeTokensHeld3
);
totalFeeAmount = totalFeeAmount.add(feeAmount);
- totalLoanTokenWRBTCBalanceShouldBeAccount1 =
- totalLoanTokenWRBTCBalanceShouldBeAccount1.add(feeAmount);
+ totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1 =
+ totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1.add(feeAmount);
await increaseTime(FEE_WITHDRAWAL_INTERVAL);
await feeSharingCollector.withdrawFees([SUSD.address]);
@@ -4321,7 +4487,11 @@ contract("FeeSharingCollector:", (accounts) => {
userInitialBtcBalance = new BN(await web3.eth.getBalance(account1));
tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
2,
ZERO_ADDRESS,
@@ -4343,18 +4513,22 @@ contract("FeeSharingCollector:", (accounts) => {
expect(processedCheckpoints.toNumber()).to.be.equal(3);
// check balances
- feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toNumber()).to.be.equal(0);
- userBalance = await loanTokenWrbtc.balanceOf.call(account1);
+ userBalance = await loanWrappedNativeToken.balanceOf.call(account1);
expect(userBalance.toNumber()).to.be.equal(0);
userLatestBTCBalance = new BN(await web3.eth.getBalance(account1));
expect(userLatestBTCBalance.toString()).to.be.equal(
userInitialBtcBalance
- .add(totalLoanTokenWRBTCBalanceShouldBeAccount1.mul(new BN(1)).div(new BN(10)))
+ .add(
+ totalLoanTokenWrappedNativeTokenBalanceShouldBeAccount1
+ .mul(new BN(1))
+ .div(new BN(10))
+ )
.toString()
);
});
@@ -4377,7 +4551,11 @@ contract("FeeSharingCollector:", (accounts) => {
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
1000,
ZERO_ADDRESS,
@@ -4412,7 +4590,11 @@ contract("FeeSharingCollector:", (accounts) => {
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
5,
ZERO_ADDRESS,
@@ -4430,7 +4612,11 @@ contract("FeeSharingCollector:", (accounts) => {
tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
3,
ZERO_ADDRESS,
@@ -4448,7 +4634,11 @@ contract("FeeSharingCollector:", (accounts) => {
tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
1000,
ZERO_ADDRESS,
@@ -4562,7 +4752,11 @@ contract("FeeSharingCollector:", (accounts) => {
let tx = await feeSharingCollector.claimAllCollectedFees(
[],
- [RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT, WRBTC.address, loanTokenWrbtc.address],
+ [
+ RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT,
+ WrappedNativeToken.address,
+ loanWrappedNativeToken.address,
+ ],
[],
10,
ZERO_ADDRESS,
@@ -4779,33 +4973,34 @@ contract("FeeSharingCollector:", (accounts) => {
"unauthorized"
);
await liquidityPoolV1Converter.setFeesController(feeSharingCollector.address);
- await liquidityPoolV1Converter.setWrbtcToken(WRBTC.address);
- await WRBTC.mint(liquidityPoolV1Converter.address, wei("2", "ether"));
+ await liquidityPoolV1Converter.setWrbtcToken(WrappedNativeToken.address);
+ await WrappedNativeToken.mint(liquidityPoolV1Converter.address, wei("2", "ether"));
- let previousFeeSharingCollectorProxyRBTCBalance = new BN(
+ let previousFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
tx = await feeSharingCollector.withdrawFeesAMM([liquidityPoolV1Converter.address]);
- //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
- // rbtc balance of feeSharingCollector should be increased
- let latestFeeSharingCollectorProxyRBTCBalance = new BN(
+ // nativeToken balance of feeSharingCollector should be increased
+ let latestFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
expect(
- previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString()
- ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString());
+ previousFeeSharingCollectorProxyNativeTokenBalance
+ .add(new BN(feeAmount))
+ .toString()
+ ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString());
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal("0");
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal("0");
//checkpoints
let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
@@ -4865,33 +5060,34 @@ contract("FeeSharingCollector:", (accounts) => {
"unauthorized"
);
await liquidityPoolV1Converter.setFeesController(feeSharingCollector.address);
- await liquidityPoolV1Converter.setWrbtcToken(WRBTC.address);
- await WRBTC.mint(liquidityPoolV1Converter.address, wei("2", "ether"));
+ await liquidityPoolV1Converter.setWrbtcToken(WrappedNativeToken.address);
+ await WrappedNativeToken.mint(liquidityPoolV1Converter.address, wei("2", "ether"));
- let previousFeeSharingCollectorProxyRBTCBalance = new BN(
+ let previousFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
tx = await feeSharingCollector.withdrawFeesAMM([liquidityPoolV1Converter.address]);
- //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal("0");
- // rbtc balance of feeSharingCollector should be increased
- let latestFeeSharingCollectorProxyRBTCBalance = new BN(
+ // nativeToken balance of feeSharingCollector should be increased
+ let latestFeeSharingCollectorProxyNativeTokenBalance = new BN(
await web3.eth.getBalance(feeSharingCollector.address)
);
expect(
- previousFeeSharingCollectorProxyRBTCBalance.add(new BN(feeAmount)).toString()
- ).to.equal(latestFeeSharingCollectorProxyRBTCBalance.toString());
+ previousFeeSharingCollectorProxyNativeTokenBalance
+ .add(new BN(feeAmount))
+ .toString()
+ ).to.equal(latestFeeSharingCollectorProxyNativeTokenBalance.toString());
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal(
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal(
new BN(0).toString()
);
@@ -4952,32 +5148,31 @@ contract("FeeSharingCollector:", (accounts) => {
"unauthorized"
);
await liquidityPoolV1Converter.setFeesController(feeSharingCollector.address);
- await liquidityPoolV1Converter.setWrbtcToken(WRBTC.address);
- await WRBTC.mint(liquidityPoolV1Converter.address, wei("2", "ether"));
+ await liquidityPoolV1Converter.setWrbtcToken(WrappedNativeToken.address);
+ await WrappedNativeToken.mint(liquidityPoolV1Converter.address, wei("2", "ether"));
tx = await feeSharingCollector.withdrawFeesAMM([liquidityPoolV1Converter.address]);
- //check WRBTC balance (wrbt balance = (totalFeeTokensHeld * mockPrice) - swapFee)
- let feeSharingCollectorProxyBalance = await loanTokenWrbtc.balanceOf.call(
+ //check WrappedNativeToken balance (wrappedNativeToken balance = (totalFeeTokensHeld * mockPrice) - swapFee)
+ let feeSharingCollectorProxyBalance = await loanWrappedNativeToken.balanceOf.call(
feeSharingCollector.address
);
expect(feeSharingCollectorProxyBalance.toString()).to.be.equal(feeAmount.toString());
- // make sure wrbtc balance is 0 after withdrawal
- let feeSharingCollectorProxyWRBTCBalance = await WRBTC.balanceOf.call(
- feeSharingCollector.address
- );
- expect(feeSharingCollectorProxyWRBTCBalance.toString()).to.be.equal(
+ // make sure wrappedNativeToken balance is 0 after withdrawal
+ let feeSharingCollectorProxyWrappedNativeTokenBalance =
+ await WrappedNativeToken.balanceOf.call(feeSharingCollector.address);
+ expect(feeSharingCollectorProxyWrappedNativeTokenBalance.toString()).to.be.equal(
new BN(0).toString()
);
//checkpoints
let totalTokenCheckpoints = await feeSharingCollector.totalTokenCheckpoints.call(
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
expect(totalTokenCheckpoints.toNumber()).to.be.equal(0);
let checkpoint = await feeSharingCollector.tokenCheckpoints.call(
- loanTokenWrbtc.address,
+ loanWrappedNativeToken.address,
0
);
expect(checkpoint.blockNumber.toNumber()).to.be.equal(0);
@@ -4986,30 +5181,30 @@ contract("FeeSharingCollector:", (accounts) => {
//check lastFeeWithdrawalTime
let lastFeeWithdrawalTime = await feeSharingCollector.lastFeeWithdrawalTime.call(
- loanTokenWrbtc.address
+ loanWrappedNativeToken.address
);
expect(lastFeeWithdrawalTime.toString()).to.be.equal("0");
});
});
- describe("withdraw wrbtc", async () => {
- it("Withdraw wrbtc from non owner should revert", async () => {
+ describe("withdraw wrappedNativeToken", async () => {
+ it("Withdraw wrappedNativeToken from non owner should revert", async () => {
await protocolDeploymentFixture();
const receiver = accounts[1];
- const previousBalanceReceiver = await WRBTC.balanceOf(receiver);
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
await expectRevert(
- feeSharingCollector.withdrawWRBTC(receiver, 0, { from: accounts[1] }),
+ feeSharingCollector.withdrawWrappedNativeToken(receiver, 0, { from: accounts[1] }),
"unauthorized"
);
});
- it("Withdraw 0 wrbtc", async () => {
+ it("Withdraw 0 wrappedNativeToken", async () => {
await protocolDeploymentFixture();
const receiver = accounts[1];
- const previousBalanceReceiver = await WRBTC.balanceOf(receiver);
- await feeSharingCollector.withdrawWRBTC(receiver, 0);
- const latestBalanceReceiver = await WRBTC.balanceOf(receiver);
- const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ await feeSharingCollector.withdrawWrappedNativeToken(receiver, 0);
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
@@ -5019,28 +5214,28 @@ contract("FeeSharingCollector:", (accounts) => {
expect(latestBalanceFeeSharingCollectorProxy.toString()).to.equal("0");
});
- it("Withdraw wrbtc more than the balance of feeSharingCollector should revert", async () => {
+ it("Withdraw wrappedNativeToken more than the balance of feeSharingCollector should revert", async () => {
await protocolDeploymentFixture();
- await WRBTC.mint(root, wei("500", "ether"));
- await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether"));
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
const receiver = accounts[1];
- const previousBalanceReceiver = await WRBTC.balanceOf(receiver);
- const feeSharingCollectorProxyBalance = await WRBTC.balanceOf(
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const feeSharingCollectorProxyBalance = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
const amount = feeSharingCollectorProxyBalance.add(new BN(100));
- const previousBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const previousBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
await expectRevert(
- feeSharingCollector.withdrawWRBTC(receiver, amount.toString()),
+ feeSharingCollector.withdrawWrappedNativeToken(receiver, amount.toString()),
"Insufficient balance"
);
- const latestBalanceReceiver = await WRBTC.balanceOf(receiver);
- const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
@@ -5052,24 +5247,24 @@ contract("FeeSharingCollector:", (accounts) => {
);
});
- it("Fully Withdraw wrbtc", async () => {
+ it("Fully Withdraw wrappedNativeToken", async () => {
await protocolDeploymentFixture();
- await WRBTC.mint(root, wei("500", "ether"));
- await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether"));
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
const receiver = accounts[1];
- const previousBalanceReceiver = await WRBTC.balanceOf(receiver);
- const feeSharingCollectorProxyBalance = await WRBTC.balanceOf(
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const feeSharingCollectorProxyBalance = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
- const tx = await feeSharingCollector.withdrawWRBTC(
+ const tx = await feeSharingCollector.withdrawWrappedNativeToken(
receiver,
feeSharingCollectorProxyBalance.toString()
);
await expectEvent.inTransaction(
tx.receipt.rawLogs[0].transactionHash,
- WRBTC,
+ WrappedNativeToken,
"Transfer",
{
src: feeSharingCollector.address,
@@ -5078,8 +5273,8 @@ contract("FeeSharingCollector:", (accounts) => {
}
);
- const latestBalanceReceiver = await WRBTC.balanceOf(receiver);
- const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
@@ -5089,27 +5284,30 @@ contract("FeeSharingCollector:", (accounts) => {
expect(latestBalanceFeeSharingCollectorProxy.toString()).to.equal("0");
});
- it("Partially Withdraw wrbtc", async () => {
+ it("Partially Withdraw wrappedNativeToken", async () => {
await protocolDeploymentFixture();
- await WRBTC.mint(root, wei("500", "ether"));
- await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether"));
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
const receiver = accounts[1];
const restAmount = new BN("100"); // 100 wei
- const previousBalanceReceiver = await WRBTC.balanceOf(receiver);
- const feeSharingCollectorProxyBalance = await WRBTC.balanceOf(
+ const previousBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const feeSharingCollectorProxyBalance = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
const amount = feeSharingCollectorProxyBalance.sub(restAmount);
- const previousBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const previousBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
expect(previousBalanceFeeSharingCollectorProxy.toString()).to.equal(wei("1", "ether"));
- const tx = await feeSharingCollector.withdrawWRBTC(receiver, amount.toString());
+ const tx = await feeSharingCollector.withdrawWrappedNativeToken(
+ receiver,
+ amount.toString()
+ );
await expectEvent.inTransaction(
tx.receipt.rawLogs[0].transactionHash,
- WRBTC,
+ WrappedNativeToken,
"Transfer",
{
src: feeSharingCollector.address,
@@ -5118,8 +5316,8 @@ contract("FeeSharingCollector:", (accounts) => {
}
);
- const latestBalanceReceiver = await WRBTC.balanceOf(receiver);
- const latestBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const latestBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
+ const latestBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
@@ -5131,14 +5329,14 @@ contract("FeeSharingCollector:", (accounts) => {
);
// try to withdraw the rest
- const tx2 = await feeSharingCollector.withdrawWRBTC(
+ const tx2 = await feeSharingCollector.withdrawWrappedNativeToken(
receiver,
latestBalanceFeeSharingCollectorProxy.toString()
);
- const finalBalanceFeeSharingCollectorProxy = await WRBTC.balanceOf(
+ const finalBalanceFeeSharingCollectorProxy = await WrappedNativeToken.balanceOf(
feeSharingCollector.address
);
- const finalBalanceReceiver = await WRBTC.balanceOf(receiver);
+ const finalBalanceReceiver = await WrappedNativeToken.balanceOf(receiver);
expect(new BN(finalBalanceReceiver).toString()).to.equal(
previousBalanceFeeSharingCollectorProxy.toString()
);
@@ -5146,7 +5344,7 @@ contract("FeeSharingCollector:", (accounts) => {
await expectEvent.inTransaction(
tx2.receipt.rawLogs[0].transactionHash,
- WRBTC,
+ WrappedNativeToken,
"Transfer",
{
src: feeSharingCollector.address,
@@ -5157,8 +5355,8 @@ contract("FeeSharingCollector:", (accounts) => {
});
});
- describe("get all rbtc balance after transferRBTC", async () => {
- it("deposit 0 RBTC should revert", async () => {
+ describe("get all nativeToken balance after transferNativeToken", async () => {
+ it("deposit 0 NativeToken should revert", async () => {
await protocolDeploymentFixture();
// stake - getPriorTotalVotingPower
let totalStake = 1000;
@@ -5167,18 +5365,18 @@ contract("FeeSharingCollector:", (accounts) => {
let amount = 1000;
await expectRevert(
- feeSharingCollector.transferRBTC({ from: root, value: 0 }),
- "FeeSharingCollector::transferRBTC: invalid value"
+ feeSharingCollector.transferNativeToken({ from: root, value: 0 }),
+ "FeeSharingCollector::transferNativeToken: invalid value"
);
- const totalAccumulatedRBTCFee =
- await feeSharingCollector.getAccumulatedRBTCFeeBalances(root);
- expect(totalAccumulatedRBTCFee.toNumber()).to.equal(0);
+ const totalAccumulatedNativeTokenFee =
+ await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root);
+ expect(totalAccumulatedNativeTokenFee.toNumber()).to.equal(0);
expect(
await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT)
).to.be.bignumber.equal(new BN(0));
});
- it("deposit RBTC should add the checkpoints", async () => {
+ it("deposit NativeToken should add the checkpoints", async () => {
await protocolDeploymentFixture();
// stake - getPriorTotalVotingPower
let totalStake = 1000;
@@ -5186,10 +5384,10 @@ contract("FeeSharingCollector:", (accounts) => {
let amount = 1000;
- let tx = await feeSharingCollector.transferRBTC({ from: root, value: amount });
- let totalAccumulatedRBTCFee =
- await feeSharingCollector.getAccumulatedRBTCFeeBalances(root);
- expect(totalAccumulatedRBTCFee.toString()).to.equal(new BN(amount).toString());
+ let tx = await feeSharingCollector.transferNativeToken({ from: root, value: amount });
+ let totalAccumulatedNativeTokenFee =
+ await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root);
+ expect(totalAccumulatedNativeTokenFee.toString()).to.equal(new BN(amount).toString());
expect(
await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT)
@@ -5230,12 +5428,12 @@ contract("FeeSharingCollector:", (accounts) => {
});
// second time
- tx = await feeSharingCollector.transferRBTC({ from: root, value: amount * 2 });
- totalAccumulatedRBTCFee =
- await feeSharingCollector.getAccumulatedRBTCFeeBalances(root);
+ tx = await feeSharingCollector.transferNativeToken({ from: root, value: amount * 2 });
+ totalAccumulatedNativeTokenFee =
+ await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root);
// the deposit still in the window of withdraw interval, so the accumulatedFees won't be added at this point.
- expect(totalAccumulatedRBTCFee.toString()).to.equal(new BN(amount).toString());
+ expect(totalAccumulatedNativeTokenFee.toString()).to.equal(new BN(amount).toString());
expect(
await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT)
@@ -5249,13 +5447,15 @@ contract("FeeSharingCollector:", (accounts) => {
await increaseTime(FEE_WITHDRAWAL_INTERVAL);
// third time
- tx = await feeSharingCollector.transferRBTC({ from: root, value: amount * 4 });
+ tx = await feeSharingCollector.transferNativeToken({ from: root, value: amount * 4 });
- totalAccumulatedRBTCFee =
- await feeSharingCollector.getAccumulatedRBTCFeeBalances(root);
+ totalAccumulatedNativeTokenFee =
+ await feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root);
// already passed the withdrawal interval
- expect(totalAccumulatedRBTCFee.toString()).to.equal(new BN(amount * 7).toString());
+ expect(totalAccumulatedNativeTokenFee.toString()).to.equal(
+ new BN(amount * 7).toString()
+ );
expect(
await feeSharingCollector.unprocessedAmount.call(RBTC_DUMMY_ADDRESS_FOR_CHECKPOINT)
@@ -5287,7 +5487,7 @@ contract("FeeSharingCollector:", (accounts) => {
describe("recover incorrect allocated fees", async () => {
let mockSOV, mockZUSD;
- let rbtcAmount = new BN(wei("878778886164898400", "wei"));
+ let nativeTokenAmount = new BN(wei("878778886164898400", "wei"));
beforeEach(async () => {
mockSOV = await smock.fake("TestToken", {
@@ -5304,69 +5504,10 @@ contract("FeeSharingCollector:", (accounts) => {
await web3.eth.sendTransaction({
from: accounts[2].toString(),
to: feeSharingCollector.address,
- value: rbtcAmount,
+ value: nativeTokenAmount,
gas: 50000,
});
});
-
- it("recoverIncorrectAllocatedFees() can only be called by the owner", async () => {
- await protocolDeploymentFixture();
- await expectRevert(
- feeSharingCollector.recoverIncorrectAllocatedFees({ from: accounts[1] }),
- "unauthorized"
- );
- });
-
- it("recoverIncorrectAllocatedFees() can only be executed once", async () => {
- const owner = root;
- await protocolDeploymentFixture();
- await feeSharingCollector.recoverIncorrectAllocatedFees({ from: owner });
- await expectRevert(
- feeSharingCollector.recoverIncorrectAllocatedFees({ from: owner }),
- "FeeSharingCollector: function can only be called once"
- );
- });
-
- it("Should be able to withdraw the incorrect allocated fees properly", async () => {
- await protocolDeploymentFixture();
- const owner = await feeSharingCollector.owner();
- const previousBalanceOwner = new BN(await web3.eth.getBalance(owner));
- const tx = await feeSharingCollector.recoverIncorrectAllocatedFees();
- const latestBalanceOwner = new BN(await web3.eth.getBalance(owner));
- const txFee = new BN((await etherGasCost(tx.receipt)).toString());
-
- expect(previousBalanceOwner.add(rbtcAmount).sub(txFee).toString()).to.be.equal(
- latestBalanceOwner.toString()
- );
- });
-
- it("Should revert if sov or zusd transfer failed", async () => {
- await protocolDeploymentFixture();
- mockSOV.transfer.returns(false);
- await expectRevert(
- feeSharingCollector.recoverIncorrectAllocatedFees(),
- "SafeERC20: ERC20 operation did not succeed"
- );
- mockSOV.transfer.returns(true);
- mockZUSD.transfer.returns(false);
- await expectRevert(
- feeSharingCollector.recoverIncorrectAllocatedFees(),
- "SafeERC20: ERC20 operation did not succeed"
- );
- });
-
- it("Should revert if rbtc transfer failed", async () => {
- feeSharingCollector = await FeeSharingCollectorMockup.new(
- sovryn.address,
- staking.address
- );
-
- /** Should revert because feeSharingCollector does not have enough balance of rbtc */
- await expectRevert(
- feeSharingCollector.recoverIncorrectAllocatedFees(),
- "FeeSharingCollector::recoverIncorrectAllocatedFees: Withdrawal rbtc failed"
- );
- });
});
describe("test coverage", async () => {
@@ -5381,7 +5522,7 @@ contract("FeeSharingCollector:", (accounts) => {
);
});
- it("getAccumulatedRBTCFeeBalances should revert loan wrbtc is not set", async () => {
+ it("getAccumulatedNativeTokenFeeBalances should revert loan wrappedNativeToken is not set", async () => {
await protocolDeploymentFixture();
feeSharingCollector = await FeeSharingCollectorMockup.new(
sovryn.address,
@@ -5389,49 +5530,49 @@ contract("FeeSharingCollector:", (accounts) => {
);
await expectRevert(
- feeSharingCollector.getAccumulatedRBTCFeeBalances(root),
+ feeSharingCollector.getAccumulatedNativeTokenFeeBalances(root),
"Transaction reverted: function call to a non-contract account"
);
});
- it("transferTokens (wrbtc) will revert if invalid total weighted stake", async () => {
+ it("transferTokens (wrappedNativeToken) will revert if invalid total weighted stake", async () => {
await protocolDeploymentFixture();
feeSharingCollector = await FeeSharingCollectorMockup.new(
sovryn.address,
staking.address
);
- const mockWrbtc = await smock.fake("TestWrbtc");
- mockWrbtc.withdraw.returns(true);
- await sovryn.setWrbtcToken(mockWrbtc.address);
+ const mockWrappedNativeToken = await smock.fake("TestWrappedNativeToken");
+ mockWrappedNativeToken.withdraw.returns(true);
+ await sovryn.setWrbtcToken(mockWrappedNativeToken.address);
- await WRBTC.mint(root, wei("500", "ether"));
- await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether"));
- await WRBTC.approve(feeSharingCollector.address, wei("1", "ether"));
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
+ await WrappedNativeToken.approve(feeSharingCollector.address, wei("1", "ether"));
await expectRevert(
- feeSharingCollector.transferTokens(WRBTC.address, 1000),
+ feeSharingCollector.transferTokens(WrappedNativeToken.address, 1000),
"Invalid totalWeightedStake"
);
});
- it("transferTokens (wrbtc) wrbtc should success", async () => {
+ it("transferTokens (wrappedNativeToken) wrappedNativeToken should success", async () => {
await protocolDeploymentFixture();
feeSharingCollector = await FeeSharingCollectorMockup.new(
sovryn.address,
staking.address
);
- await WRBTC.mint(root, wei("500", "ether"));
- await WRBTC.transfer(feeSharingCollector.address, wei("1", "ether"));
+ await WrappedNativeToken.mint(root, wei("500", "ether"));
+ await WrappedNativeToken.transfer(feeSharingCollector.address, wei("1", "ether"));
// stake - getPriorTotalVotingPower
let totalStake = 1000;
await stake(totalStake, root);
let amount = 1000;
- await WRBTC.approve(feeSharingCollector.address, amount * 7);
+ await WrappedNativeToken.approve(feeSharingCollector.address, amount * 7);
- await feeSharingCollector.transferTokens(WRBTC.address, amount);
+ await feeSharingCollector.transferTokens(WrappedNativeToken.address, amount);
});
it("endOfRange with 0 max checkpoint", async () => {
@@ -5440,7 +5581,9 @@ contract("FeeSharingCollector:", (accounts) => {
sovryn.address,
staking.address
);
- const end = await feeSharingCollector.endOfRangeWithZeroMaxCheckpoint(WRBTC.address);
+ const end = await feeSharingCollector.endOfRangeWithZeroMaxCheckpoint(
+ WrappedNativeToken.address
+ );
expect(end).to.equal(0);
});
@@ -5464,7 +5607,7 @@ contract("FeeSharingCollector:", (accounts) => {
expect(hasFees).to.equal(false);
});
- it("getRBTCBalance should revert error if non-rbtc token is passed", async () => {
+ it("getNativeTokenBalance should revert error if non native token token is passed", async () => {
await protocolDeploymentFixture();
feeSharingCollector = await FeeSharingCollectorMockup.new(
sovryn.address,
@@ -5472,8 +5615,8 @@ contract("FeeSharingCollector:", (accounts) => {
);
await expectRevert(
- feeSharingCollector.getRBTCBalance(SOVToken.address, root, 0),
- "FeeSharingCollector::_getRBTCBalance: only rbtc-based tokens are allowed"
+ feeSharingCollector.getNativeTokenBalance(SOVToken.address, root, 0),
+ "FeeSharingCollector::_getNativeTokenBalance: only native token based tokens are allowed"
);
});
@@ -5516,13 +5659,13 @@ contract("FeeSharingCollector:", (accounts) => {
lendingFee,
tradingFee,
borrowingFee,
- wrbtcTokenFee = false,
+ wrappedNativeTokenFee = false,
sovTokenFee = false
) {
let totalFeeAmount = lendingFee.add(tradingFee).add(borrowingFee);
let tokenFee;
- if (wrbtcTokenFee) {
- tokenFee = WRBTC;
+ if (wrappedNativeTokenFee) {
+ tokenFee = WrappedNativeToken;
} else {
tokenFee = SUSD;
await tokenFee.transfer(sovryn.address, totalFeeAmount);
@@ -5540,7 +5683,11 @@ contract("FeeSharingCollector:", (accounts) => {
return totalFeeAmount;
}
- async function checkWithdrawFee(checkSUSD = true, checkWRBTC = false, checkSOV = false) {
+ async function checkWithdrawFee(
+ checkSUSD = true,
+ checkWrappedNativeToken = false,
+ checkSOV = false
+ ) {
if (checkSUSD) {
let protocolBalance = await SUSD.balanceOf(sovryn.address);
expect(protocolBalance.toString()).to.be.equal(new BN(0).toString());
@@ -5552,12 +5699,18 @@ contract("FeeSharingCollector:", (accounts) => {
expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
}
- if (checkWRBTC) {
- lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(WRBTC.address);
+ if (checkWrappedNativeToken) {
+ lendingFeeTokensHeld = await sovryn.lendingFeeTokensHeld.call(
+ WrappedNativeToken.address
+ );
expect(lendingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
- tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(WRBTC.address);
+ tradingFeeTokensHeld = await sovryn.tradingFeeTokensHeld.call(
+ WrappedNativeToken.address
+ );
expect(tradingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
- borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(WRBTC.address);
+ borrowingFeeTokensHeld = await sovryn.borrowingFeeTokensHeld.call(
+ WrappedNativeToken.address
+ );
expect(borrowingFeeTokensHeld.toString()).to.be.equal(new BN(0).toString());
}
diff --git a/tests/swaps/SwapsExternal.js b/tests/swaps/SwapsExternal.js
index 51a1ef97d..007794ed3 100644
--- a/tests/swaps/SwapsExternal.js
+++ b/tests/swaps/SwapsExternal.js
@@ -487,7 +487,7 @@ contract("SwapsExternal", (accounts) => {
// need to sub by swap fee because at this point, protocol will received the trading fee again.
loanTokenWRBTCBalanceShouldBe = amount.mul(new BN(1)).sub(swapFee);
- expectEvent(tx, "FeeWithdrawnInRBTC", {
+ expectEvent(tx, "FeeWithdrawnInNativeToken", {
sender: lender,
amount: loanTokenWRBTCBalanceShouldBe,
});