Documenting fees + rewards now use weighted stake in denominator of cobb douglas

This commit is contained in:
Greg Hysen 2019-06-27 14:15:55 -07:00
parent 9f1904ad3d
commit 3f2be5b2da
12 changed files with 253 additions and 106 deletions

View File

@ -31,6 +31,7 @@ contract StakingProxy is
constructor(address _stakingContract)
public
{
owner = msg.sender;
stakingContract = _stakingContract;
}

View File

@ -18,17 +18,17 @@
pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracts/src/Authorizable.sol";
import "../interfaces/IStakingEvents.sol";
import "../immutable/MixinConstants.sol";
import "../immutable/MixinStorage.sol";
import "./MixinOwnable.sol";
contract MixinExchangeManager is
Authorizable,
IStakingEvents,
MixinConstants,
MixinStorage
MixinStorage,
MixinOwnable
{
/// @dev This mixin contains logic for managing exchanges.
@ -73,8 +73,9 @@ contract MixinExchangeManager is
emit ExchangeRemoved(addr);
}
/// @dev Returns true iff the address is a valid exchange
/// @param addr Address of exchange contract
/// @dev Returns true iff the address is a valid exchange.
/// @param addr Address of exchange contract.
/// @return True iff input address is a valid exchange.
function isValidExchangeAddress(address addr)
public
view

View File

