Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fee sharing contract and update network with fee sharing program #4

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
105 changes: 105 additions & 0 deletions contracts/FeeSharing.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
pragma solidity 0.4.25;


import "./FeeSharingInterface.sol";
import "./Withdrawable.sol";
import "./Utils2.sol";
import "./NetworkInterface.sol";

/**
* @title Helps contracts guard against reentrancy attacks.
*/
contract ReentrancyGuard {

/// @dev counter to allow mutex lock with only one SSTORE operation
uint256 private guardCounter = 1;

/**
* @dev Prevents a function from calling itself, directly or indirectly.
* Calling one `nonReentrant` function from
* another is not supported. Instead, you can implement a
* `private` function doing the actual work, and an `external`
* wrapper marked as `nonReentrant`.
*/
modifier nonReentrant() {
guardCounter += 1;
uint256 localCounter = guardCounter;
_;
require(localCounter == guardCounter);
}
}

contract FeeSharing is Withdrawable, FeeSharingInterface, Utils2, ReentrancyGuard {

mapping(address=>uint) public walletFeesInBps; // commission (percentage) of a wallet
mapping(address=>uint) public walletFeesToPay; // amount of fee to pay for a wallet
mapping(address=>uint) public feePayedPerWallet; // total fee payed to a wallet

NetworkInterface public network;

constructor(address _admin, NetworkInterface _network) public {
require(_admin != address(0));
require(_network != address(0));
network = _network;
admin = _admin;
}

event SetNewNetworkContract(NetworkInterface _network);
function setNetworkContract(NetworkInterface _network) public onlyAdmin {
require(_network != address(0));
network = _network;
emit SetNewNetworkContract(_network);
}

event WalletFeesSet(address wallet, uint feesInBps);
function setWalletFees(address wallet, uint feesInBps) public onlyAdmin {
require(feesInBps < 10000); // under 100%
walletFeesInBps[wallet] = feesInBps;
emit WalletFeesSet(wallet, feesInBps);
}

event AssignFeeToWallet(address wallet, uint walletFee);
function handleFees(address wallet)
public
nonReentrant
payable
returns(bool)
{
require(msg.sender == address(network));
require(msg.value <= MAX_QTY);
uint fee = msg.value;

uint walletFee = fee * walletFeesInBps[wallet] / 10000;
require(fee >= walletFee);

if (walletFee > 0) {
walletFeesToPay[wallet] += walletFee;
emit AssignFeeToWallet(wallet, walletFee);
}
return true;
}

event SendWalletFees(address indexed wallet, address sender);
// this function is callable by anyone
function sendFeeToWallet(address wallet) public nonReentrant {
uint feeAmount = walletFeesToPay[wallet];
require(feeAmount > 0);

uint thisBalance = address(this).balance;
uint walletBalance = wallet.balance;

require(thisBalance >= feeAmount);

walletFeesToPay[wallet] = 0;
wallet.transfer(feeAmount);
feePayedPerWallet[wallet] += feeAmount;

uint newThisBalance = address(this).balance;
require(newThisBalance == thisBalance - feeAmount);

uint newWalletBalance = wallet.balance;
require(newWalletBalance == walletBalance + feeAmount);

emit SendWalletFees(wallet, msg.sender);
}
}
5 changes: 5 additions & 0 deletions contracts/FeeSharingInterface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma solidity 0.4.25;

interface FeeSharingInterface {
function handleFees(address wallet) external payable returns(bool);
}
81 changes: 38 additions & 43 deletions contracts/Network.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "./Withdrawable.sol";
import "./Utils2.sol";
import "./WhiteListInterface.sol";
import "./ExpectedRateInterface.sol";
import "./FeeSharingInterface.sol";

