Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions contracts/merkle-distributor/AcrossMerkleDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,31 @@
pragma solidity ^0.8.0;

import "@uma/core/contracts/merkle-distributor/implementation/MerkleDistributor.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
* @title Extended MerkleDistributor contract.
* @notice Adds additional constraints governing who can claim leaves from merkle windows.
*/
contract AcrossMerkleDistributor is MerkleDistributor {
// Addresses that can claim on user's behalf. Useful to get around the requirement that claim recipient
// must also be claimer.
using SafeERC20 for IERC20;

// Addresses that can claim on user's behalf.
mapping(address => bool) public whitelistedClaimers;

/****************************************
* EVENTS
****************************************/
event WhitelistedClaimer(address indexed claimer, bool indexed whitelist);
event ClaimFor(
address indexed caller,
uint256 windowIndex,
address indexed account,
uint256 accountIndex,
uint256 amount,
address indexed rewardToken
);

/****************************
* ADMIN FUNCTIONS
Expand All @@ -39,26 +50,47 @@ contract AcrossMerkleDistributor is MerkleDistributor {
/**
* @notice Batch claims to reduce gas versus individual submitting all claims. Method will fail
* if any individual claims within the batch would fail.
* @dev All claim recipients must be equal to msg.sender or claimer must be whitelisted.
* @dev All claim recipients must be equal to msg.sender.
* @param claims array of claims to claim.
*/
function claimMulti(Claim[] memory claims) public override {
if (!whitelistedClaimers[msg.sender]) {
uint256 claimCount = claims.length;
for (uint256 i = 0; i < claimCount; i++) {
require(claims[i].account == msg.sender, "invalid claimer");
}
uint256 claimCount = claims.length;
for (uint256 i = 0; i < claimCount; i++) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undoes changes in https://github.com/across-protocol/contracts-v2/pull/195/files that allow a whitelisted claimer to claim on behalf of claimer

require(claims[i].account == msg.sender, "invalid claimer");
}
super.claimMulti(claims);
}

/**
* @notice Claim amount of reward tokens for account, as described by Claim input object.
* @dev Claim recipient must be equal to msg.sender or caller must be whitelisted.
* @dev Claim recipient must be equal to msg.sender.
* @param _claim claim object describing amount, accountIndex, account, window index, and merkle proof.
*/
function claim(Claim memory _claim) public override {
require(whitelistedClaimers[msg.sender] || _claim.account == msg.sender, "invalid claimer");
require(_claim.account == msg.sender, "invalid claimer");
super.claim(_claim);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be done in a separate PR, but it's a little confusing that whitelisted claimers can claim directly to their own address and call claimMulti to send tokens to the user.

If we end up going in this direction, do you think we should drop the overridden claimMulti and claim methods, just to reduce surface area and complexity?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need to require that the claim.account is the caller so users can't reset each other's referral multipliers but i can remove the whitelist requirement. Thats more so to make testing easier but there's an easier way to do this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented the change where the whitelist only applies for claimFor: e21526a

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, that makes sense!

/**
* @notice Executes merkle leaf claim on behaf of user. This can only be called by a trusted
* claimer address. This function is designed to be called atomically with other transactions
* that ultimately return the claimed amount to the rightful recipient. For example,
* AcceleratingDistributor could call this function and then stake atomically on behalf of the user.
* @dev Caller must be in whitelistedClaimers struct set to "true".
* @param _claim leaf to claim.
*/

function claimFor(Claim memory _claim) public {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New claimFor method that whitelisted claimer can call

require(whitelistedClaimers[msg.sender], "unwhitelisted claimer");
_verifyAndMarkClaimed(_claim);
merkleWindows[_claim.windowIndex].rewardToken.safeTransfer(msg.sender, _claim.amount);
emit ClaimFor(
msg.sender,
_claim.windowIndex,
_claim.account,
_claim.accountIndex,
_claim.amount,
address(merkleWindows[_claim.windowIndex].rewardToken)
);
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@across-protocol/contracts-v2",
"version": "1.0.6",
"version": "1.0.7",
"author": "UMA Team",
"license": "AGPL-3.0",
"repository": {
Expand Down Expand Up @@ -37,7 +37,7 @@
"@openzeppelin/contracts": "^4.7.3",
"@uma/common": "^2.28.0",
"@uma/contracts-node": "^0.3.18",
"@uma/core": "^2.40.0",
"@uma/core": "^2.41.0",
"@uma/merkle-distributor": "^1.3.38",
"arb-bridge-eth": "^0.7.4",
"arb-bridge-peripherals": "^1.0.5"
Expand Down
Loading