@ -44,6 +44,19 @@ contract MixinFees is
using LibSafeMath for uint256;
/// @dev This mixin contains the logic for 0x protocol fees.
/// Protocol fees are sent by 0x exchanges every time there is a trade.
/// If the maker has associated their address with a pool (see MixinPools.sol), then
/// the fee will be attributed to their pool. At the end of an epoch the maker and
/// their pool will receive a rebate that is proportional to (i) the fee volume attributed
/// to their pool over the epoch, and (ii) the amount of stake provided by the maker and
/// their delegators. Note that delegated stake (see MixinStake) is weighted less than
/// stake provided by directly by the maker; this is a disincentive for market makers to
/// monopolize a single pool that they all delegate to.
/// @dev Pays a protocol fee in ETH.
/// Only a known 0x exchange can call this method. See (MixinExchangeManager).
/// @param makerAddress The address of the order's maker
function payProtocolFee(address makerAddress)
external
payable
@ -54,25 +67,37 @@ contract MixinFees is
uint256 _feesCollectedThisEpoch = protocolFeesThisEpochByPool[poolId];
protocolFeesThisEpochByPool[poolId] = _feesCollectedThisEpoch._add(amount);
if (_feesCollectedThisEpoch == 0) {
activePoolIdsThisEpoch.push(poolId);
activePoolsThisEpoch.push(poolId);
}
}
/// @dev Pays the rebates for to market making pool that was active this epoch,
/// then updates the epoch and other time-based periods via the scheduler (see MixinScheduler).
/// This is intentionally permissionless, and may be called by anyone.
function finalizeFees()
external
{
_payRebates();
// payout rewards
(uint256 totalActivePools,
uint256 totalFeesCollected,
uint256 totalWeightedStake,
uint256 totalRewardsPaid,
uint256 initialContractBalance,
uint256 finalContractBalance) = _payMakerRewards();
emit RewardsPaid(
totalActivePools,
totalFeesCollected,
totalWeightedStake,
totalRewardsPaid,
initialContractBalance,
finalContractBalance
);
_goToNextEpoch();
}
function getProtocolFeesThisEpochByPool(bytes32 poolId)
public
view
returns (uint256)
{
return protocolFeesThisEpochByPool[poolId];
}
/// @dev Returns the total amount of fees collected thus far, in the current epoch.
/// @return Amount of fees.
function getTotalProtocolFeesThisEpoch()
public
view
@ -81,78 +106,137 @@ contract MixinFees is
return address(this).balance;
}
function _payRebates()
internal
/// @dev Returns the amount of fees attributed to the input pool.
/// @param poolId Pool Id to query.
/// @return Amount of fees.
function getProtocolFeesThisEpochByPool(bytes32 poolId)
public
view
returns (uint256)
{
// Step 1 - compute total fees this epoch
uint256 numberOfActivePoolIds = activePoolIdsThisEpoch.length;
IStructs.ActivePool[] memory activePoolIds = new IStructs.ActivePool[](activePoolIdsThisEpoch.length);
uint256 totalFees = 0;
for (uint i = 0; i != numberOfActivePoolIds; i++) {
activePoolIds[i].poolId = activePoolIdsThisEpoch[i];
activePoolIds[i].feesCollected = protocolFeesThisEpochByPool[activePoolIds[i].poolId];
totalFees = totalFees._add(activePoolIds[i].feesCollected);
return protocolFeesThisEpochByPool[poolId];
}
uint256 totalRewards = address(this).balance;
uint256 totalStake = getActivatedStakeAcrossAllOwners();
emit EpochFinalized(
numberOfActivePoolIds,
totalRewards,
0
/// @dev Pays rewards to market making pools that were active this epoch.
/// Each pool receives a portion of the fees generated this epoch (see LibFeeMath) that is
/// proportional to (i) the fee volume attributed to their pool over the epoch, and
/// (ii) the amount of stake provided by the maker and their delegators. Rebates are paid
/// into the Reward Vault (see MixinRewardVault) where they can be withdraw by makers and
/// the members of their pool. There will be a small amount of ETH leftover in this contract
/// after paying out the rebates; at present, this rolls over into the next epoch. Eventually,
/// we plan to deposit this leftover into a DAO managed by the 0x community.
/// @return totalActivePools Total active pools this epoch.
/// @return totalFeesCollected Total fees collected this epoch, across all active pools.
/// @return totalWeightedStake Total weighted stake attributed to each pool. Delegated stake is weighted less.
/// @return totalRewardsPaid Total rewards paid out across all active pools.
/// @return initialContractBalance Balance of this contract before paying rewards.
/// @return finalContractBalance Balance of this contract after paying rewards.
function _payMakerRewards()
private
returns (
uint256 totalActivePools,
uint256 totalFeesCollected,
uint256 totalWeightedStake,
uint256 totalRewardsPaid,
uint256 initialContractBalance,
uint256 finalContractBalance
)
{
// initialize return values
totalActivePools = activePoolsThisEpoch.length;
totalFeesCollected = 0;
totalWeightedStake = 0;
totalRewardsPaid = 0;
initialContractBalance = address(this).balance;
finalContractBalance = initialContractBalance;
// sanity check - is there a balance to payout and were there any active pools?
if (initialContractBalance == 0 || totalActivePools == 0) {
return (
totalActivePools,
totalFeesCollected,
totalWeightedStake,
totalRewardsPaid,
initialContractBalance,
finalContractBalance
);
// no rebates available
// note that there is a case in cobb-douglas where if we weigh either fees or stake at 100%,
// then the other value doesn't matter. However, it's cheaper on gas to assume that there is some
// non-zero split.
if (totalRewards == 0 || totalFees == 0 || totalStake == 0) {
return;
}
// Step 2 - payout
uint256 totalRewardsRecordedInVault = 0;
for (uint i = 0; i != numberOfActivePoolIds; i++) {
uint256 stakeDelegatedToPool = getStakeDelegatedToPool(activePoolIds[i].poolId);
uint256 stakeHeldByPoolOperator = getActivatedAndUndelegatedStake(getPoolOperator(activePoolIds[i].poolId));
uint256 scaledStake = stakeHeldByPoolOperator._add(
// step 1/3 - compute stats for active maker pools
IStructs.ActivePool[] memory activePools = new IStructs.ActivePool[](activePoolsThisEpoch.length);
for (uint i = 0; i != totalActivePools; i++) {
bytes32 poolId = activePoolsThisEpoch[i];
// compute weighted stake
uint256 stakeDelegatedToPool = getStakeDelegatedToPool(poolId);
uint256 stakeHeldByPoolOperator = getActivatedAndUndelegatedStake(getPoolOperator(poolId));
uint256 weightedStake = stakeHeldByPoolOperator._add(
stakeDelegatedToPool
._mul(REWARD_PAYOUT_DELEGATED_STAKE_PERCENT_VALUE)
._div(100)
);
// store pool stats
activePools[i].poolId = poolId;
activePools[i].feesCollected = protocolFeesThisEpochByPool[poolId];
activePools[i].weightedStake = weightedStake;
// update cumulative amounts
totalFeesCollected = totalFeesCollected._add(activePools[i].feesCollected);
totalWeightedStake = totalWeightedStake._add(activePools[i].weightedStake);
}
// sanity check - this is a gas optimization that can be used because we assume a non-zero
// split between stake and fees generated in the cobb-douglas computation (see below).
if (totalFeesCollected == 0 || totalWeightedStake == 0) {
return (
totalActivePools,
totalFeesCollected,
totalWeightedStake,
totalRewardsPaid,
initialContractBalance,
finalContractBalance
);
}
// step 2/3 - record reward for each pool
for (uint i = 0; i != totalActivePools; i++) {
// compute reward using cobb-douglas formula
uint256 reward = LibFeeMath._cobbDouglasSuperSimplified(
totalRewards,
activePoolIds[i].feesCollected,
totalFees,
scaledStake,
totalStake
initialContractBalance,
activePools[i].feesCollected,
totalFeesCollected,
activePools[i].weightedStake,
totalWeightedStake
);
// record reward in vault
_recordDepositInRewardVault(activePoolIds[i].poolId, reward);
totalRewardsRecordedInVault = totalRewardsRecordedInVault._add(reward);
_recordDepositInRewardVault(activePools[i].poolId, reward);
totalRewardsPaid = totalRewardsPaid._add(reward);
// clear state for refunds
protocolFeesThisEpochByPool[activePoolIds[i].poolId] = 0;
activePoolIdsThisEpoch[i] = 0;
// clear state for gas refunds
protocolFeesThisEpochByPool[activePools[i].poolId] = 0;
activePoolsThisEpoch[i] = 0;
}
activePoolIdsThisEpoch.length = 0;
activePoolsThisEpoch.length = 0;
// Step 3 send total payout to vault
// step 3/3 send total payout to vault
require(
totalRewardsRecordedInVault <= totalRewards,
totalRewardsPaid <= initialContractBalance,
"MISCALCULATED_REWARDS"
);
if (totalRewardsRecordedInVault > 0) {
_depositIntoRewardVault(totalRewardsRecordedInVault);
if (totalRewardsPaid > 0) {
_depositIntoRewardVault(totalRewardsPaid);
}
finalContractBalance = address(this).balance;
// Notify finalization
emit EpochFinalized(
numberOfActivePoolIds,
totalRewards,
totalRewardsRecordedInVault
return (
totalActivePools,
totalFeesCollected,
totalWeightedStake,
totalRewardsPaid,
initialContractBalance,
finalContractBalance
);
}
}

View File

@ -24,9 +24,11 @@ import "../libs/LibSafeMath64.sol";
import "../immutable/MixinConstants.sol";
import "../immutable/MixinStorage.sol";
import "../interfaces/IStructs.sol";
import "../interfaces/IStakingEvents.sol";
contract MixinScheduler is
IStakingEvents,
MixinConstants,
MixinStorage,
IMixinScheduler
@ -43,6 +45,7 @@ contract MixinScheduler is
/// and consistent scheduling metric than time. Timelocks, for example, are measured in epochs.
/// @dev Returns the current epoch.
/// @return Epoch.
function getCurrentEpoch()
public
view
@ -53,6 +56,7 @@ contract MixinScheduler is
/// @dev Returns the current epoch period, measured in seconds.
/// Epoch period = [startTimeInSeconds..endTimeInSeconds)
/// @return Time in seconds.
function getEpochPeriodInSeconds()
public
pure
@ -63,6 +67,7 @@ contract MixinScheduler is
/// @dev Returns the start time in seconds of the current epoch.
/// Epoch period = [startTimeInSeconds..endTimeInSeconds)
/// @return Time in seconds.
function getCurrentEpochStartTimeInSeconds()
public
view
@ -74,6 +79,7 @@ contract MixinScheduler is
/// @dev Returns the earliest end time in seconds of this epoch.
/// The next epoch can begin once this time is reached.
/// Epoch period = [startTimeInSeconds..endTimeInSeconds)
/// @return Time in seconds.
function getCurrentEpochEarliestEndTimeInSeconds()
public
view
@ -82,7 +88,8 @@ contract MixinScheduler is
return getCurrentEpochStartTimeInSeconds()._add(getEpochPeriodInSeconds());
}
/// @dev Returns the current timelock period
/// @dev Returns the current timelock period.
/// @return Timelock period.
function getCurrentTimelockPeriod()
public
view
@ -93,6 +100,7 @@ contract MixinScheduler is
/// @dev Returns the length of a timelock period, measured in epochs.
/// Timelock period = [startEpoch..endEpoch)
/// @return Timelock period end.
function getTimelockPeriodInEpochs()
public
pure
@ -103,6 +111,7 @@ contract MixinScheduler is
/// @dev Returns the epoch that the current timelock period started at.
/// Timelock period = [startEpoch..endEpoch)
/// @return Timelock period start.
function getCurrentTimelockPeriodStartEpoch()
public
view
@ -113,6 +122,7 @@ contract MixinScheduler is
/// @dev Returns the epoch that the current timelock period will end.
/// Timelock period = [startEpoch..endEpoch)
/// @return Timelock period.
function getCurrentTimelockPeriodEndEpoch()
public
view
@ -128,7 +138,6 @@ contract MixinScheduler is
internal
{
// get current timestamp
// solium-disable security/no-block-members
// solhint-disable-next-line not-rely-on-time
uint64 currentBlockTimestamp = block.timestamp._downcastToUint64();
@ -142,11 +151,27 @@ contract MixinScheduler is
uint64 nextEpoch = currentEpoch._add(1);
currentEpoch = nextEpoch;
currentEpochStartTimeInSeconds = currentBlockTimestamp;
uint64 earliestEndTimeInSeconds = currentEpochStartTimeInSeconds._add(getEpochPeriodInSeconds());
// notify of epoch change
emit EpochChanged(
currentEpoch,
currentEpochStartTimeInSeconds,
earliestEndTimeInSeconds
);
// increment timelock period, if needed
if (getCurrentTimelockPeriodEndEpoch() <= nextEpoch) {
currentTimelockPeriod = currentTimelockPeriod._add(1);
currentTimelockPeriodStartEpoch = currentEpoch;
uint64 endEpoch = currentEpoch._add(getTimelockPeriodInEpochs());
// notify
emit TimelockPeriodChanged(
currentTimelockPeriod,
currentTimelockPeriodStartEpoch,
endEpoch
);
}
}
}

View File

@ -28,6 +28,7 @@ contract IMixinScheduler {
/// Epochs serve as the basis for all other time intervals, which provides a more stable
/// and consistent scheduling metric than time. Timelocks, for example, are measured in epochs.
/*
/// @dev Returns the current epoch.
function getCurrentEpoch()
public
@ -82,4 +83,5 @@ contract IMixinScheduler {
public
view
returns (uint64);
*/
}

View File

@ -31,16 +31,15 @@ contract MixinStorage is
// @TODO Add notes about which Mixin manages which state
// address of owner
address internal owner;
// address of staking contract
address internal stakingContract;
// mapping from Owner to Amount Staked
mapping (address => uint256) internal stakeByOwner;
// @TODO Think about merging these different states
// It would be nice if the sum of the different states had to equal `stakeByOwner`
// and it were all in a single variable (stakeByOwner in its own)
// mapping from Owner to Amount of Instactive Stake
mapping (address => uint256) internal activeStakeByOwner;
@ -88,7 +87,7 @@ contract MixinStorage is
mapping (bytes32 => uint256) internal protocolFeesThisEpochByPool;
//
bytes32[] internal activePoolIdsThisEpoch;
bytes32[] internal activePoolsThisEpoch;
// mapping from POol Id to Shadow Rewards
mapping (bytes32 => uint256) internal shadowRewardsByPoolId;

View File

@ -27,9 +27,39 @@ interface IStakingEvents {
address exchangeAddress
);
event EpochFinalized(
/// @dev Emitted by MixinScheduler when the epoch is changed.
/// @param epoch The epoch we changed to.
/// @param startTimeInSeconds The start time of the epoch.
/// @param earliestEndTimeInSeconds The earliest this epoch can end.
event EpochChanged(
uint64 epoch,
uint64 startTimeInSeconds,
uint64 earliestEndTimeInSeconds
);
/// @dev Emitted by MixinScheduler when the timelock period is changed.
/// @param timelockPeriod The timelock period we changed to.
/// @param startEpoch The epoch this period started.
/// @param endEpoch The epoch this period ends.
event TimelockPeriodChanged(
uint64 timelockPeriod,
uint64 startEpoch,
uint64 endEpoch
);
/// @dev Emitted by MixinFees when rewards are paid out.
/// @param totalActivePools Total active pools this epoch.
/// @param totalFeesCollected Total fees collected this epoch, across all active pools.
/// @param totalWeightedStake Total weighted stake attributed to each pool. Delegated stake is weighted less.
/// @param totalRewardsPaid Total rewards paid out across all active pools.
/// @param initialContractBalance Balance of this contract before paying rewards.
/// @param finalContractBalance Balance of this contract after paying rewards.
event RewardsPaid(
uint256 totalActivePools,
uint256 totalFees,
uint256 totalRewards
uint256 totalFeesCollected,
uint256 totalWeightedStake,
uint256 totalRewardsPaid,
uint256 initialContractBalance,
uint256 finalContractBalance
);
}

View File

@ -50,5 +50,6 @@ interface IStructs {
struct ActivePool {
bytes32 poolId;
uint256 feesCollected;
uint256 weightedStake;
}
}

View File

@ -82,6 +82,7 @@ describe('Exchange Integrations', () => {
stakingWrapper.removeExchangeAddressAsync(exchange),
RevertReason.ExchangeAddressNotRegistered,
);
// @todo should not be able to add / remove an exchange if not contract owner.
});
});
});

