diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml
index b8e19d39b8..daf17c0809 100644
--- a/.github/workflows/benchmarks.yml
+++ b/.github/workflows/benchmarks.yml
@@ -52,17 +52,6 @@ jobs:
- name: Benchmark Join/Exit
run: yarn workspace @balancer-labs/v2-benchmarks measure-join-exit
- merkle-claim:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - name: Set up environment
- uses: ./.github/actions/setup
- - name: Compile
- run: yarn build
- - name: Benchmark Merkle Claim
- run: yarn workspace @balancer-labs/v2-benchmarks measure-merkle-claim
-
relayer:
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b41ffd5656..390429a768 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -163,25 +163,6 @@ jobs:
- name: Run Forge tests
run: yarn workspace @balancer-labs/v2-pool-linear test-fuzz
- test-distributors:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: recursive
- - name: Set up environment
- uses: ./.github/actions/setup
- - name: Compile
- run: yarn build
- - name: Test
- run: yarn workspace @balancer-labs/v2-distributors test
- - name: Install Foundry
- uses: foundry-rs/foundry-toolchain@v1
- with:
- version: nightly
- - name: Run Forge tests
- run: yarn workspace @balancer-labs/v2-distributors test-fuzz
-
test-liquidity-mining:
runs-on: ubuntu-latest
steps:
diff --git a/audits/test-report.md b/audits/test-report.md
index beeff2972d..774bb636d3 100644
--- a/audits/test-report.md
+++ b/audits/test-report.md
@@ -138,73 +138,6 @@ codes instead of standard revert strings.
➤ [@balancer-labs/v2-asset-manager-utils]:
➤ [@balancer-labs/v2-asset-manager-utils]: 54 passing (41s)
➤ [@balancer-labs/v2-asset-manager-utils]:
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]: MerkleRedeem
-➤ [@balancer-labs/v2-distributors]: ✓ stores an allocation (284ms)
-➤ [@balancer-labs/v2-distributors]: ✓ emits RewardAdded when an allocation is stored (252ms)
-➤ [@balancer-labs/v2-distributors]: ✓ requisitions tokens when it stores a balance
-➤ [@balancer-labs/v2-distributors]: ✓ reverts when unauthorized to seed allocation
-➤ [@balancer-labs/v2-distributors]: ✓ stores multiple allocations
-➤ [@balancer-labs/v2-distributors]: with an allocation
-➤ [@balancer-labs/v2-distributors]: ✓ allows the user to claimWeek (255ms)
-➤ [@balancer-labs/v2-distributors]: ✓ emits RewardPaid when an allocation is claimed
-➤ [@balancer-labs/v2-distributors]: ✓ marks claimed weeks as claimed
-➤ [@balancer-labs/v2-distributors]: ✓ allows the user to claimWeek to internal balance
-➤ [@balancer-labs/v2-distributors]: ✓ reverts when a user attempts to claim for another user
-➤ [@balancer-labs/v2-distributors]: ✓ reverts when the user attempts to claim the wrong balance
-➤ [@balancer-labs/v2-distributors]: ✓ reverts when the user attempts to claim twice
-➤ [@balancer-labs/v2-distributors]: ✓ reverts when an admin attempts to overwrite an allocationn
-➤ [@balancer-labs/v2-distributors]: with several allocations
-➤ [@balancer-labs/v2-distributors]: ✓ allows the user to claim multiple weeks at once
-➤ [@balancer-labs/v2-distributors]: ✓ reports weeks as unclaimed
-➤ [@balancer-labs/v2-distributors]: ✓ returns an array of merkle roots
-➤ [@balancer-labs/v2-distributors]: When a user has claimed one of their allocations
-➤ [@balancer-labs/v2-distributors]: ✓ reports one of the weeks as claimed
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]: Staking contract
-➤ [@balancer-labs/v2-distributors]: isAllowlistedRewarder
-➤ [@balancer-labs/v2-distributors]: ✓ allows thet asset managers to allowlist themselves
-➤ [@balancer-labs/v2-distributors]: ✓ allows the owner to allowlist someone
-➤ [@balancer-labs/v2-distributors]: ✓ returns false for random users
-➤ [@balancer-labs/v2-distributors]: addReward
-➤ [@balancer-labs/v2-distributors]: ✓ sets up a reward for an asset manager
-➤ [@balancer-labs/v2-distributors]: stakeWithPermit
-➤ [@balancer-labs/v2-distributors]: ✓ stakes with a permit signature
-➤ [@balancer-labs/v2-distributors]: ✓ stakes with a permit signature to a recipient
-➤ [@balancer-labs/v2-distributors]: with two stakes
-➤ [@balancer-labs/v2-distributors]: ✓ sends expected amount of reward token to the rewards contract
-➤ [@balancer-labs/v2-distributors]: ✓ emits RewardAdded when an allocation is stored
-➤ [@balancer-labs/v2-distributors]: when the rewarder has called notifyRewardAmount
-➤ [@balancer-labs/v2-distributors]: ✓ distributes the reward according to the fraction of staked LP tokens
-➤ [@balancer-labs/v2-distributors]: ✓ allows a user to claim the reward to an EOA
-➤ [@balancer-labs/v2-distributors]: ✓ allows a user to claim the reward to internal balance
-➤ [@balancer-labs/v2-distributors]: ✓ emits RewardPaid when an allocation is claimed
-➤ [@balancer-labs/v2-distributors]: with a second distribution from the same rewarder
-➤ [@balancer-labs/v2-distributors]: ✓ calculates totalEarned from both distributions
-➤ [@balancer-labs/v2-distributors]: with a second distributions from another rewarder
-➤ [@balancer-labs/v2-distributors]: ✓ calculates totalEarned from both distributions
-➤ [@balancer-labs/v2-distributors]: with two pools
-➤ [@balancer-labs/v2-distributors]: ✓ allows you to claim across multiple pools (431ms)
-➤ [@balancer-labs/v2-distributors]: - emits RewardPaid for each pool
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]: Staking contract
-➤ [@balancer-labs/v2-distributors]: with a stake and a reward
-➤ [@balancer-labs/v2-distributors]: ✓ allows a user to claim the reward to a callback contract
-➤ [@balancer-labs/v2-distributors]: ✓ calls the callback on the contract
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]: Reinvestor
-➤ [@balancer-labs/v2-distributors]: with a stake and a reward
-➤ [@balancer-labs/v2-distributors]: with a pool to claim into
-➤ [@balancer-labs/v2-distributors]: ✓ emits PoolBalanceChanged when a LP claims to weighted pool (413ms)
-➤ [@balancer-labs/v2-distributors]: ✓ mints bpt to a LP when they claim to weighted pool (380ms)
-➤ [@balancer-labs/v2-distributors]: addReward
-➤ [@balancer-labs/v2-distributors]: ✓ returns rewards that are unused in reinvestment (438ms)
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]:
-➤ [@balancer-labs/v2-distributors]: 37 passing (41s)
-➤ [@balancer-labs/v2-distributors]: 1 pending
-➤ [@balancer-labs/v2-distributors]:
➤ [@balancer-labs/v2-solidity-utils]:
➤ [@balancer-labs/v2-solidity-utils]:
➤ [@balancer-labs/v2-solidity-utils]: BalancerErrors
diff --git a/pkg/deployments/tasks/deprecated/20210811-ldo-merkle/test/task.fork.ts b/pkg/deployments/tasks/deprecated/20210811-ldo-merkle/test/task.fork.ts
index 84be0d04a3..d464202bd1 100644
--- a/pkg/deployments/tasks/deprecated/20210811-ldo-merkle/test/task.fork.ts
+++ b/pkg/deployments/tasks/deprecated/20210811-ldo-merkle/test/task.fork.ts
@@ -1,10 +1,9 @@
import hre, { ethers } from 'hardhat';
-import { Contract, BigNumber, utils } from 'ethers';
+import { Contract, BigNumber } from 'ethers';
import { bn, fp } from '@balancer-labs/v2-helpers/src/numbers';
import { expectEqualWithError } from '@balancer-labs/v2-helpers/src/test/relativeError';
-import { MerkleTree } from '@balancer-labs/v2-distributors/lib/merkleTree';
-import { deploy } from '@balancer-labs/v2-helpers/src/contract';
+import { MerkleTree } from '@balancer-labs/v2-helpers/src/merkleTree';
import { MAX_UINT256 } from '@balancer-labs/v2-helpers/src/constants';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';
@@ -66,17 +65,5 @@ describeForkTest('MerkleRedeem', 'mainnet', 14850000, function () {
await distributor.connect(lp).claimWeek(lp.address, bn(1), fp(66), proof);
expectEqualWithError(await token.balanceOf(lp.address), fp(66), fp(1));
});
-
- it('can claim a reward to a callback', async () => {
- await distributor.connect(whale).seedAllocations(bn(2), root, fp(100));
-
- const calldata = utils.defaultAbiCoder.encode([], []);
- const callbackContract = await deploy('v2-distributors/MockRewardCallback', { args: [] });
-
- const claims = [{ week: bn(2), balance: fp(66), merkleProof: proof }];
-
- await distributor.connect(lp).claimWeeksWithCallback(lp.address, callbackContract.address, calldata, claims);
- expectEqualWithError(await token.balanceOf(callbackContract.address), fp(66), fp(1));
- });
});
});
diff --git a/pkg/deployments/tasks/deprecated/20210913-bal-arbitrum-merkle/test/task.fork.ts b/pkg/deployments/tasks/deprecated/20210913-bal-arbitrum-merkle/test/task.fork.ts
index 26d1bf7b2d..a879097cfa 100644
--- a/pkg/deployments/tasks/deprecated/20210913-bal-arbitrum-merkle/test/task.fork.ts
+++ b/pkg/deployments/tasks/deprecated/20210913-bal-arbitrum-merkle/test/task.fork.ts
@@ -1,10 +1,9 @@
import hre, { ethers } from 'hardhat';
-import { Contract, BigNumber, utils } from 'ethers';
+import { Contract, BigNumber } from 'ethers';
import { bn, fp } from '@balancer-labs/v2-helpers/src/numbers';
import { expectEqualWithError } from '@balancer-labs/v2-helpers/src/test/relativeError';
-import { MerkleTree } from '@balancer-labs/v2-distributors/lib/merkleTree';
-import { deploy } from '@balancer-labs/v2-helpers/src/contract';
+import { MerkleTree } from '@balancer-labs/v2-helpers/src/merkleTree';
import { MAX_UINT256 } from '@balancer-labs/v2-helpers/src/constants';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';
@@ -66,17 +65,5 @@ describeForkTest('MerkleRedeem', 'arbitrum', 846769, function () {
await distributor.connect(lp).claimWeek(lp.address, bn(1), fp(66), proof);
expectEqualWithError(await token.balanceOf(lp.address), fp(66), fp(1));
});
-
- it('can claim a reward to a callback', async () => {
- await distributor.connect(whale).seedAllocations(bn(2), root, fp(100));
-
- const calldata = utils.defaultAbiCoder.encode([], []);
- const callbackContract = await deploy('v2-distributors/MockRewardCallback', { args: [] });
-
- const claims = [{ week: bn(2), balance: fp(66), merkleProof: proof }];
-
- await distributor.connect(lp).claimWeeksWithCallback(lp.address, callbackContract.address, calldata, claims);
- expectEqualWithError(await token.balanceOf(callbackContract.address), fp(66), fp(1));
- });
});
});
diff --git a/pkg/deployments/tasks/deprecated/20210928-mcb-arbitrum-merkle/test/task.fork.ts b/pkg/deployments/tasks/deprecated/20210928-mcb-arbitrum-merkle/test/task.fork.ts
index ec58e14f9e..f4827c9a62 100644
--- a/pkg/deployments/tasks/deprecated/20210928-mcb-arbitrum-merkle/test/task.fork.ts
+++ b/pkg/deployments/tasks/deprecated/20210928-mcb-arbitrum-merkle/test/task.fork.ts
@@ -1,10 +1,9 @@
import hre, { ethers } from 'hardhat';
-import { Contract, BigNumber, utils } from 'ethers';
+import { Contract, BigNumber } from 'ethers';
import { bn, fp } from '@balancer-labs/v2-helpers/src/numbers';
import { expectEqualWithError } from '@balancer-labs/v2-helpers/src/test/relativeError';
-import { MerkleTree } from '@balancer-labs/v2-distributors/lib/merkleTree';
-import { deploy } from '@balancer-labs/v2-helpers/src/contract';
+import { MerkleTree } from '@balancer-labs/v2-helpers/src/merkleTree';
import { MAX_UINT256 } from '@balancer-labs/v2-helpers/src/constants';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';
@@ -66,17 +65,5 @@ describeForkTest('MerkleRedeem', 'arbitrum', 1731663, function () {
await distributor.connect(lp).claimWeek(lp.address, bn(1), fp(66), proof);
expectEqualWithError(await token.balanceOf(lp.address), fp(66), fp(1));
});
-
- it('can claim a reward to a callback', async () => {
- await distributor.connect(whale).seedAllocations(bn(2), root, fp(100));
-
- const calldata = utils.defaultAbiCoder.encode([], []);
- const callbackContract = await deploy('v2-distributors/MockRewardCallback', { args: [] });
-
- const claims = [{ week: bn(2), balance: fp(66), merkleProof: proof }];
-
- await distributor.connect(lp).claimWeeksWithCallback(lp.address, callbackContract.address, calldata, claims);
- expectEqualWithError(await token.balanceOf(callbackContract.address), fp(66), fp(1));
- });
});
});
diff --git a/pkg/distributors/contracts/MerkleOrchard.sol b/pkg/distributors/contracts/MerkleOrchard.sol
deleted file mode 100644
index ebe464a331..0000000000
--- a/pkg/distributors/contracts/MerkleOrchard.sol
+++ /dev/null
@@ -1,361 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-// 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 experimental ABIEncoderV2;
-
-import "@balancer-labs/v2-interfaces/contracts/solidity-utils/openzeppelin/IERC20.sol";
-import "@balancer-labs/v2-interfaces/contracts/distributors/IDistributorCallback.sol";
-import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
-import "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol";
-
-import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/MerkleProof.sol";
-import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/SafeERC20.sol";
-
-pragma solidity ^0.7.0;
-
-contract MerkleOrchard {
- using SafeERC20 for IERC20;
-
- // Recorded distributions
- // channelId > distributionId
- mapping(bytes32 => uint256) private _nextDistributionId;
- // channelId > distributionId > root
- mapping(bytes32 => mapping(uint256 => bytes32)) private _distributionRoot;
- // channelId > claimer > distributionId / 256 (word index) -> bitmap
- mapping(bytes32 => mapping(address => mapping(uint256 => uint256))) private _claimedBitmap;
- // channelId > balance
- mapping(bytes32 => uint256) private _remainingBalance;
-
- event DistributionAdded(
- address indexed distributor,
- IERC20 indexed token,
- uint256 distributionId,
- bytes32 merkleRoot,
- uint256 amount
- );
- event DistributionClaimed(
- address indexed distributor,
- IERC20 indexed token,
- uint256 distributionId,
- address indexed claimer,
- address recipient,
- uint256 amount
- );
-
- IVault private immutable _vault;
-
- constructor(IVault vault) {
- _vault = vault;
- }
-
- struct Claim {
- uint256 distributionId;
- uint256 balance;
- address distributor;
- uint256 tokenIndex;
- bytes32[] merkleProof;
- }
-
- // Getters
- function getVault() public view returns (IVault) {
- return _vault;
- }
-
- function getDistributionRoot(
- IERC20 token,
- address distributor,
- uint256 distributionId
- ) external view returns (bytes32) {
- bytes32 channelId = _getChannelId(token, distributor);
- return _distributionRoot[channelId][distributionId];
- }
-
- function getRemainingBalance(IERC20 token, address distributor) external view returns (uint256) {
- bytes32 channelId = _getChannelId(token, distributor);
- return _remainingBalance[channelId];
- }
-
- /**
- * @notice distribution ids must be sequential and can have an optional offset
- */
- function getNextDistributionId(IERC20 token, address distributor) external view returns (uint256) {
- bytes32 channelId = _getChannelId(token, distributor);
- return _nextDistributionId[channelId];
- }
-
- function isClaimed(
- IERC20 token,
- address distributor,
- uint256 distributionId,
- address claimer
- ) public view returns (bool) {
- (uint256 distributionWordIndex, uint256 distributionBitIndex) = _getIndices(distributionId);
-
- bytes32 channelId = _getChannelId(token, distributor);
- return (_claimedBitmap[channelId][claimer][distributionWordIndex] & (1 << distributionBitIndex)) != 0;
- }
-
- function verifyClaim(
- IERC20 token,
- address distributor,
- uint256 distributionId,
- address claimer,
- uint256 claimedBalance,
- bytes32[] memory merkleProof
- ) external view returns (bool) {
- bytes32 channelId = _getChannelId(token, distributor);
- return _verifyClaim(channelId, distributionId, claimer, claimedBalance, merkleProof);
- }
-
- // Claim functions
-
- /**
- * @notice Allows anyone to claim multiple distributions for a claimer.
- */
- function claimDistributions(
- address claimer,
- Claim[] memory claims,
- IERC20[] memory tokens
- ) external {
- _processClaims(claimer, claimer, claims, tokens, false);
- }
-
- /**
- * @notice Allows a user to claim their own multiple distributions to internal balance.
- */
- function claimDistributionsToInternalBalance(
- address claimer,
- Claim[] memory claims,
- IERC20[] memory tokens
- ) external {
- require(msg.sender == claimer, "user must claim own balance");
- _processClaims(claimer, claimer, claims, tokens, true);
- }
-
- /**
- * @notice Allows a user to claim their own several distributions to a callback.
- */
- function claimDistributionsWithCallback(
- address claimer,
- Claim[] memory claims,
- IERC20[] memory tokens,
- IDistributorCallback callbackContract,
- bytes calldata callbackData
- ) external {
- require(msg.sender == claimer, "user must claim own balance");
- _processClaims(claimer, address(callbackContract), claims, tokens, true);
- callbackContract.distributorCallback(callbackData);
- }
-
- /**
- * @notice Allows a distributor to add funds to the contract as a merkle tree.
- */
- function createDistribution(
- IERC20 token,
- bytes32 merkleRoot,
- uint256 amount,
- uint256 distributionId
- ) external {
- address distributor = msg.sender;
-
- bytes32 channelId = _getChannelId(token, distributor);
- require(
- _nextDistributionId[channelId] == distributionId || _nextDistributionId[channelId] == 0,
- "invalid distribution ID"
- );
- token.safeTransferFrom(distributor, address(this), amount);
-
- token.safeApprove(address(getVault()), amount);
- IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1);
-
- ops[0] = IVault.UserBalanceOp({
- asset: IAsset(address(token)),
- amount: amount,
- sender: address(this),
- recipient: payable(address(this)),
- kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL
- });
-
- getVault().manageUserBalance(ops);
-
- _remainingBalance[channelId] += amount;
- _distributionRoot[channelId][distributionId] = merkleRoot;
- _nextDistributionId[channelId] = distributionId + 1;
- emit DistributionAdded(distributor, token, distributionId, merkleRoot, amount);
- }
-
- // Helper functions
-
- function _getChannelId(IERC20 token, address distributor) private pure returns (bytes32) {
- return keccak256(abi.encodePacked(token, distributor));
- }
-
- function _processClaims(
- address claimer,
- address recipient,
- Claim[] memory claims,
- IERC20[] memory tokens,
- bool asInternalBalance
- ) internal {
- uint256[] memory amounts = new uint256[](tokens.length);
-
- // To save gas when setting claimed statuses in storage, we group claims for each channel and word index
- // (referred to as a 'claims set'), aggregating the claim bits to set and total claimed amount, only committing
- // to storage when changing claims sets (or when processing the last claim).
- // This means that callers should sort claims by grouping distribution channels and distributions with the same
- // word index in order to achieve reduced gas costs.
-
- // Variables to support claims set aggregation
- bytes32 currentChannelId; // Since channel ids are a hash, the initial zero id can be safely considered invalid
- uint256 currentWordIndex;
-
- uint256 currentBits; // The accumulated claimed bits to set in storage
- uint256 currentClaimAmount; // The accumulated tokens to be claimed from the current channel (not claims set!)
-
- Claim memory claim;
- for (uint256 i = 0; i < claims.length; i++) {
- claim = claims[i];
-
- // New scope to avoid stack-too-deep issues
- {
- (uint256 distributionWordIndex, uint256 distributionBitIndex) = _getIndices(claim.distributionId);
-
- if (currentChannelId == _getChannelId(tokens[claim.tokenIndex], claim.distributor)) {
- if (currentWordIndex == distributionWordIndex) {
- // Same claims set as the previous one: simply track the new bit to set.
- currentBits |= 1 << distributionBitIndex;
- } else {
- // This case is an odd exception: the claims set is not the same, but the channel id is. This
- // happens for example when there are so many distributions that they don't fit in a single 32
- // byte bitmap.
- // Since the channel is the same, we can continue accumulating the claim amount, but must commit
- // the previous claim bits as they correspond to a different word index.
- _setClaimedBits(currentChannelId, claimer, currentWordIndex, currentBits);
-
- // Start a new claims set, except channel id is the same as the previous one, and amount is not
- // reset.
- currentWordIndex = distributionWordIndex;
- currentBits = 1 << distributionBitIndex;
- }
-
- // Amounts are always accumulated for the same channel id
- currentClaimAmount += claim.balance;
- } else {
- // Skip initial invalid claims set
- if (currentChannelId != bytes32(0)) {
- // Commit previous claims set
- _setClaimedBits(currentChannelId, claimer, currentWordIndex, currentBits);
- _deductClaimedBalance(currentChannelId, currentClaimAmount);
- }
-
- // Start a new claims set
- currentChannelId = _getChannelId(tokens[claim.tokenIndex], claim.distributor);
- currentWordIndex = distributionWordIndex;
- currentBits = 1 << distributionBitIndex;
- currentClaimAmount = claim.balance;
- }
- }
-
- // Since a claims set is only committed if the next one is not part of the same set, the last claims set
- // must be manually committed always.
- if (i == claims.length - 1) {
- _setClaimedBits(currentChannelId, claimer, currentWordIndex, currentBits);
- _deductClaimedBalance(currentChannelId, currentClaimAmount);
- }
-
- require(
- _verifyClaim(currentChannelId, claim.distributionId, claimer, claim.balance, claim.merkleProof),
- "incorrect merkle proof"
- );
-
- // Note that balances to claim are here accumulated *per token*, independent of the distribution channel and
- // claims set accounting.
- amounts[claim.tokenIndex] += claim.balance;
-
- emit DistributionClaimed(
- claim.distributor,
- tokens[claim.tokenIndex],
- claim.distributionId,
- claimer,
- recipient,
- claim.balance
- );
- }
-
- IVault.UserBalanceOpKind kind = asInternalBalance
- ? IVault.UserBalanceOpKind.TRANSFER_INTERNAL
- : IVault.UserBalanceOpKind.WITHDRAW_INTERNAL;
- IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](tokens.length);
-
- for (uint256 i = 0; i < tokens.length; i++) {
- ops[i] = IVault.UserBalanceOp({
- asset: IAsset(address(tokens[i])),
- amount: amounts[i],
- sender: address(this),
- recipient: payable(recipient),
- kind: kind
- });
- }
- getVault().manageUserBalance(ops);
- }
-
- /**
- * @dev Sets the bits set in `newClaimsBitmap` for the corresponding distribution.
- */
- function _setClaimedBits(
- bytes32 channelId,
- address claimer,
- uint256 wordIndex,
- uint256 newClaimsBitmap
- ) private {
- uint256 currentBitmap = _claimedBitmap[channelId][claimer][wordIndex];
-
- // All newly set bits must not have been previously set
- require((newClaimsBitmap & currentBitmap) == 0, "cannot claim twice");
-
- _claimedBitmap[channelId][claimer][wordIndex] = currentBitmap | newClaimsBitmap;
- }
-
- /**
- * @dev Deducts `balanceBeingClaimed` from a distribution channel's allocation. This isolates tokens accross
- * distribution channels, and prevents claims for one channel from using the tokens of another one.
- */
- function _deductClaimedBalance(bytes32 channelId, uint256 balanceBeingClaimed) private {
- require(
- _remainingBalance[channelId] >= balanceBeingClaimed,
- "distributor hasn't provided sufficient tokens for claim"
- );
- _remainingBalance[channelId] -= balanceBeingClaimed;
- }
-
- function _verifyClaim(
- bytes32 channelId,
- uint256 distributionId,
- address claimer,
- uint256 claimedBalance,
- bytes32[] memory merkleProof
- ) internal view returns (bool) {
- bytes32 leaf = keccak256(abi.encodePacked(claimer, claimedBalance));
- return MerkleProof.verify(merkleProof, _distributionRoot[channelId][distributionId], leaf);
- }
-
- function _getIndices(uint256 distributionId)
- private
- pure
- returns (uint256 distributionWordIndex, uint256 distributionBitIndex)
- {
- distributionWordIndex = distributionId / 256;
- distributionBitIndex = distributionId % 256;
- }
-}
diff --git a/pkg/distributors/contracts/MerkleRedeem.sol b/pkg/distributors/contracts/MerkleRedeem.sol
deleted file mode 100644
index 5b3db335b5..0000000000
--- a/pkg/distributors/contracts/MerkleRedeem.sol
+++ /dev/null
@@ -1,201 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-// 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 experimental ABIEncoderV2;
-
-import "@balancer-labs/v2-interfaces/contracts/solidity-utils/openzeppelin/IERC20.sol";
-import "@balancer-labs/v2-interfaces/contracts/distributors/IDistributorCallback.sol";
-import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol";
-import "@balancer-labs/v2-interfaces/contracts/vault/IAsset.sol";
-
-import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/MerkleProof.sol";
-import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/SafeERC20.sol";
-import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/Ownable.sol";
-import "@balancer-labs/v2-solidity-utils/contracts/math/FixedPoint.sol";
-
-pragma solidity ^0.7.0;
-
-contract MerkleRedeem is Ownable {
- using FixedPoint for uint256;
- using SafeERC20 for IERC20;
-
- IERC20 public immutable rewardToken;
-
- // Recorded weeks
- mapping(uint256 => bytes32) public weekMerkleRoots;
- mapping(uint256 => mapping(address => bool)) public claimed;
-
- IVault public immutable vault;
-
- event RewardAdded(address indexed token, uint256 amount);
- event RewardPaid(address indexed user, address indexed rewardToken, uint256 amount);
-
- constructor(IVault _vault, IERC20 _rewardToken) {
- vault = _vault;
- rewardToken = _rewardToken;
- _rewardToken.safeApprove(address(_vault), type(uint256).max);
- }
-
- function _disburse(address recipient, uint256 balance) private {
- if (balance > 0) {
- emit RewardPaid(recipient, address(rewardToken), balance);
- rewardToken.safeTransfer(recipient, balance);
- }
- }
-
- function _disburseToInternalBalance(address recipient, uint256 balance) private {
- if (balance > 0) {
- IVault.UserBalanceOp[] memory ops = new IVault.UserBalanceOp[](1);
-
- ops[0] = IVault.UserBalanceOp({
- asset: IAsset(address(rewardToken)),
- amount: balance,
- sender: address(this),
- recipient: payable(recipient),
- kind: IVault.UserBalanceOpKind.DEPOSIT_INTERNAL
- });
-
- emit RewardPaid(recipient, address(rewardToken), balance);
- vault.manageUserBalance(ops);
- }
- }
-
- /**
- * @notice Allows a user to claim a particular week's worth of rewards
- */
- function claimWeek(
- address liquidityProvider,
- uint256 week,
- uint256 claimedBalance,
- bytes32[] memory merkleProof
- ) external {
- require(msg.sender == liquidityProvider, "user must claim own balance");
- require(!claimed[week][liquidityProvider], "cannot claim twice");
- require(verifyClaim(liquidityProvider, week, claimedBalance, merkleProof), "Incorrect merkle proof");
-
- claimed[week][liquidityProvider] = true;
- _disburse(liquidityProvider, claimedBalance);
- }
-
- struct Claim {
- uint256 week;
- uint256 balance;
- bytes32[] merkleProof;
- }
-
- function _processClaims(address liquidityProvider, Claim[] memory claims) internal returns (uint256 totalBalance) {
- Claim memory claim;
- for (uint256 i = 0; i < claims.length; i++) {
- claim = claims[i];
-
- require(!claimed[claim.week][liquidityProvider], "cannot claim twice");
- require(
- verifyClaim(liquidityProvider, claim.week, claim.balance, claim.merkleProof),
- "Incorrect merkle proof"
- );
-
- totalBalance = totalBalance.add(claim.balance);
- claimed[claim.week][liquidityProvider] = true;
- }
- }
-
- /**
- * @notice Allows a user to claim multiple weeks of reward
- */
- function claimWeeks(address liquidityProvider, Claim[] memory claims) external {
- require(msg.sender == liquidityProvider, "user must claim own balance");
-
- uint256 totalBalance = _processClaims(liquidityProvider, claims);
- _disburse(liquidityProvider, totalBalance);
- }
-
- /**
- * @notice Allows a user to claim multiple weeks of reward to internal balance
- */
- function claimWeeksToInternalBalance(address liquidityProvider, Claim[] memory claims) external {
- require(msg.sender == liquidityProvider, "user must claim own balance");
-
- uint256 totalBalance = _processClaims(liquidityProvider, claims);
-
- _disburseToInternalBalance(liquidityProvider, totalBalance);
- }
-
- /**
- * @notice Allows a user to claim several weeks of rewards to a callback
- */
- function claimWeeksWithCallback(
- address liquidityProvider,
- IDistributorCallback callbackContract,
- bytes calldata callbackData,
- Claim[] memory claims
- ) external {
- require(msg.sender == liquidityProvider, "user must claim own balance");
- uint256 totalBalance = _processClaims(liquidityProvider, claims);
-
- _disburseToInternalBalance(address(callbackContract), totalBalance);
-
- callbackContract.distributorCallback(callbackData);
- }
-
- function claimStatus(
- address liquidityProvider,
- uint256 begin,
- uint256 end
- ) external view returns (bool[] memory) {
- require(begin <= end, "weeks must be specified in ascending order");
- uint256 size = 1 + end - begin;
- bool[] memory arr = new bool[](size);
- for (uint256 i = 0; i < size; i++) {
- arr[i] = claimed[begin + i][liquidityProvider];
- }
- return arr;
- }
-
- function merkleRoots(uint256 begin, uint256 end) external view returns (bytes32[] memory) {
- require(begin <= end, "weeks must be specified in ascending order");
- uint256 size = 1 + end - begin;
- bytes32[] memory arr = new bytes32[](size);
- for (uint256 i = 0; i < size; i++) {
- arr[i] = weekMerkleRoots[begin + i];
- }
- return arr;
- }
-
- function verifyClaim(
- address liquidityProvider,
- uint256 week,
- uint256 claimedBalance,
- bytes32[] memory merkleProof
- ) public view returns (bool) {
- bytes32 leaf = keccak256(abi.encodePacked(liquidityProvider, claimedBalance));
- return MerkleProof.verify(merkleProof, weekMerkleRoots[week], leaf);
- }
-
- /**
- * @notice
- * Allows the owner to add funds to the contract as a merkle tree, These tokens will
- * be withdrawn from the sender
- * These will be pulled from the user
- */
- function seedAllocations(
- uint256 week,
- bytes32 _merkleRoot,
- uint256 amount
- ) external onlyOwner {
- require(weekMerkleRoots[week] == bytes32(0), "cannot rewrite merkle root");
- weekMerkleRoots[week] = _merkleRoot;
- rewardToken.safeTransferFrom(msg.sender, address(this), amount);
- emit RewardAdded(address(rewardToken), amount);
- }
-}
diff --git a/pkg/distributors/contracts/test/MockRewardCallback.sol b/pkg/distributors/contracts/test/MockRewardCallback.sol
deleted file mode 100644
index 9af62e2551..0000000000
--- a/pkg/distributors/contracts/test/MockRewardCallback.sol
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-or-later
-// 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.7.0;
-pragma experimental ABIEncoderV2;
-
-import "@balancer-labs/v2-interfaces/contracts/distributors/IDistributorCallback.sol";
-
-contract MockRewardCallback is IDistributorCallback {
- event CallbackReceived();
-
- function distributorCallback(bytes calldata) external override {
- emit CallbackReceived();
- return;
- }
-}
diff --git a/pkg/distributors/foundry.toml b/pkg/distributors/foundry.toml
deleted file mode 120000
index 2d554be761..0000000000
--- a/pkg/distributors/foundry.toml
+++ /dev/null
@@ -1 +0,0 @@
-../../foundry.toml
\ No newline at end of file
diff --git a/pkg/distributors/hardhat.config.ts b/pkg/distributors/hardhat.config.ts
deleted file mode 100644
index f218c31a09..0000000000
--- a/pkg/distributors/hardhat.config.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import '@nomiclabs/hardhat-ethers';
-import '@nomiclabs/hardhat-waffle';
-import 'hardhat-ignore-warnings';
-
-import { hardhatBaseConfig } from '@balancer-labs/v2-common';
-import { name } from './package.json';
-
-import { task } from 'hardhat/config';
-import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names';
-import overrideQueryFunctions from '@balancer-labs/v2-helpers/plugins/overrideQueryFunctions';
-
-task(TASK_COMPILE).setAction(overrideQueryFunctions);
-
-export default {
- solidity: {
- compilers: hardhatBaseConfig.compilers,
- overrides: { ...hardhatBaseConfig.overrides(name) },
- },
- warnings: hardhatBaseConfig.warnings,
-};
diff --git a/pkg/distributors/package.json b/pkg/distributors/package.json
deleted file mode 100644
index bc990a4371..0000000000
--- a/pkg/distributors/package.json
+++ /dev/null
@@ -1,63 +0,0 @@
-{
- "name": "@balancer-labs/v2-distributors",
- "version": "0.1.0",
- "description": "Utilities for distributing tokens from internal balance",
- "license": "GPL-3.0-only",
- "homepage": "https://github.com/balancer-labs/balancer-v2-monorepo/pkg/distributors#readme",
- "repository": {
- "type": "git",
- "url": "https://github.com/balancer-labs/balancer-v2-monorepo.git",
- "directory": "pkg/distributors"
- },
- "bugs": {
- "url": "https://github.com/balancer-labs/balancer-v2-monorepo/issues"
- },
- "scripts": {
- "build": "yarn compile",
- "compile": "hardhat compile && rm -rf artifacts/build-info",
- "compile:watch": "nodemon --ext sol --exec yarn compile",
- "lint": "yarn lint:solidity && yarn lint:typescript",
- "lint:solidity": "solhint 'contracts/**/*.sol'",
- "lint:typescript": "eslint --max-warnings 0 . --ext .ts",
- "test": "yarn compile && mocha --extension ts --require hardhat/register --require @balancer-labs/v2-common/setupTests --recursive",
- "test:fast": "yarn compile && mocha --extension ts --require hardhat/register --require @balancer-labs/v2-common/setupTests --recursive --parallel --exit",
- "test:watch": "nodemon --ext js,ts --watch test --watch lib --exec 'clear && yarn test --no-compile'",
- "test-fuzz": "forge test"
- },
- "devDependencies": {
- "@balancer-labs/balancer-js": "workspace:*",
- "@balancer-labs/v2-interfaces": "workspace:*",
- "@balancer-labs/v2-solidity-utils": "workspace:*",
- "@nomiclabs/hardhat-ethers": "^2.2.1",
- "@nomiclabs/hardhat-waffle": "^2.0.3",
- "@types/chai": "^4.3.3",
- "@types/lodash": "^4.14.186",
- "@types/mocha": "^10.0.0",
- "@types/node": "^14.14.31",
- "@typescript-eslint/eslint-plugin": "^5.41.0",
- "@typescript-eslint/parser": "^5.41.0",
- "chai": "^4.3.6",
- "decimal.js": "^10.4.2",
- "eslint": "^8.26.0",
- "eslint-plugin-mocha-no-only": "^1.1.1",
- "eslint-plugin-prettier": "^4.2.1",
- "ethereum-waffle": "^3.4.4",
- "ethereumjs-util": "^7.1.5",
- "ethers": "^5.7.2",
- "hardhat": "^2.12.2",
- "hardhat-ignore-warnings": "^0.2.4",
- "lodash.frompairs": "^4.0.1",
- "lodash.pick": "^4.4.0",
- "lodash.range": "^3.2.0",
- "lodash.times": "^4.3.2",
- "lodash.zip": "^4.2.0",
- "mocha": "^10.1.0",
- "nodemon": "^2.0.20",
- "prettier": "^2.7.1",
- "prettier-plugin-solidity": "v1.0.0-alpha.59",
- "solhint": "^3.2.0",
- "solhint-plugin-prettier": "^0.0.4",
- "ts-node": "^10.9.1",
- "typescript": "^4.0.2"
- }
-}
diff --git a/pkg/distributors/test/MerkleOrchard.test.ts b/pkg/distributors/test/MerkleOrchard.test.ts
deleted file mode 100644
index 08dbc630e1..0000000000
--- a/pkg/distributors/test/MerkleOrchard.test.ts
+++ /dev/null
@@ -1,618 +0,0 @@
-import { ethers } from 'hardhat';
-import { BytesLike, BigNumber } from 'ethers';
-import { expect } from 'chai';
-import { Contract, utils } from 'ethers';
-
-import { deploy } from '@balancer-labs/v2-helpers/src/contract';
-import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault';
-import { expectBalanceChange } from '@balancer-labs/v2-helpers/src/test/tokenBalance';
-import Token from '@balancer-labs/v2-helpers/src/models/tokens/Token';
-import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList';
-import * as expectEvent from '@balancer-labs/v2-helpers/src/test/expectEvent';
-
-import { bn } from '@balancer-labs/v2-helpers/src/numbers';
-import { MerkleTree } from '../lib/merkleTree';
-
-import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';
-
-function encodeElement(address: string, balance: BigNumber): string {
- return ethers.utils.solidityKeccak256(['address', 'uint'], [address, balance]);
-}
-
-interface Claim {
- distributionId: BigNumber;
- balance: BigNumber;
- distributor: string;
- tokenIndex: number;
- merkleProof: BytesLike[];
-}
-
-describe('MerkleOrchard', () => {
- let tokens: TokenList,
- token1: Token,
- token2: Token,
- vault: Contract,
- merkleOrchard: Contract,
- tokenAddresses: string[];
-
- let admin: SignerWithAddress,
- distributor: SignerWithAddress,
- claimer1: SignerWithAddress,
- claimer2: SignerWithAddress,
- other: SignerWithAddress;
- const tokenInitialBalance = bn(100e18);
- const claimBalance = bn('9876');
-
- before('setup', async () => {
- [, admin, distributor, claimer1, claimer2, other] = await ethers.getSigners();
- });
-
- sharedBeforeEach('deploy vault and tokens', async () => {
- const vaultHelper = await Vault.create({ admin });
- vault = vaultHelper.instance;
-
- tokens = await TokenList.create(['DAI', 'BAT'], { sorted: true });
- token1 = tokens.DAI;
- token2 = tokens.BAT;
- tokenAddresses = [token1.address, token2.address];
-
- merkleOrchard = await deploy('MerkleOrchard', {
- args: [vault.address],
- from: admin,
- });
- await tokens.mint({ to: distributor.address, amount: tokenInitialBalance });
- await tokens.approve({ to: merkleOrchard.address, from: [distributor] });
- });
-
- it('stores an allocation', async () => {
- const elements = [encodeElement(claimer1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, bn(1));
-
- const proof = merkleTree.getHexProof(elements[0]);
-
- const result = await merkleOrchard.verifyClaim(
- token1.address,
- distributor.address,
- 1,
- claimer1.address,
- claimBalance,
- proof
- );
- expect(result).to.equal(true);
- });
-
- it('emits DistributionAdded when an allocation is stored', async () => {
- const elements = [encodeElement(claimer1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- const receipt = await (
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, bn(1))
- ).wait();
-
- expectEvent.inReceipt(receipt, 'DistributionAdded', {
- token: token1.address,
- distributor: distributor.address,
- merkleRoot: root,
- amount: claimBalance,
- });
- });
-
- it('requisitions tokens when it stores a balance', async () => {
- const elements = [encodeElement(claimer1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await expectBalanceChange(
- () => merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, bn(1)),
- tokens,
- [{ account: merkleOrchard, changes: { DAI: claimBalance } }],
- vault
- );
- });
-
- it("increments the distribution channel's distributionId", async () => {
- const elements = [encodeElement(claimer1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- expect(await merkleOrchard.getNextDistributionId(token1.address, distributor.address)).to.be.eq(0);
- expect(await merkleOrchard.getNextDistributionId(token1.address, claimer1.address)).to.be.eq(0);
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, 0);
-
- expect(await merkleOrchard.getNextDistributionId(token1.address, distributor.address)).to.be.eq(1);
- expect(await merkleOrchard.getNextDistributionId(token1.address, claimer1.address)).to.be.eq(0);
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, 1);
-
- expect(await merkleOrchard.getNextDistributionId(token1.address, distributor.address)).to.be.eq(2);
- expect(await merkleOrchard.getNextDistributionId(token1.address, claimer1.address)).to.be.eq(0);
- });
-
- it('allows the distribution id to have an offset', async () => {
- const elements = [encodeElement(claimer1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, 72);
- expect(await merkleOrchard.getNextDistributionId(token1.address, distributor.address)).to.be.eq(73);
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, 73);
- expect(await merkleOrchard.getNextDistributionId(token1.address, distributor.address)).to.be.eq(74);
- });
-
- context('when provided an invalid distribution ID', () => {
- it('reverts', async () => {
- const elements = [encodeElement(claimer1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, 0);
- // Here's we're providing an old ID (i.e. we've accidentally created the same distribution twice)
- await expect(
- merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimBalance, 0)
- ).to.be.revertedWith('invalid distribution ID');
- });
- });
-
- it('stores multiple allocations', async () => {
- const claimBalance0 = bn('1000');
- const claimBalance1 = bn('2000');
-
- const elements = [encodeElement(claimer1.address, claimBalance0), encodeElement(claimer2.address, claimBalance1)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, bn('3000'), 1);
-
- const proof0 = merkleTree.getHexProof(elements[0]);
- let result = await merkleOrchard.verifyClaim(
- token1.address,
- distributor.address,
- 1,
- claimer1.address,
- claimBalance0,
- proof0
- );
- expect(result).to.equal(true); //"account 0 should have an allocation";
-
- const proof1 = merkleTree.getHexProof(elements[1]);
- result = await merkleOrchard.verifyClaim(
- token1.address,
- distributor.address,
- 1,
- claimer2.address,
- claimBalance1,
- proof1
- );
- expect(result).to.equal(true); // "account 1 should have an allocation";
- });
-
- describe('with an allocation', () => {
- const claimableBalance = bn('1000');
- let elements: string[];
- let merkleTree: MerkleTree;
- let claims: Claim[];
-
- sharedBeforeEach(async () => {
- elements = [encodeElement(claimer1.address, claimableBalance)];
- merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root, claimableBalance, 1);
- const merkleProof: BytesLike[] = merkleTree.getHexProof(elements[0]);
-
- claims = [
- {
- distributionId: bn(1),
- balance: claimableBalance,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof,
- },
- ];
- });
-
- it('allows the user to claim a single distribution', async () => {
- await expectBalanceChange(
- () => merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses),
- tokens,
- [{ account: claimer1, changes: { DAI: claimableBalance } }]
- );
- });
-
- it('emits DistributionClaimed when an allocation is claimed', async () => {
- const receipt = await (
- await merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses)
- ).wait();
-
- expectEvent.inReceipt(receipt, 'DistributionClaimed', {
- claimer: claimer1.address,
- recipient: claimer1.address,
- token: token1.address,
- distributor: distributor.address,
- amount: claimableBalance,
- });
- });
-
- it('marks claimed distributions as claimed', async () => {
- await merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses);
-
- const isClaimed = await merkleOrchard.isClaimed(token1.address, distributor.address, 1, claimer1.address);
- expect(isClaimed).to.equal(true); // "claim should be marked as claimed";
- });
-
- it('allows anyone to claim for another user', async () => {
- await expectBalanceChange(
- () => merkleOrchard.connect(other).claimDistributions(claimer1.address, claims, tokenAddresses),
- tokens,
- [{ account: claimer1, changes: { DAI: claimableBalance } }]
- );
- });
-
- it('reverts when a user attempts to claim to internal balance for another user', async () => {
- const errorMsg = 'user must claim own balance';
- await expect(
- merkleOrchard.connect(other).claimDistributionsToInternalBalance(claimer1.address, claims, tokenAddresses)
- ).to.be.revertedWith(errorMsg);
- });
-
- it('reverts when the user attempts to claim the wrong balance', async () => {
- const incorrectClaimedBalance = bn('666');
- const merkleProof = merkleTree.getHexProof(elements[0]);
- const errorMsg = 'incorrect merkle proof';
-
- const claimsWithIncorrectClaimableBalance = [
- {
- distributionId: 1,
- balance: incorrectClaimedBalance,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof,
- },
- ];
- await expect(
- merkleOrchard
- .connect(claimer1)
- .claimDistributions(claimer1.address, claimsWithIncorrectClaimableBalance, tokenAddresses)
- ).to.be.revertedWith(errorMsg);
- });
-
- it('reverts when the user attempts to claim twice', async () => {
- await merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses);
-
- const errorMsg = 'cannot claim twice';
- await expect(
- merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses)
- ).to.be.revertedWith(errorMsg);
- });
-
- it('reverts when an admin attempts to overwrite an allocationn', async () => {
- const elements2 = [
- encodeElement(claimer1.address, claimableBalance),
- encodeElement(claimer2.address, claimableBalance),
- ];
- const merkleTree2 = new MerkleTree(elements2);
- const root2 = merkleTree2.getHexRoot();
-
- const errorMsg = 'invalid distribution ID';
- expect(
- merkleOrchard.connect(admin).createDistribution(token1.address, root2, claimableBalance.mul(2), 1)
- ).to.be.revertedWith(errorMsg);
- });
- });
-
- describe('with several allocations in the same channel', () => {
- const claimBalance1 = bn('1000');
- const claimBalance2 = bn('1234');
-
- let elements1: string[];
- let merkleTree1: MerkleTree;
- let root1: string;
-
- let elements2: string[];
- let merkleTree2: MerkleTree;
- let root2: string;
-
- sharedBeforeEach(async () => {
- elements1 = [encodeElement(claimer1.address, claimBalance1)];
- merkleTree1 = new MerkleTree(elements1);
- root1 = merkleTree1.getHexRoot();
-
- elements2 = [encodeElement(claimer1.address, claimBalance2)];
- merkleTree2 = new MerkleTree(elements2);
- root2 = merkleTree2.getHexRoot();
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root1, claimBalance1, bn(1));
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root2, claimBalance2, bn(2));
- });
-
- it('allows the user to claim multiple distributions at once', async () => {
- const claimedBalance1 = bn('1000');
- const claimedBalance2 = bn('1234');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- const claims: Claim[] = [
- {
- distributionId: bn(1),
- balance: claimedBalance1,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof1,
- },
- {
- distributionId: bn(2),
- balance: claimedBalance2,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof2,
- },
- ];
-
- await expectBalanceChange(
- () => merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses),
- tokens,
- [{ account: claimer1, changes: { DAI: bn('2234') } }]
- );
- });
-
- it('allows the user to claim multiple distributions at once to internal balance', async () => {
- const claimedBalance1 = bn('1000');
- const claimedBalance2 = bn('1234');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- const claims: Claim[] = [
- {
- distributionId: bn(1),
- balance: claimedBalance1,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof1,
- },
- {
- distributionId: bn(2),
- balance: claimedBalance2,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof2,
- },
- ];
-
- await expectBalanceChange(
- () =>
- merkleOrchard.connect(claimer1).claimDistributionsToInternalBalance(claimer1.address, claims, tokenAddresses),
- tokens,
- [{ account: claimer1, changes: { DAI: bn('2234') } }],
- vault
- );
- });
-
- it('reports distributions as unclaimed', async () => {
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 1, claimer1.address)).to.eql(false);
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 2, claimer1.address)).to.eql(false);
- });
-
- describe('with a callback', () => {
- let callbackContract: Contract;
- let claims: Claim[];
-
- sharedBeforeEach('set up mock callback', async () => {
- callbackContract = await deploy('MockRewardCallback');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- claims = [
- {
- distributionId: bn(1),
- balance: claimBalance1,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof1,
- },
- {
- distributionId: bn(2),
- balance: claimBalance2,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof2,
- },
- ];
- });
-
- it('allows a user to claim the tokens to a callback contract', async () => {
- const expectedClaim = claimBalance1.add(claimBalance2);
- const calldata = utils.defaultAbiCoder.encode([], []);
-
- await expectBalanceChange(
- () =>
- merkleOrchard
- .connect(claimer1)
- .claimDistributionsWithCallback(
- claimer1.address,
- claims,
- tokenAddresses,
- callbackContract.address,
- calldata
- ),
- tokens,
- [{ account: callbackContract.address, changes: { DAI: ['very-near', expectedClaim] } }],
- vault
- );
- });
-
- it('calls the callback on the contract', async () => {
- const calldata = utils.defaultAbiCoder.encode([], []);
-
- const receipt = await (
- await merkleOrchard
- .connect(claimer1)
- .claimDistributionsWithCallback(
- claimer1.address,
- claims,
- tokenAddresses,
- callbackContract.address,
- calldata
- )
- ).wait();
-
- expectEvent.inIndirectReceipt(receipt, callbackContract.interface, 'CallbackReceived', {});
- });
- });
-
- describe('When a user has claimed one of their allocations', async () => {
- sharedBeforeEach(async () => {
- const claimedBalance1 = bn('1000');
- const proof1 = merkleTree1.getHexProof(elements1[0]);
-
- const claims: Claim[] = [
- {
- distributionId: bn(1),
- balance: claimedBalance1,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof1,
- },
- ];
-
- await merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses);
- });
-
- it('reports one of the distributions as claimed', async () => {
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 1, claimer1.address)).to.eql(true);
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 2, claimer1.address)).to.eql(false);
- });
- });
- });
-
- describe('with allocations in the same channel, crossing claim storage slots', () => {
- const claimBalance1 = bn('1000');
- const claimBalance2 = bn('1234');
-
- let elements1: string[];
- let merkleTree1: MerkleTree;
- let root1: string;
-
- let elements2: string[];
- let merkleTree2: MerkleTree;
- let root2: string;
-
- let proof1: BytesLike[];
- let proof2: BytesLike[];
- let claims: Claim[];
-
- sharedBeforeEach(async () => {
- elements1 = [encodeElement(claimer1.address, claimBalance1)];
- merkleTree1 = new MerkleTree(elements1);
- root1 = merkleTree1.getHexRoot();
- proof1 = merkleTree1.getHexProof(elements1[0]);
-
- elements2 = [encodeElement(claimer1.address, claimBalance2)];
- merkleTree2 = new MerkleTree(elements2);
- root2 = merkleTree2.getHexRoot();
- proof2 = merkleTree2.getHexProof(elements2[0]);
-
- // very end of one claim storage slot
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root1, claimBalance1, bn(255));
- // very beginning of the next claim storage slot
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root2, claimBalance2, bn(256));
-
- claims = [
- {
- distributionId: bn(255),
- balance: claimBalance1,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof1,
- },
- {
- distributionId: bn(256),
- balance: claimBalance2,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof2,
- },
- ];
- });
-
- it('allows the user to claim multiple distributions at once', async () => {
- await expectBalanceChange(
- () => merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses),
- tokens,
- [{ account: claimer1, changes: { DAI: bn('2234') } }]
- );
- });
-
- it('marks distributions as claimed', async () => {
- await merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses);
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 254, claimer1.address)).to.eql(false);
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 255, claimer1.address)).to.eql(true);
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 256, claimer1.address)).to.eql(true);
- expect(await merkleOrchard.isClaimed(token1.address, distributor.address, 257, claimer1.address)).to.eql(false);
- });
- });
-
- describe('with several allocations accross multiple channels', () => {
- const claimBalance1 = bn('1000');
- const claimBalance2 = bn('1234');
-
- let elements1: string[];
- let merkleTree1: MerkleTree;
- let root1: string;
-
- let elements2: string[];
- let merkleTree2: MerkleTree;
- let root2: string;
-
- sharedBeforeEach(async () => {
- elements1 = [encodeElement(claimer1.address, claimBalance1)];
- merkleTree1 = new MerkleTree(elements1);
- root1 = merkleTree1.getHexRoot();
-
- elements2 = [encodeElement(claimer1.address, claimBalance2)];
- merkleTree2 = new MerkleTree(elements2);
- root2 = merkleTree2.getHexRoot();
-
- await merkleOrchard.connect(distributor).createDistribution(token1.address, root1, claimBalance1, bn(1));
-
- await merkleOrchard.connect(distributor).createDistribution(token2.address, root2, claimBalance2, bn(2));
- });
-
- it('allows the user to claim multiple distributions at once', async () => {
- const claimedBalance1 = bn('1000');
- const claimedBalance2 = bn('1234');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- const claims: Claim[] = [
- {
- distributionId: bn(1),
- balance: claimedBalance1,
- distributor: distributor.address,
- tokenIndex: 0,
- merkleProof: proof1,
- },
- {
- distributionId: bn(2),
- balance: claimedBalance2,
- distributor: distributor.address,
- tokenIndex: 1,
- merkleProof: proof2,
- },
- ];
-
- await expectBalanceChange(
- () => merkleOrchard.connect(claimer1).claimDistributions(claimer1.address, claims, tokenAddresses),
- tokens,
- [{ account: claimer1, changes: { DAI: bn('1000'), BAT: bn('1234') } }]
- );
- });
- });
-});
diff --git a/pkg/distributors/test/MerkleRedeem.test.ts b/pkg/distributors/test/MerkleRedeem.test.ts
deleted file mode 100644
index 89b3baf31a..0000000000
--- a/pkg/distributors/test/MerkleRedeem.test.ts
+++ /dev/null
@@ -1,352 +0,0 @@
-import { ethers } from 'hardhat';
-import { BytesLike, BigNumber } from 'ethers';
-import { expect } from 'chai';
-import { Contract, utils } from 'ethers';
-
-import { deploy } from '@balancer-labs/v2-helpers/src/contract';
-import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault';
-import { expectBalanceChange } from '@balancer-labs/v2-helpers/src/test/tokenBalance';
-import Token from '@balancer-labs/v2-helpers/src/models/tokens/Token';
-import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList';
-import * as expectEvent from '@balancer-labs/v2-helpers/src/test/expectEvent';
-
-import { bn } from '@balancer-labs/v2-helpers/src/numbers';
-import { MerkleTree } from '../lib/merkleTree';
-
-import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';
-
-function encodeElement(address: string, balance: BigNumber): string {
- return ethers.utils.solidityKeccak256(['address', 'uint'], [address, balance]);
-}
-
-describe('MerkleRedeem', () => {
- let rewardTokens: TokenList, rewardToken: Token, vault: Contract, merkleRedeem: Contract;
-
- let admin: SignerWithAddress, lp1: SignerWithAddress, lp2: SignerWithAddress, other: SignerWithAddress;
- const rewardTokenInitialBalance = bn(100e18);
- const week1 = bn(1);
-
- before('setup', async () => {
- [, admin, lp1, lp2, other] = await ethers.getSigners();
- });
-
- sharedBeforeEach('deploy vault and tokens', async () => {
- const vaultHelper = await Vault.create({ admin });
- vault = vaultHelper.instance;
-
- rewardTokens = await TokenList.create(['DAI'], { sorted: true });
- rewardToken = rewardTokens.DAI;
-
- merkleRedeem = await deploy('MerkleRedeem', {
- args: [vault.address, rewardToken.address],
- from: admin,
- });
- await rewardTokens.mint({ to: admin.address, amount: rewardTokenInitialBalance });
- await rewardTokens.approve({ to: merkleRedeem.address, from: [admin] });
- });
-
- it('stores an allocation', async () => {
- const claimBalance = bn('9876');
-
- const elements = [encodeElement(lp1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await merkleRedeem.connect(admin).seedAllocations(week1, root, claimBalance);
-
- const proof = merkleTree.getHexProof(elements[0]);
-
- const result = await merkleRedeem.verifyClaim(lp1.address, 1, claimBalance, proof);
- expect(result).to.equal(true);
- });
-
- it('emits RewardAdded when an allocation is stored', async () => {
- const claimBalance = bn('9876');
-
- const elements = [encodeElement(lp1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- const receipt = await (await merkleRedeem.connect(admin).seedAllocations(week1, root, claimBalance)).wait();
-
- expectEvent.inReceipt(receipt, 'RewardAdded', {
- token: rewardToken.address,
- amount: claimBalance,
- });
- });
-
- it('requisitions tokens when it stores a balance', async () => {
- const claimBalance = bn('9876');
-
- const elements = [encodeElement(lp1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await expectBalanceChange(
- () => merkleRedeem.connect(admin).seedAllocations(week1, root, claimBalance),
- rewardTokens,
- [
- { account: admin, changes: { DAI: claimBalance.mul(-1) } },
- { account: merkleRedeem, changes: { DAI: claimBalance } },
- ]
- );
- });
-
- it('reverts when unauthorized to seed allocation', async () => {
- const claimBalance = bn('9876');
-
- const elements = [encodeElement(lp1.address, claimBalance)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await expect(merkleRedeem.connect(other).seedAllocations(week1, root, claimBalance)).to.be.revertedWith(
- 'CALLER_IS_NOT_OWNER'
- );
- });
-
- it('stores multiple allocations', async () => {
- const claimBalance0 = bn('1000');
- const claimBalance1 = bn('2000');
-
- const elements = [encodeElement(lp1.address, claimBalance0), encodeElement(lp2.address, claimBalance1)];
- const merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await merkleRedeem.connect(admin).seedAllocations(1, root, bn('3000'));
-
- const proof0 = merkleTree.getHexProof(elements[0]);
- let result = await merkleRedeem.verifyClaim(lp1.address, 1, claimBalance0, proof0);
- expect(result).to.equal(true); //"account 0 should have an allocation";
-
- const proof1 = merkleTree.getHexProof(elements[1]);
- result = await merkleRedeem.verifyClaim(lp2.address, 1, claimBalance1, proof1);
- expect(result).to.equal(true); // "account 1 should have an allocation";
- });
-
- describe('with an allocation', () => {
- const claimableBalance = bn('1000');
- let elements: string[];
- let merkleTree: MerkleTree;
-
- sharedBeforeEach(async () => {
- elements = [encodeElement(lp1.address, claimableBalance)];
- merkleTree = new MerkleTree(elements);
- const root = merkleTree.getHexRoot();
-
- await merkleRedeem.connect(admin).seedAllocations(1, root, claimableBalance);
- });
-
- it('allows the user to claimWeek', async () => {
- const merkleProof: BytesLike[] = merkleTree.getHexProof(elements[0]);
-
- await expectBalanceChange(
- () => merkleRedeem.connect(lp1).claimWeek(lp1.address, 1, claimableBalance, merkleProof),
- rewardTokens,
- [{ account: lp1, changes: { DAI: claimableBalance } }]
- );
- });
-
- it('emits RewardPaid when an allocation is claimed', async () => {
- const merkleProof: BytesLike[] = merkleTree.getHexProof(elements[0]);
-
- const receipt = await (
- await merkleRedeem.connect(lp1).claimWeek(lp1.address, 1, claimableBalance, merkleProof)
- ).wait();
-
- expectEvent.inReceipt(receipt, 'RewardPaid', {
- user: lp1.address,
- rewardToken: rewardToken.address,
- amount: claimableBalance,
- });
- });
-
- it('marks claimed weeks as claimed', async () => {
- const merkleProof: BytesLike[] = merkleTree.getHexProof(elements[0]);
- await merkleRedeem.connect(lp1).claimWeek(lp1.address, 1, claimableBalance, merkleProof);
-
- const isClaimed = await merkleRedeem.claimed(1, lp1.address);
- expect(isClaimed).to.equal(true); // "claim should be marked as claimed";
- });
-
- it('reverts when a user attempts to claim for another user', async () => {
- const merkleProof = merkleTree.getHexProof(elements[0]);
-
- const errorMsg = 'user must claim own balance';
- await expect(
- merkleRedeem.connect(other).claimWeek(lp1.address, 1, claimableBalance, merkleProof)
- ).to.be.revertedWith(errorMsg);
- });
-
- it('reverts when the user attempts to claim the wrong balance', async () => {
- const incorrectClaimedBalance = bn('666');
- const merkleProof = merkleTree.getHexProof(elements[0]);
- const errorMsg = 'Incorrect merkle proof';
- await expect(
- merkleRedeem.connect(lp1).claimWeek(lp1.address, 1, incorrectClaimedBalance, merkleProof)
- ).to.be.revertedWith(errorMsg);
- });
-
- it('reverts when the user attempts to claim twice', async () => {
- const merkleProof = merkleTree.getHexProof(elements[0]);
-
- await merkleRedeem.connect(lp1).claimWeek(lp1.address, 1, claimableBalance, merkleProof);
-
- const errorMsg = 'cannot claim twice';
- await expect(
- merkleRedeem.connect(lp1).claimWeek(lp1.address, 1, claimableBalance, merkleProof)
- ).to.be.revertedWith(errorMsg);
- });
-
- it('reverts when an admin attempts to overwrite an allocationn', async () => {
- const elements2 = [encodeElement(lp1.address, claimableBalance), encodeElement(lp2.address, claimableBalance)];
- const merkleTree2 = new MerkleTree(elements2);
- const root2 = merkleTree2.getHexRoot();
-
- const errorMsg = 'cannot rewrite merkle root';
- await expect(merkleRedeem.connect(admin).seedAllocations(1, root2, claimableBalance.mul(2))).to.be.revertedWith(
- errorMsg
- );
- });
- });
-
- describe('with several allocations', () => {
- const claimBalance1 = bn('1000');
- const claimBalance2 = bn('1234');
-
- let elements1: string[];
- let merkleTree1: MerkleTree;
- let root1: string;
-
- let elements2: string[];
- let merkleTree2: MerkleTree;
- let root2: string;
-
- sharedBeforeEach(async () => {
- elements1 = [encodeElement(lp1.address, claimBalance1)];
- merkleTree1 = new MerkleTree(elements1);
- root1 = merkleTree1.getHexRoot();
-
- elements2 = [encodeElement(lp1.address, claimBalance2)];
- merkleTree2 = new MerkleTree(elements2);
- root2 = merkleTree2.getHexRoot();
-
- await merkleRedeem.connect(admin).seedAllocations(week1, root1, claimBalance1);
-
- await merkleRedeem.connect(admin).seedAllocations(bn(2), root2, claimBalance2);
- });
-
- it('allows the user to claim multiple weeks at once', async () => {
- const claimedBalance1 = bn('1000');
- const claimedBalance2 = bn('1234');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- const merkleProofs = [
- { week: week1, balance: claimedBalance1, merkleProof: proof1 },
- { week: bn(2), balance: claimedBalance2, merkleProof: proof2 },
- ];
-
- await expectBalanceChange(() => merkleRedeem.connect(lp1).claimWeeks(lp1.address, merkleProofs), rewardTokens, [
- { account: lp1, changes: { DAI: bn('2234') } },
- ]);
- });
-
- it('allows the user to claim multiple weeks at once to internal balance', async () => {
- const claimedBalance1 = bn('1000');
- const claimedBalance2 = bn('1234');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- const merkleProofs = [
- { week: week1, balance: claimedBalance1, merkleProof: proof1 },
- { week: bn(2), balance: claimedBalance2, merkleProof: proof2 },
- ];
-
- await expectBalanceChange(
- () => merkleRedeem.connect(lp1).claimWeeksToInternalBalance(lp1.address, merkleProofs),
- rewardTokens,
- [{ account: lp1, changes: { DAI: bn('2234') } }],
- vault
- );
- });
-
- it('reports weeks as unclaimed', async () => {
- const expectedResult = [false, false];
- const result = await merkleRedeem.claimStatus(lp1.address, 1, 2);
- expect(result).to.eql(expectedResult);
- });
-
- it('returns an array of merkle roots', async () => {
- const expectedResult = [root1, root2];
- const result = await merkleRedeem.merkleRoots(1, 2);
- expect(result).to.eql(expectedResult); // "claim status should be accurate"
- });
-
- interface Claim {
- week: BigNumber;
- balance: BigNumber;
- merkleProof: BytesLike[];
- }
-
- describe('with a callback', () => {
- let callbackContract: Contract;
- let claims: Claim[];
-
- sharedBeforeEach('set up mock callback', async () => {
- callbackContract = await deploy('MockRewardCallback');
-
- const proof1: BytesLike[] = merkleTree1.getHexProof(elements1[0]);
- const proof2: BytesLike[] = merkleTree2.getHexProof(elements2[0]);
-
- claims = [
- { week: bn(1), balance: claimBalance1, merkleProof: proof1 },
- { week: bn(2), balance: claimBalance2, merkleProof: proof2 },
- ];
- });
-
- it('allows a user to claim the reward to a callback contract', async () => {
- const expectedReward = claimBalance1.add(claimBalance2);
- const calldata = utils.defaultAbiCoder.encode([], []);
-
- await expectBalanceChange(
- () =>
- merkleRedeem.connect(lp1).claimWeeksWithCallback(lp1.address, callbackContract.address, calldata, claims),
- rewardTokens,
- [{ account: callbackContract.address, changes: { DAI: ['very-near', expectedReward] } }],
- vault
- );
- });
-
- it('calls the callback on the contract', async () => {
- const calldata = utils.defaultAbiCoder.encode([], []);
-
- const receipt = await (
- await merkleRedeem
- .connect(lp1)
- .claimWeeksWithCallback(lp1.address, callbackContract.address, calldata, claims)
- ).wait();
-
- expectEvent.inIndirectReceipt(receipt, callbackContract.interface, 'CallbackReceived', {});
- });
- });
-
- describe('When a user has claimed one of their allocations', async () => {
- sharedBeforeEach(async () => {
- const claimedBalance1 = bn('1000');
- const proof1 = merkleTree1.getHexProof(elements1[0]);
-
- const merkleProofs = [{ week: week1, balance: claimedBalance1, merkleProof: proof1 }];
-
- await merkleRedeem.connect(lp1).claimWeeks(lp1.address, merkleProofs);
- });
-
- it('reports one of the weeks as claimed', async () => {
- const expectedResult = [true, false];
- const result = await merkleRedeem.claimStatus(lp1.address, 1, 2);
- expect(result).to.eql(expectedResult);
- });
- });
- });
-});
diff --git a/pkg/distributors/tsconfig.json b/pkg/distributors/tsconfig.json
deleted file mode 100644
index b4e69ae1f9..0000000000
--- a/pkg/distributors/tsconfig.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "extends": "../../tsconfig.json",
- "compilerOptions": {
- "outDir": "dist"
- }
-}
diff --git a/pvt/benchmarks/merkleClaim.ts b/pvt/benchmarks/merkleClaim.ts
deleted file mode 100644
index 4795e9d8d3..0000000000
--- a/pvt/benchmarks/merkleClaim.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { BigNumber } from 'ethers';
-import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/dist/src/signer-with-address';
-
-import { setupEnvironment } from './misc';
-import { printGas } from '@balancer-labs/v2-helpers/src/numbers';
-import { BytesLike, solidityKeccak256 } from 'ethers/lib/utils';
-import { deploy } from '@balancer-labs/v2-helpers/src/contract';
-import TokenList from '@balancer-labs/v2-helpers/src/models/tokens/TokenList';
-import Vault from '@balancer-labs/v2-helpers/src/models/vault/Vault';
-
-interface Claim {
- distributionId: BigNumber;
- balance: BigNumber;
- distributor: string;
- tokenIndex: BigNumber;
- merkleProof: BytesLike[];
-}
-
-let vault: Vault;
-let tokens: TokenList;
-let trader: SignerWithAddress;
-
-async function main() {
- ({ vault, tokens, trader } = await setupEnvironment());
-
- for (let i = 1; i <= 5; i++) {
- console.log(`\n# Claiming ${i} distributions`);
-
- await claimDistributions(i, false);
- await claimDistributions(i, true);
- }
-}
-
-async function claimDistributions(numberOfDistributions: number, useInternalBalance: boolean) {
- console.log(`\n## ${useInternalBalance ? 'Using Internal Balance' : 'Sending and receiving tokens'}`);
-
- const merkleOrchard = await deploy('v2-distributors/MerkleOrchard', { args: [vault.address] });
-
- const token = tokens.first;
- const tokenAddresses = tokens.subset(1).addresses;
- const amount = BigNumber.from(100);
- const merkleLeaf = solidityKeccak256(['address', 'uint256'], [trader.address, amount]);
-
- const claims: Claim[] = Array.from({ length: numberOfDistributions }, (_, distribution) => ({
- distributionId: BigNumber.from(distribution),
- balance: amount,
- distributor: trader.address,
- tokenIndex: BigNumber.from(0),
- merkleProof: [],
- }));
-
- await token.approve(merkleOrchard.address, amount.mul(numberOfDistributions), { from: trader });
- for (let distribution = 0; distribution < numberOfDistributions; ++distribution) {
- await (
- await merkleOrchard.connect(trader).createDistribution(token.address, merkleLeaf, amount, distribution)
- ).wait();
- }
-
- let receipt;
- if (useInternalBalance) {
- receipt = await (
- await merkleOrchard.connect(trader).claimDistributionsToInternalBalance(trader.address, claims, tokenAddresses)
- ).wait();
- } else {
- receipt = await (
- await merkleOrchard.connect(trader).claimDistributions(trader.address, claims, tokenAddresses)
- ).wait();
- }
-
- console.log(
- `${numberOfDistributions} claims: ${printGas(receipt.gasUsed)} (${printGas(
- receipt.gasUsed / numberOfDistributions
- )} per claim)`
- );
-}
-
-main()
- .then(() => process.exit(0))
- .catch((error) => {
- console.error(error);
- process.exit(1);
- });
diff --git a/pvt/benchmarks/package.json b/pvt/benchmarks/package.json
index 0e4c70c972..223b16164f 100644
--- a/pvt/benchmarks/package.json
+++ b/pvt/benchmarks/package.json
@@ -9,9 +9,8 @@
"measure-single-pair": "hardhat run singlePair.ts",
"measure-multihop": "hardhat run multihop.ts",
"measure-join-exit": "hardhat run joinExit.ts",
- "measure-merkle-claim": "hardhat run merkleClaim.ts",
"measure-relayer": "hardhat run relayer.ts",
- "benchmark": "yarn measure-deployment && yarn measure-single-pair && yarn measure-multihop && yarn measure-join-exit && yarn measure-merkle-claim && yarn measure-relayer",
+ "benchmark": "yarn measure-deployment && yarn measure-single-pair && yarn measure-multihop && yarn measure-join-exit && yarn measure-relayer",
"lint": "yarn lint:typescript",
"lint:typescript": "eslint . --ext .ts --ignore-path ../../.eslintignore --max-warnings 0"
},
diff --git a/pvt/helpers/package.json b/pvt/helpers/package.json
index 272da726a7..1cee03e802 100644
--- a/pvt/helpers/package.json
+++ b/pvt/helpers/package.json
@@ -14,6 +14,7 @@
"@types/lodash": "^4.14.186",
"@types/node": "^14.14.31",
"decimal.js": "^10.4.2",
+ "ethereumjs-util": "^7.1.5",
"ethers": "^5.7.2",
"hardhat": "^2.12.2",
"lodash.frompairs": "^4.0.1",
diff --git a/pkg/distributors/lib/merkleTree.ts b/pvt/helpers/src/merkleTree.ts
similarity index 100%
rename from pkg/distributors/lib/merkleTree.ts
rename to pvt/helpers/src/merkleTree.ts
diff --git a/yarn.lock b/yarn.lock
index 94c63b7429..7811ab841f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -183,52 +183,11 @@ __metadata:
ts-node: ^10.9.1
typescript: ^4.0.2
peerDependencies:
- "@nomiclabs/hardhat-ethers": ^2.0.6
+ "@ethersproject/contracts": ^5.0.0
hardhat: ^2.8.3
languageName: unknown
linkType: soft
-"@balancer-labs/v2-distributors@workspace:pkg/distributors":
- version: 0.0.0-use.local
- resolution: "@balancer-labs/v2-distributors@workspace:pkg/distributors"
- dependencies:
- "@balancer-labs/balancer-js": "workspace:*"
- "@balancer-labs/v2-interfaces": "workspace:*"
- "@balancer-labs/v2-solidity-utils": "workspace:*"
- "@nomiclabs/hardhat-ethers": ^2.2.1
- "@nomiclabs/hardhat-waffle": ^2.0.3
- "@types/chai": ^4.3.3
- "@types/lodash": ^4.14.186
- "@types/mocha": ^10.0.0
- "@types/node": ^14.14.31
- "@typescript-eslint/eslint-plugin": ^5.41.0
- "@typescript-eslint/parser": ^5.41.0
- chai: ^4.3.6
- decimal.js: ^10.4.2
- eslint: ^8.26.0
- eslint-plugin-mocha-no-only: ^1.1.1
- eslint-plugin-prettier: ^4.2.1
- ethereum-waffle: ^3.4.4
- ethereumjs-util: ^7.1.5
- ethers: ^5.7.2
- hardhat: ^2.12.2
- hardhat-ignore-warnings: ^0.2.4
- lodash.frompairs: ^4.0.1
- lodash.pick: ^4.4.0
- lodash.range: ^3.2.0
- lodash.times: ^4.3.2
- lodash.zip: ^4.2.0
- mocha: ^10.1.0
- nodemon: ^2.0.20
- prettier: ^2.7.1
- prettier-plugin-solidity: v1.0.0-alpha.59
- solhint: ^3.2.0
- solhint-plugin-prettier: ^0.0.4
- ts-node: ^10.9.1
- typescript: ^4.0.2
- languageName: unknown
- linkType: soft
-
"@balancer-labs/v2-governance-scripts@workspace:pkg/governance-scripts":
version: 0.0.0-use.local
resolution: "@balancer-labs/v2-governance-scripts@workspace:pkg/governance-scripts"
@@ -278,6 +237,7 @@ __metadata:
decimal.js: ^10.4.2
eslint: ^8.26.0
eslint-plugin-prettier: ^4.2.1
+ ethereumjs-util: ^7.1.5
ethers: ^5.7.2
hardhat: ^2.12.2
lodash.frompairs: ^4.0.1