/**
* @title Helps contracts guard against reentrancy attacks.
Expand All @@ -32,7 +33,6 @@ contract ReentrancyGuard {
}



////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @title Network main contract
contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
Expand All @@ -52,12 +52,11 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
// only 1 reserve for a token
mapping(address=>address) public reservePerTokenFee;
mapping(address=>uint) public feeForReserve;
address public feeHolder;
FeeSharingInterface public feeSharing;

constructor(address _admin) public {
require(_admin != address(0));
admin = _admin;
feeHolder = address(this);
}

event EtherReceival(address indexed sender, uint amount);
Expand Down Expand Up @@ -218,7 +217,7 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
if (add) {
require(token.approve(reserve, 2**255)); // approve infinity
} else {
require(token.approve(reserve, 0));
require(token.approve(reserve, 0));
}

emit ListReservePairs(reserve, token, TOMO_TOKEN_ADDRESS, add);
Expand All @@ -227,6 +226,13 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
setDecimals(token);
}

event FeeSharingSet(address fee, address sender);
function setFeeSharing(FeeSharingInterface _feeSharing) public onlyAdmin {
require(_feeSharing != address(0));
feeSharing = _feeSharing;
emit FeeSharingSet(_feeSharing, msg.sender);
}

function setWhiteList(WhiteListInterface whiteList) public onlyAdmin {
whiteListContract = whiteList;
}
Expand Down Expand Up @@ -275,14 +281,6 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
return reserves.length;
}

event FeeHolderSet(address holder);
function setFeeHolder(address holder) public onlyAdmin {
require(holder != address(0));
feeHolder = holder;
emit FeeHolderSet(holder);
}


event FeeForReserveSet(address reserve, uint percent);
function setFeePercent(address reserve, uint newPercent) public onlyAdmin {
require(isReserve[reserve]);
Expand All @@ -301,10 +299,6 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
return maxGasPriceValue;
}

function getFeeHolder() public view returns(address) {
return feeHolder;
}

function getExpectedRate(TRC20 src, TRC20 dest, uint srcQty)
public view
returns(uint expectedRate, uint slippageRate)
Expand Down Expand Up @@ -521,6 +515,7 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {

//do the trade
//src to ETH

require(doReserveTrade(
tradeInput.src,
actualSrcAmount,
Expand All @@ -529,7 +524,8 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
weiAmount,
ReserveInterface(rateResult.reserve1),
rateResult.rateSrcToTomo,
true));
true,
tradeInput.walletId));

//Eth to dest
require(doReserveTrade(
Expand All @@ -540,7 +536,8 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
actualDestAmount,
ReserveInterface(rateResult.reserve2),
rateResult.rateTomoToDest,
true));
true,
tradeInput.walletId));

emit Trade(tradeInput.trader, tradeInput.src, actualSrcAmount, tradeInput.destAddress, tradeInput.dest,
actualDestAmount);
Expand Down Expand Up @@ -590,7 +587,7 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {

if (actualSrcAmount < tradeInput.srcAmount) {
// if there is "change" send back to trader
require(tradeInput.src.transfer(tradeInput.trader, (tradeInput.srcAmount - actualSrcAmount)));
tradeInput.src.transfer(tradeInput.trader, (tradeInput.srcAmount - actualSrcAmount));
}

// verify trade size is smaller than user cap, dest is always TOMO
Expand All @@ -605,7 +602,8 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
actualDestAmount,
ReserveInterface(reserve),
expectedRate,
true));
true,
tradeInput.walletId));

emit TradeFee(tradeInput.trader, tradeInput.src, actualSrcAmount, tradeInput.destAddress, tradeInput.dest,
actualDestAmount);
Expand Down Expand Up @@ -659,7 +657,8 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
uint expectedDestAmount,
ReserveInterface reserve,
uint conversionRate,
bool validate
bool validate,
address walletId
)
internal
returns(bool)
Expand All @@ -678,27 +677,22 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
}

// calculate expected fee for this transaction based on amount of Tomo
uint tomoValue = src == TOMO_TOKEN_ADDRESS ? callValue : expectedDestAmount;
uint feeInWei = tomoValue * feeForReserve[reserve] / 10000; // feePercent = 25 -> fee = 25/10000 = 0.25%

// Logics: expected to receive exactly feeInWei amount of TOMO as fee from reserve.
// - If feeHolder and src == TOMO
// + callValue of TOMO will be transfered from network -> reserve
// - If feeHolder is the destAdress and dest token is TOMO
// + expectedDestAmount of TOMO will be transfered from reserve -> destAdress
uint expectedFeeHolderTomoBal = feeHolder.balance;
if (feeHolder == address(this) && src == TOMO_TOKEN_ADDRESS) {
uint feeInWei = src == TOMO_TOKEN_ADDRESS ? callValue : expectedDestAmount;
feeInWei = feeInWei * feeForReserve[reserve] / 10000; // feePercent = 25 -> fee = 25/10000 = 0.25%

uint expectedTomoBal = address(this).balance;
if (src == TOMO_TOKEN_ADDRESS) {
// callValue amount of Tomo will be transfered to reserve
require(expectedFeeHolderTomoBal >= callValue);
expectedFeeHolderTomoBal -= callValue;
require(expectedTomoBal >= callValue);
expectedTomoBal -= callValue;
}
if (feeHolder == destAddress && dest == TOMO_TOKEN_ADDRESS) {
// expectedDestAmount of Tomo will be transfered to destAdress (which is also feeHolder)
expectedFeeHolderTomoBal += expectedDestAmount;
if (address(this) == destAddress && dest == TOMO_TOKEN_ADDRESS) {
// expectedDestAmount of Tomo will be transfered to destAdress
expectedTomoBal += expectedDestAmount;
}

// receive feeInWei amount of Tomo as fee
expectedFeeHolderTomoBal += feeInWei;
expectedTomoBal += feeInWei;

// reserve sends tokens/eth to network. network sends it to destination
require(reserve.trade.value(callValue)(src, amount, dest, this, conversionRate, feeInWei, validate), "doReserveTrade: reserve trade failed");
Expand All @@ -711,14 +705,15 @@ contract Network is Withdrawable, Utils2, NetworkInterface, ReentrancyGuard {
require(dest.transfer(destAddress, expectedDestAmount), "doReserveTrade: transfer token failed");
}
}
if (feeHolder != address(this)) {
require(address(this).balance >= feeInWei);
// transfer fee to feeHolder
feeHolder.transfer(feeInWei);
}

// Expected to receive exact amount fee in TOMO
require(feeHolder.balance == expectedFeeHolderTomoBal);
require(address(this).balance == expectedTomoBal);

if (feeSharing != address(0)) {
require(address(this).balance >= feeInWei);
// transfer fee to feeSharing
require(feeSharing.handleFees.value(feeInWei)(walletId));
}

return true;
}
Expand Down
1 change: 0 additions & 1 deletion contracts/NetworkInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import "./TRC20Interface.sol";
/// @title Kyber Network interface
interface NetworkInterface {
function maxGasPrice() external view returns(uint);
function getFeeHolder() external view returns(address);
function getUserCapInWei(address user) external view returns(uint);
function getUserCapInTokenWei(address user, TRC20 token) external view returns(uint);
function enabled() external view returns(bool);
Expand Down
2 changes: 0 additions & 2 deletions contracts/NetworkProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ contract NetworkProxy is NetworkProxyInterface, SimpleNetworkInterface, Withdraw
{
require(src == TOMO_TOKEN_ADDRESS || msg.value == 0);
require(payFeeCallers[msg.sender] == true, "payTxFee: Sender is not callable this function");
require(msg.sender != networkContract.getFeeHolder());
TRC20 dest = TOMO_TOKEN_ADDRESS;

UserBalance memory userBalanceBefore;
Expand Down Expand Up @@ -235,7 +234,6 @@ contract NetworkProxy is NetworkProxyInterface, SimpleNetworkInterface, Withdraw
returns(uint)
{
require(src == TOMO_TOKEN_ADDRESS || msg.value == 0);
require(msg.sender != networkContract.getFeeHolder());

UserBalance memory userBalanceBefore;

Expand Down
Loading