Store separate operator / pool balances in the reward vault. This reduces complexity in the staking contract.
This commit is contained in:
		@@ -97,7 +97,7 @@ contract MixinFees is
 | 
			
		||||
        // 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) {
 | 
			
		||||
            revert("We don't want to hit this case in testing");
 | 
			
		||||
            // revert("We don't want to hit this case in testing");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -61,9 +61,10 @@ contract MixinPools is
 | 
			
		||||
        // create pool in reward vault
 | 
			
		||||
        rewardVault.createPool(
 | 
			
		||||
            poolId,
 | 
			
		||||
            operatorAddress
 | 
			
		||||
            operatorAddress,
 | 
			
		||||
            operatorShare
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        // 
 | 
			
		||||
        emit PoolCreated(poolId, operatorAddress, operatorShare);
 | 
			
		||||
        return poolId;
 | 
			
		||||
 
 | 
			
		||||
@@ -173,7 +173,7 @@ contract MixinStake is
 | 
			
		||||
 | 
			
		||||
        // update delegator's share of reward pool
 | 
			
		||||
        // note that this uses the snapshot parameters
 | 
			
		||||
        uint256 poolBalance = rewardVault.balanceOf(poolId);
 | 
			
		||||
        uint256 poolBalance = rewardVault.poolBalanceOf(poolId);
 | 
			
		||||
        uint256 buyIn = _computeBuyInDenominatedInShadowAsset(
 | 
			
		||||
            amount,
 | 
			
		||||
            _delegatedStakeByPoolId,
 | 
			
		||||
@@ -215,7 +215,7 @@ contract MixinStake is
 | 
			
		||||
 | 
			
		||||
        // get payout
 | 
			
		||||
        // TODO -- not full balance, just balance that belongs to delegators.
 | 
			
		||||
        uint256 poolBalance = rewardVault.balanceOf(poolId);
 | 
			
		||||
        uint256 poolBalance = rewardVault.poolBalanceOf(poolId);
 | 
			
		||||
        uint256 payoutInRealAsset;
 | 
			
		||||
        uint256 payoutInShadowAsset;
 | 
			
		||||
        if (_delegatedStakeToPoolByOwner == amount) {
 | 
			
		||||
@@ -238,7 +238,7 @@ contract MixinStake is
 | 
			
		||||
            );
 | 
			
		||||
        } else {
 | 
			
		||||
            // partial payout
 | 
			
		||||
            revert('no partial');
 | 
			
		||||
            // revert('no partial');
 | 
			
		||||
            (payoutInRealAsset, payoutInShadowAsset) = _computePartialPayout(
 | 
			
		||||
                 amount,
 | 
			
		||||
                _delegatedStakeByOwner,
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,8 @@ pragma solidity ^0.5.5;
 | 
			
		||||
 | 
			
		||||
interface IRewardVault {
 | 
			
		||||
 | 
			
		||||
    function createPool(bytes32 poolId, address payable poolOwner)
 | 
			
		||||
        external
 | 
			
		||||
        payable;
 | 
			
		||||
    function createPool(bytes32 poolId, address payable poolOperator, uint8 poolOperatorShare)
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
    function depositFor(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
@@ -35,15 +34,27 @@ interface IRewardVault {
 | 
			
		||||
    function withdrawFor(bytes32 poolId, uint256 amount)
 | 
			
		||||
        external;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    function withdrawAllFrom(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        returns (uint256);
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
    function balanceOf(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256);
 | 
			
		||||
 | 
			
		||||
    function operatorBalanceOf(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256);
 | 
			
		||||
 | 
			
		||||
    function poolBalanceOf(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256);
 | 
			
		||||
 | 
			
		||||
    function getPoolOwner(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@ import "../immutable/MixinConstants.sol";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
contract RewardVault is
 | 
			
		||||
    IRewardVault,
 | 
			
		||||
    //IRewardVault,
 | 
			
		||||
    SafeMath,
 | 
			
		||||
    MixinConstants,
 | 
			
		||||
    MixinVaultCore
 | 
			
		||||
@@ -35,14 +35,17 @@ contract RewardVault is
 | 
			
		||||
    // designed in such a way that it contains minimal logic (it is not upgradeable)
 | 
			
		||||
    // but has all the necessary information to compute withdrawals in the event of
 | 
			
		||||
    // a catastrophic failure
 | 
			
		||||
 | 
			
		||||
    // uint256 constant NIL_BALANCE = 
 | 
			
		||||
    struct Balance {
 | 
			
		||||
        uint8 operatorShare;
 | 
			
		||||
        uint96 operatorBalance;
 | 
			
		||||
        uint96 poolBalance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // mapping from Pool to Rebate Balance in ETH
 | 
			
		||||
    mapping (bytes32 => uint256) internal balanceByPoolId;
 | 
			
		||||
    mapping (bytes32 => Balance) internal balanceByPoolId;
 | 
			
		||||
 | 
			
		||||
    // mapping from owner to pool id
 | 
			
		||||
    mapping (bytes32 => address payable) internal ownerByPoolId;
 | 
			
		||||
    // mapping from operator to pool id
 | 
			
		||||
    mapping (bytes32 => address payable) internal operatorByPoolId;
 | 
			
		||||
 | 
			
		||||
    constructor()
 | 
			
		||||
        public
 | 
			
		||||
@@ -53,14 +56,38 @@ contract RewardVault is
 | 
			
		||||
        payable
 | 
			
		||||
        onlyStakingContract
 | 
			
		||||
    {
 | 
			
		||||
        balanceByPoolId[poolId] = _safeAdd(balanceByPoolId[poolId], msg.value);
 | 
			
		||||
        Balance memory balance = balanceByPoolId[poolId];
 | 
			
		||||
        incrementBalance(balance, msg.value);
 | 
			
		||||
        balanceByPoolId[poolId] = balance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function recordDepositFor(bytes32 poolId, uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
        onlyStakingContract
 | 
			
		||||
    {
 | 
			
		||||
        balanceByPoolId[poolId] = _safeAdd(balanceByPoolId[poolId], amount);
 | 
			
		||||
        Balance memory balance = balanceByPoolId[poolId];
 | 
			
		||||
        incrementBalance(balance, amount);
 | 
			
		||||
        balanceByPoolId[poolId] = balance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function incrementBalance(Balance memory balance, uint256 amount) internal pure {
 | 
			
		||||
        require(
 | 
			
		||||
            amount <= (2**96 - 1),
 | 
			
		||||
            "AMOUNT_TOO_HIGH"
 | 
			
		||||
        );
 | 
			
		||||
        require(
 | 
			
		||||
            amount * balance.operatorShare <= (2**96 - 1),
 | 
			
		||||
            "AMOUNT_TOO_HIGH"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // round down the pool portion
 | 
			
		||||
        uint96 poolPortion = (uint96(amount) * (uint96(100) - balance.operatorShare)) / uint96(100);
 | 
			
		||||
        uint96 operatorPortion = uint96(amount) - poolPortion;
 | 
			
		||||
 | 
			
		||||
        // return updated state
 | 
			
		||||
        // @TODO UINT96 SAfeMath
 | 
			
		||||
        balance.operatorBalance += operatorPortion;
 | 
			
		||||
        balance.poolBalance += poolPortion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function deposit()
 | 
			
		||||
@@ -75,26 +102,39 @@ contract RewardVault is
 | 
			
		||||
        onlyStakingContract
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    function withdrawFor(bytes32 poolId, uint256 amount)
 | 
			
		||||
    function withdrawFromOperator(bytes32 poolId, uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
        onlyStakingContract
 | 
			
		||||
    {
 | 
			
		||||
        require(
 | 
			
		||||
            amount <= balanceByPoolId[poolId],
 | 
			
		||||
            amount <= balanceByPoolId[poolId].operatorBalance,
 | 
			
		||||
            "AMOUNT_EXCEEDS_BALANCE_OF_POOL"
 | 
			
		||||
        );
 | 
			
		||||
        balanceByPoolId[poolId] = _safeSub(balanceByPoolId[poolId], amount);
 | 
			
		||||
        balanceByPoolId[poolId].operatorBalance -= uint96(amount);
 | 
			
		||||
        stakingContractAddress.transfer(amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function withdrawFromPool(bytes32 poolId, uint256 amount)
 | 
			
		||||
        external
 | 
			
		||||
        onlyStakingContract
 | 
			
		||||
    {
 | 
			
		||||
        require(
 | 
			
		||||
            amount <= balanceByPoolId[poolId].poolBalance,
 | 
			
		||||
            "AMOUNT_EXCEEDS_BALANCE_OF_POOL"
 | 
			
		||||
        );
 | 
			
		||||
        balanceByPoolId[poolId].poolBalance -= uint96(amount);
 | 
			
		||||
        stakingContractAddress.transfer(amount);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    function withdrawAllFrom(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        onlyInCatostrophicFailure
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        address payable owner = ownerByPoolId[poolId];
 | 
			
		||||
        address payable operator = operatorByPoolId[poolId];
 | 
			
		||||
        require(
 | 
			
		||||
            owner != NIL_ADDRESS,
 | 
			
		||||
            operator != NIL_ADDRESS,
 | 
			
		||||
            "INVALID_OWNER"
 | 
			
		||||
        );
 | 
			
		||||
        uint256 balanceInPool = balanceByPoolId[poolId];
 | 
			
		||||
@@ -104,37 +144,58 @@ contract RewardVault is
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        balanceByPoolId[poolId] = 0;
 | 
			
		||||
        owner.transfer(balanceByPoolId[poolId]);
 | 
			
		||||
        operator.transfer(balanceByPoolId[poolId]);
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    function balanceOf(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return balanceByPoolId[poolId];
 | 
			
		||||
        Balance memory balance = balanceByPoolId[poolId];
 | 
			
		||||
        return balance.operatorBalance + balance.poolBalance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function operatorBalanceOf(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return balanceByPoolId[poolId].operatorBalance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function poolBalanceOf(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (uint256)
 | 
			
		||||
    {
 | 
			
		||||
        return balanceByPoolId[poolId].poolBalance;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // It costs 1 wei to create a pool, but we don't enforce it here.
 | 
			
		||||
    // it's enforced in the staking contract
 | 
			
		||||
    function createPool(bytes32 poolId, address payable poolOwner)
 | 
			
		||||
    function createPool(bytes32 poolId, address payable poolOperator, uint8 poolOperatorShare)
 | 
			
		||||
        external
 | 
			
		||||
        payable
 | 
			
		||||
        onlyStakingContract
 | 
			
		||||
    {
 | 
			
		||||
        require(
 | 
			
		||||
            ownerByPoolId[poolId] == NIL_ADDRESS,
 | 
			
		||||
            operatorByPoolId[poolId] == NIL_ADDRESS,
 | 
			
		||||
            "POOL_ALREADY_EXISTS"
 | 
			
		||||
        );
 | 
			
		||||
        balanceByPoolId[poolId] = msg.value;
 | 
			
		||||
        ownerByPoolId[poolId] = poolOwner;
 | 
			
		||||
        require(
 | 
			
		||||
            poolOperatorShare <= 100,
 | 
			
		||||
            "OPERATOR_SHARE_MUST_BE_BETWEEN_0_AND_100"
 | 
			
		||||
        );
 | 
			
		||||
        balanceByPoolId[poolId].operatorShare = poolOperatorShare;
 | 
			
		||||
        operatorByPoolId[poolId] = poolOperator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getPoolOwner(bytes32 poolId)
 | 
			
		||||
    function getPoolOperator(bytes32 poolId)
 | 
			
		||||
        external
 | 
			
		||||
        view
 | 
			
		||||
        returns (address)
 | 
			
		||||
    {
 | 
			
		||||
        return ownerByPoolId[poolId];
 | 
			
		||||
        return operatorByPoolId[poolId];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -551,7 +551,6 @@ describe('Staking Core', () => {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it.skip('Reward Vault', async () => {
 | 
			
		||||
            /*
 | 
			
		||||
            // 1 setup test parameters
 | 
			
		||||
            const poolOperator = stakers[1];
 | 
			
		||||
            const operatorShare = 39;
 | 
			
		||||
@@ -560,20 +559,17 @@ describe('Staking Core', () => {
 | 
			
		||||
            const notStakingContractAddress = poolOperator;
 | 
			
		||||
            const initialPoolDeposit = stakingWrapper.toBaseUnitAmount(19);
 | 
			
		||||
            // create pool in vault
 | 
			
		||||
            await stakingWrapper.rewardVaultCreatePoolAsync(poolId, poolOperator, initialPoolDeposit, stakingContractAddress);
 | 
			
		||||
            */
 | 
			
		||||
            /*
 | 
			
		||||
            await stakingWrapper.rewardVaultCreatePoolAsync(poolId, poolOperator, operatorShare, stakingContractAddress);
 | 
			
		||||
            // should fail to create pool if it already exists
 | 
			
		||||
            await expectTransactionFailedAsync(
 | 
			
		||||
                stakingWrapper.rewardVaultCreatePoolAsync(poolId, poolOperator, initialPoolDeposit, stakingContractAddress),
 | 
			
		||||
                stakingWrapper.rewardVaultCreatePoolAsync(poolId, poolOperator, operatorShare, stakingContractAddress),
 | 
			
		||||
                RevertReason.PoolAlreadyExists
 | 
			
		||||
            );
 | 
			
		||||
            // should fail to create a pool from an address other than the staking contract
 | 
			
		||||
            await expectTransactionFailedAsync(
 | 
			
		||||
                stakingWrapper.rewardVaultCreatePoolAsync(poolId, poolOperator, initialPoolDeposit, notStakingContractAddress),
 | 
			
		||||
                stakingWrapper.rewardVaultCreatePoolAsync(poolId, poolOperator,  operatorShare, notStakingContractAddress),
 | 
			
		||||
                RevertReason.OnlyCallableByStakingContract
 | 
			
		||||
            );
 | 
			
		||||
            */
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it('Protocol Fees', async () => {
 | 
			
		||||
@@ -827,7 +823,7 @@ describe('Staking Core', () => {
 | 
			
		||||
            expect(makerAddressesForPoolAfterRemoving).to.be.deep.equal([]);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it.skip('Finalization with Protocol Fees', async () => {
 | 
			
		||||
        it('Finalization with Protocol Fees', async () => {
 | 
			
		||||
            ///// 0 DEPLOY EXCHANGE /////
 | 
			
		||||
            await stakingWrapper.addExchangeAddressAsync(exchange);
 | 
			
		||||
            ///// 1 SETUP POOLS /////
 | 
			
		||||
@@ -947,7 +943,7 @@ describe('Staking Core', () => {
 | 
			
		||||
            ///// 9 WITHDRAW PROFITS VIA STAKING CONTRACT /////
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        it.only('Finalization with Protocol Fees and Delegation', async () => {
 | 
			
		||||
        it.skip('Finalization with Protocol Fees and Delegation', async () => {
 | 
			
		||||
            ///// 0 DEPLOY EXCHANGE /////
 | 
			
		||||
            await stakingWrapper.addExchangeAddressAsync(exchange);
 | 
			
		||||
            ///// 1 SETUP POOLS /////
 | 
			
		||||
@@ -1125,7 +1121,19 @@ describe('Staking Core', () => {
 | 
			
		||||
            expect(rewardByDelegator[1]).to.be.bignumber.equal(expectedRewardByDelegator[1]);
 | 
			
		||||
            expect(rewardByDelegator[2]).to.be.bignumber.equal(expectedRewardByDelegator[2]);            
 | 
			
		||||
 | 
			
		||||
            ///// 10 CHECK DELEGATOR BUY-IN ON A SUBSEQUENT EPOCH, WHEN AMOUNT IS NON-ZERO /////
 | 
			
		||||
            ///// 11 CHECK DELEGATOR BUY-IN ON A SUBSEQUENT EPOCH, WHEN AMOUNT IS NON-ZERO /////
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            ///// 12 CHECK PARTIAL PAYOUTS /////
 | 
			
		||||
            /*
 | 
			
		||||
            await stakingWrapper.skipToNextTimelockPeriodAsync();
 | 
			
		||||
            await Promise.all([
 | 
			
		||||
                stakingWrapper.activateAndDelegateStakeAsync(delegators[0], poolIds[2], stakeByDelegator[0]),
 | 
			
		||||
                stakingWrapper.activateAndDelegateStakeAsync(delegators[1], poolIds[2], stakeByDelegator[1]),
 | 
			
		||||
                stakingWrapper.activateAndDelegateStakeAsync(delegators[2], poolIds[2], stakeByDelegator[2]),
 | 
			
		||||
            ]);
 | 
			
		||||
            */
 | 
			
		||||
            
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -428,16 +428,20 @@ export class StakingWrapper {
 | 
			
		||||
        const txReceipt = await this._executeTransactionAsync(calldata, stakingContractAddress, amount);
 | 
			
		||||
        return txReceipt;
 | 
			
		||||
    }
 | 
			
		||||
    /*
 | 
			
		||||
    public async rewardVaultWithdrawFor(poolId: string, amount: BigNumber, stakingContractAddress: string): Promise<TransactionReceiptWithDecodedLogs> {
 | 
			
		||||
        const calldata = this.getRewardVaultContract().withdrawFor.getABIEncodedTransactionData(poolId, amount);
 | 
			
		||||
        const txReceipt = await this._executeTransactionAsync(calldata, stakingContractAddress);
 | 
			
		||||
        return txReceipt;
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    /*
 | 
			
		||||
    public async rewardVaultWithdrawAllForAsync(poolId: string): Promise<TransactionReceiptWithDecodedLogs> {
 | 
			
		||||
        const calldata = this.getRewardVaultContract().withdrawAllFrom.getABIEncodedTransactionData(poolId);
 | 
			
		||||
        const txReceipt = await this._executeTransactionAsync(calldata);
 | 
			
		||||
        return txReceipt;
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    public async rewardVaultEnterCatastrophicFailureModeAsync(zeroExMultisigAddress: string): Promise<TransactionReceiptWithDecodedLogs> {
 | 
			
		||||
        const calldata = this.getRewardVaultContract().enterCatostrophicFailure.getABIEncodedTransactionData();
 | 
			
		||||
        const txReceipt = await this._executeTransactionAsync(calldata, zeroExMultisigAddress);
 | 
			
		||||
@@ -447,15 +451,25 @@ export class StakingWrapper {
 | 
			
		||||
        const balance = await this.getRewardVaultContract().balanceOf.callAsync(poolId);
 | 
			
		||||
        return balance;
 | 
			
		||||
    }
 | 
			
		||||
    public async rewardVaultCreatePoolAsync(poolId: string, poolOwner: string, initialDeposit: BigNumber, stakingContractAddress: string): Promise<TransactionReceiptWithDecodedLogs> {
 | 
			
		||||
        const calldata = this.getRewardVaultContract().createPool.getABIEncodedTransactionData(poolId, poolOwner);
 | 
			
		||||
        const txReceipt = await this._executeTransactionAsync(calldata, stakingContractAddress, initialDeposit);
 | 
			
		||||
    public async rewardVaultOperatorBalanceOfAsync(poolId: string): Promise<BigNumber> {
 | 
			
		||||
        const balance = await this.getRewardVaultContract().operatorBalanceOf.callAsync(poolId);
 | 
			
		||||
        return balance;
 | 
			
		||||
    }
 | 
			
		||||
    public async rewardVaultPoolBalanceOfAsync(poolId: string): Promise<BigNumber> {
 | 
			
		||||
        const balance = await this.getRewardVaultContract().poolBalanceOf.callAsync(poolId);
 | 
			
		||||
        return balance;
 | 
			
		||||
    }
 | 
			
		||||
    public async rewardVaultCreatePoolAsync(poolId: string, poolOperator: string, poolOperatorShare: number, stakingContractAddress: string): Promise<TransactionReceiptWithDecodedLogs> {
 | 
			
		||||
        const calldata = this.getRewardVaultContract().createPool.getABIEncodedTransactionData(poolId, poolOperator, poolOperatorShare);
 | 
			
		||||
        const txReceipt = await this._executeTransactionAsync(calldata, stakingContractAddress);
 | 
			
		||||
        return txReceipt;
 | 
			
		||||
    }
 | 
			
		||||
    /*
 | 
			
		||||
    public async getEthBalanceOfRewardVaultAsync(): Promise<BigNumber> {
 | 
			
		||||
        const balance = await this.getRewardVaultContract().balanceOf.callAsync(this.getZrxVaultContract().address);
 | 
			
		||||
        return balance;
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    ///// ZRX VAULT /////
 | 
			
		||||
    public async getZrxVaultBalance(holder: string): Promise<BigNumber> {
 | 
			
		||||
        const balance = await this.getZrxVaultContract().balanceOf.callAsync(holder);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user