View File

@ -118,13 +118,13 @@ describe('End-To-End Simulations', () => {
/*
\ // the expected payouts were computed by hand
// @TODO - get computations more accurate
Pool | Total Fees | Total Stake | Total Delegated Stake | Total Stake (Scaled)
0 | 0.304958 | 42 | 0 | 42
1 | 15.323258 | 84 | 0 | 84
Pool | Total Fees | Total Stake | Total Delegated Stake | Total Stake (Weighted) | Payout
0 | 0.304958 | 42 | 0 | 42 | 3.0060373...
1 | 15.323258 | 84 | 0 | 84 |
3 | 28.12222236 | 97 | 182 | 260.8
...
Cumulative Fees = 43.75043836
Cumulative Stake = 405
Cumulative Weighted Stake = 386.8
Total Rewards = 43.75043836
*/
const simulationParams = {
@ -164,27 +164,27 @@ describe('End-To-End Simulations', () => {
StakingWrapper.toBaseUnitAmount(28.12222236),
],
expectedPayoutByPool: [
new BigNumber('2.89303'), // 2.8930364057678784829875695710382241749912199174798475
new BigNumber('9.90218'), // 9.9021783083174087034787071054543342142019746753770943
new BigNumber('28.16463'), // 28.164631904035798614670299155719067954180760345463798
new BigNumber('3.00603'), // 3.006037310109530277237724562632303034914024715508955780682
new BigNumber('10.28895'), // 10.28895363598396754741643198605226143579652264694121578135
new BigNumber('29.26472'), // 29.26473180250053106672049765968527817034954761113582833460
],
expectedPayoutByPoolOperator: [
new BigNumber('1.12828'), // 0.39 * 2.89303
new BigNumber('5.84228'), // 0.59 * 9.90218
new BigNumber('12.11079'), // 0.43 * 28.16463
new BigNumber('1.17235'), // 0.39 * 3.00603
new BigNumber('6.07048'), // 0.59 * 10.28895
new BigNumber('12.58383'), // 0.43 * 29.26472
],
expectedMembersPayoutByPool: [
new BigNumber('1.76475'), // (1 - 0.39) * 2.89303
new BigNumber('4.05989'), // (1 - 0.59) * 9.90218
new BigNumber('16.05383'), // (1 - 0.43) * 28.16463
new BigNumber('1.83368'), // (1 - 0.39) * 3.00603
new BigNumber('4.21847'), // (1 - 0.59) * 10.28895
new BigNumber('16.68089'), // (1 - 0.43) * 29.26472
],
expectedPayoutByDelegator: [
// note that the on-chain values may be slightly different due to rounding down on each entry
// there is a carry over between calls, which we account for here. the result is that delegators
// who withdraw later on will scoop up any rounding spillover from those who have already withdrawn.
new BigNumber('1.49953'), // (17 / 182) * 16.05383
new BigNumber('6.61559'), // (75 / 182) * 16.05383
new BigNumber('7.93871'), // (90 / 182) * 16.05383
new BigNumber('1.55810'), // (17 / 182) * 16.6809
new BigNumber('6.87399'), // (75 / 182) * 16.6809
new BigNumber('8.24879'), // (90 / 182) * 16.6809
],
exchangeAddress: exchange,
};
@ -201,7 +201,7 @@ describe('End-To-End Simulations', () => {
3 | 28.12222236 | 97 | 182 | 260.8
...
Cumulative Fees = 43.75043836
Cumulative Stake = 405
Cumulative Weighted Stake = 386.8
Total Rewards = 43.75043836
// In this case, there was already a pot of ETH in the delegator pool that nobody had claimed.
@ -280,7 +280,7 @@ describe('End-To-End Simulations', () => {
3 | 28.12222236 | 97 | 182 | 260.8
...
Cumulative Fees = 43.75043836
Cumulative Stake = 405
Cumulative Weighted Stake = 386.8
Total Rewards = 43.75043836
// In this case, there was already a pot of ETH in the delegator pool that nobody had claimed.

View File

@ -25,7 +25,7 @@ export class StakingWrapper {
private readonly _web3Wrapper: Web3Wrapper;
private readonly _provider: Provider;
private readonly _logDecoder: LogDecoder;
private readonly _ownerAddres: string;
private readonly _ownerAddress: string;
private readonly _erc20ProxyContract: ERC20ProxyContract;
private readonly _zrxTokenContract: DummyERC20TokenContract;
private readonly _accounts: string[];
@ -73,7 +73,7 @@ export class StakingWrapper {
this._provider = provider;
const decoderArtifacts = _.merge(artifacts, erc20Artifacts);
this._logDecoder = new LogDecoder(this._web3Wrapper, decoderArtifacts);
this._ownerAddres = ownerAddres;
this._ownerAddress = ownerAddres;
this._erc20ProxyContract = erc20ProxyContract;
this._zrxTokenContract = zrxTokenContract;
this._accounts = accounts;
@ -143,7 +143,7 @@ export class StakingWrapper {
(this._zrxVaultContractIfExists).address,
);
const setZrxVaultTxData = {
from: this._ownerAddres,
from: this._ownerAddress,
to: (this._stakingProxyContractIfExists).address,
data: setZrxVaultCalldata,
};
@ -161,7 +161,7 @@ export class StakingWrapper {
(this._rewardVaultContractIfExists).address,
);
const setRewardVaultTxData = {
from: this._ownerAddres,
from: this._ownerAddress,
to: (this._stakingProxyContractIfExists).address,
data: setRewardVaultCalldata,
};
@ -243,7 +243,7 @@ export class StakingWrapper {
}
public async forceTimelockSyncAsync(owner: string): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().forceTimelockSync.getABIEncodedTransactionData(owner);
const txReceipt = await this._executeTransactionAsync(calldata, this._ownerAddres);
const txReceipt = await this._executeTransactionAsync(calldata, this._ownerAddress);
return txReceipt;
}
///// STAKE BALANCES /////
@ -411,6 +411,7 @@ export class StakingWrapper {
logUtils.log(
`Finalization costed ${txReceipt.gasUsed} gas`,
);
console.log(JSON.stringify(txReceipt.logs, null, 4));
return txReceipt;
}
public async skipToNextEpochAsync(): Promise<TransactionReceiptWithDecodedLogs> {
@ -511,14 +512,16 @@ export class StakingWrapper {
const isValid = this.getStakingContract().isValidExchangeAddress.getABIDecodedReturnData(returnData);
return isValid;
}
public async addExchangeAddressAsync(exchangeAddress: string): Promise<TransactionReceiptWithDecodedLogs> {
public async addExchangeAddressAsync(exchangeAddress: string, ownerAddressIfExists?: string): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().addExchangeAddress.getABIEncodedTransactionData(exchangeAddress);
const txReceipt = await this._executeTransactionAsync(calldata, this._ownerAddres);
const ownerAddress = ownerAddressIfExists !== undefined ? ownerAddressIfExists : this._ownerAddress;
const txReceipt = await this._executeTransactionAsync(calldata, ownerAddress);
return txReceipt;
}
public async removeExchangeAddressAsync(exchangeAddress: string): Promise<TransactionReceiptWithDecodedLogs> {
public async removeExchangeAddressAsync(exchangeAddress: string, ownerAddressIfExists?: string): Promise<TransactionReceiptWithDecodedLogs> {
const calldata = this.getStakingContract().removeExchangeAddress.getABIEncodedTransactionData(exchangeAddress);
const txReceipt = await this._executeTransactionAsync(calldata, this._ownerAddres);
const ownerAddress = ownerAddressIfExists !== undefined ? ownerAddressIfExists : this._ownerAddress;
const txReceipt = await this._executeTransactionAsync(calldata, ownerAddress);
return txReceipt;
}
///// REWARDS /////
@ -735,7 +738,7 @@ export class StakingWrapper {
includeLogs?: boolean,
): Promise<TransactionReceiptWithDecodedLogs> {
const txData = {
from: from ? from : this._ownerAddres,
from: from ? from : this._ownerAddress,
to: this.getStakingProxyContract().address,
data: calldata,
gas: 3000000,
@ -750,7 +753,7 @@ export class StakingWrapper {
}
private async _callAsync(calldata: string, from?: string): Promise<any> {
const txData = {
from: from ? from : this._ownerAddres,
from: from ? from : this._ownerAddress,
to: this.getStakingProxyContract().address,
data: calldata,
gas: 3000000,

View File

@ -873,7 +873,7 @@
"@0x/web3-wrapper@^4.0.1":
version "4.0.2"
resolved "https://registry.npmjs.org/@0x/web3-wrapper/-/web3-wrapper-4.0.2.tgz#d4e0a4fa1217155e1aed4cd91086654fd99f2959"
resolved "https://registry.yarnpkg.com/@0x/web3-wrapper/-/web3-wrapper-4.0.2.tgz#d4e0a4fa1217155e1aed4cd91086654fd99f2959"
dependencies:
"@0x/assert" "^2.0.2"
"@0x/json-schemas" "^3.0.2"