Skip to content

Commit

Permalink
Merge branch 'develop' into zksync-tx-known-error
Browse files Browse the repository at this point in the history
  • Loading branch information
poopoothegorilla authored Mar 26, 2024
2 parents d0db812 + 6fcc739 commit 3e1f506
Show file tree
Hide file tree
Showing 25 changed files with 422 additions and 243 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-berries-sin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

fix bug in auto2.3 withdrawERC20Fees
5 changes: 5 additions & 0 deletions .changeset/silent-pets-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

Exposing information about LogPoller finality violation via Healthy method. It's raised whenever LogPoller sees reorg deeper than the finality
5 changes: 5 additions & 0 deletions .changeset/wicked-gorillas-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": minor
---

VRFV2PlusWrapper config refactor
5 changes: 5 additions & 0 deletions contracts/.changeset/early-hairs-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

fix bug in auto2.3 withdrawERC20Fees
5 changes: 5 additions & 0 deletions contracts/.changeset/eight-peas-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": minor
---

VRFV2PlusWrapper config refactor

Large diffs are not rendered by default.

83 changes: 51 additions & 32 deletions contracts/src/v0.8/automation/dev/test/AutomationRegistry2_3.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ contract SetUp is BaseTest {
"",
""
);

vm.startPrank(OWNER);
registry.addFunds(linkUpkeepID, registry.getMinBalanceForUpkeep(linkUpkeepID));
registry.addFunds(usdUpkeepID, registry.getMinBalanceForUpkeep(usdUpkeepID));
registry.addFunds(nativeUpkeepID, registry.getMinBalanceForUpkeep(nativeUpkeepID));
vm.stopPrank();
}
}

Expand Down Expand Up @@ -140,32 +146,34 @@ contract AddFunds is SetUp {
}

function test_anyoneCanAddFunds() public {
assertEq(registry.getBalance(linkUpkeepID), 0);
uint256 startAmount = registry.getBalance(linkUpkeepID);
vm.prank(UPKEEP_ADMIN);
registry.addFunds(linkUpkeepID, 1);
assertEq(registry.getBalance(linkUpkeepID), 1);
assertEq(registry.getBalance(linkUpkeepID), startAmount + 1);
vm.prank(STRANGER);
registry.addFunds(linkUpkeepID, 1);
assertEq(registry.getBalance(linkUpkeepID), 2);
assertEq(registry.getBalance(linkUpkeepID), startAmount + 2);
}

function test_movesFundFromCorrectToken() public {
vm.startPrank(UPKEEP_ADMIN);

uint256 startBalanceLINK = linkToken.balanceOf(address(registry));
uint256 startBalanceUSDToken = usdToken.balanceOf(address(registry));
uint256 startLinkUpkeepBalance = registry.getBalance(linkUpkeepID);
uint256 startUSDUpkeepBalance = registry.getBalance(usdUpkeepID);

registry.addFunds(linkUpkeepID, 1);
assertEq(registry.getBalance(linkUpkeepID), 1);
assertEq(registry.getBalance(usdUpkeepID), 0);
assertEq(linkToken.balanceOf(address(registry)), startBalanceLINK + 1);
assertEq(usdToken.balanceOf(address(registry)), startBalanceUSDToken);
assertEq(registry.getBalance(linkUpkeepID), startBalanceLINK + 1);
assertEq(registry.getBalance(usdUpkeepID), startBalanceUSDToken);
assertEq(linkToken.balanceOf(address(registry)), startLinkUpkeepBalance + 1);
assertEq(usdToken.balanceOf(address(registry)), startUSDUpkeepBalance);

registry.addFunds(usdUpkeepID, 2);
assertEq(registry.getBalance(linkUpkeepID), 1);
assertEq(registry.getBalance(usdUpkeepID), 2);
assertEq(linkToken.balanceOf(address(registry)), startBalanceLINK + 1);
assertEq(usdToken.balanceOf(address(registry)), startBalanceUSDToken + 2);
assertEq(registry.getBalance(linkUpkeepID), startBalanceLINK + 1);
assertEq(registry.getBalance(usdUpkeepID), startBalanceUSDToken + 2);
assertEq(linkToken.balanceOf(address(registry)), startLinkUpkeepBalance + 1);
assertEq(usdToken.balanceOf(address(registry)), startUSDUpkeepBalance + 2);
}

function test_emitsAnEvent() public {
Expand All @@ -177,78 +185,89 @@ contract AddFunds is SetUp {
}

contract Withdraw is SetUp {
address internal aMockAddress = address(0x1111111111111111111111111111111111111113);
address internal aMockAddress = randomAddress();

function testLinkAvailableForPaymentReturnsLinkBalance() public {
uint256 startBalance = linkToken.balanceOf(address(registry));
int256 startLinkAvailable = registry.linkAvailableForPayment();

//simulate a deposit of link to the liquidity pool
_mintLink(address(registry), 1e10);

//check there's a balance
assertGt(linkToken.balanceOf(address(registry)), 0);
assertEq(linkToken.balanceOf(address(registry)), startBalance + 1e10);

//check the link available for payment is the link balance
assertEq(uint256(registry.linkAvailableForPayment()), linkToken.balanceOf(address(registry)));
//check the link available has increased by the same amount
assertEq(uint256(registry.linkAvailableForPayment()), uint256(startLinkAvailable) + 1e10);
}

function testWithdrawLinkFeesRevertsBecauseOnlyFinanceAdminAllowed() public {
function testWithdrawLinkRevertsBecauseOnlyFinanceAdminAllowed() public {
vm.expectRevert(abi.encodeWithSelector(Registry.OnlyFinanceAdmin.selector));
registry.withdrawLinkFees(aMockAddress, 1);
registry.withdrawLink(aMockAddress, 1);
}

function testWithdrawLinkFeesRevertsBecauseOfInsufficientBalance() public {
function testWithdrawLinkRevertsBecauseOfInsufficientBalance() public {
vm.startPrank(FINANCE_ADMIN);

// try to withdraw 1 link while there is 0 balance
vm.expectRevert(abi.encodeWithSelector(Registry.InsufficientBalance.selector, 0, 1));
registry.withdrawLinkFees(aMockAddress, 1);
registry.withdrawLink(aMockAddress, 1);

vm.stopPrank();
}

function testWithdrawLinkFeesRevertsBecauseOfInvalidRecipient() public {
function testWithdrawLinkRevertsBecauseOfInvalidRecipient() public {
vm.startPrank(FINANCE_ADMIN);

// try to withdraw 1 link while there is 0 balance
vm.expectRevert(abi.encodeWithSelector(Registry.InvalidRecipient.selector));
registry.withdrawLinkFees(ZERO_ADDRESS, 1);
registry.withdrawLink(ZERO_ADDRESS, 1);

vm.stopPrank();
}

function testWithdrawLinkFeeSuccess() public {
function testWithdrawLinkSuccess() public {
//simulate a deposit of link to the liquidity pool
_mintLink(address(registry), 1e10);

//check there's a balance
assertGt(linkToken.balanceOf(address(registry)), 0);
uint256 startBalance = linkToken.balanceOf(address(registry));

vm.startPrank(FINANCE_ADMIN);

// try to withdraw 1 link while there is a ton of link available
registry.withdrawLinkFees(aMockAddress, 1);
registry.withdrawLink(aMockAddress, 1);

vm.stopPrank();

assertEq(linkToken.balanceOf(address(aMockAddress)), 1);
assertEq(linkToken.balanceOf(address(registry)), 1e10 - 1);
assertEq(linkToken.balanceOf(address(registry)), startBalance - 1);
}

function test_WithdrawERC20Fees_RespectsReserveAmount() public {
assertEq(registry.getBalance(usdUpkeepID), registry.getReserveAmount(address(usdToken)));
vm.startPrank(FINANCE_ADMIN);
vm.expectRevert(abi.encodeWithSelector(Registry.InsufficientBalance.selector, 0, 1));
registry.withdrawERC20Fees(address(usdToken), FINANCE_ADMIN, 1);
}

function testWithdrawERC20FeeSuccess() public {
// simulate a deposit of ERC20 to the liquidity pool
// deposit excess USDToken to the registry (this goes to the "finance withdrawable" pool be default)
uint256 startReserveAmount = registry.getReserveAmount(address(usdToken));
uint256 startAmount = usdToken.balanceOf(address(registry));
_mintERC20(address(registry), 1e10);

// check there's a balance
assertGt(usdToken.balanceOf(address(registry)), 0);
// depositing shouldn't change reserve amount
assertEq(registry.getReserveAmount(address(usdToken)), startReserveAmount);

vm.startPrank(FINANCE_ADMIN);

// try to withdraw 1 link while there is a ton of link available
// try to withdraw 1 USDToken
registry.withdrawERC20Fees(address(usdToken), aMockAddress, 1);

vm.stopPrank();

assertEq(usdToken.balanceOf(address(aMockAddress)), 1);
assertEq(usdToken.balanceOf(address(registry)), 1e10 - 1);
assertEq(usdToken.balanceOf(address(registry)), startAmount + 1e10 - 1);
assertEq(registry.getReserveAmount(address(usdToken)), startReserveAmount);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
error IncorrectNumberOfSignatures();
error IncorrectNumberOfSigners();
error IndexOutOfRange();
error InsufficientBalance(int256 available, uint256 requested);
error InsufficientBalance(uint256 available, uint256 requested);
error InvalidBillingToken();
error InvalidDataLength();
error InvalidFeed();
Expand Down Expand Up @@ -483,7 +483,7 @@ abstract contract AutomationRegistryBase2_3 is ConfirmedOwner {
event Unpaused(address account);
// Event to emit when a billing configuration is set
event BillingConfigSet(IERC20 indexed token, BillingConfig config);
event FeesWithdrawn(address indexed recipient, address indexed assetAddress, uint256 amount);
event FeesWithdrawn(address indexed assetAddress, address indexed recipient, uint256 amount);

/**
* @param link address of the LINK Token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,27 +212,32 @@ contract AutomationRegistryLogicB2_3 is AutomationRegistryBase2_3, Chainable {
return int256(i_link.balanceOf(address(this))) - int256(s_reserveAmounts[IERC20(address(i_link))]);
}

function withdrawLinkFees(address to, uint256 amount) external {
function withdrawLink(address to, uint256 amount) external {
_onlyFinanceAdminAllowed();
if (to == ZERO_ADDRESS) revert InvalidRecipient();

int256 available = linkAvailableForPayment();
if (available < 0 || amount > uint256(available)) revert InsufficientBalance(available, amount);
if (available < 0) {
revert InsufficientBalance(0, amount);
} else if (amount > uint256(available)) {
revert InsufficientBalance(uint256(available), amount);
}

bool transferStatus = i_link.transfer(to, amount);
if (!transferStatus) {
revert TransferFailed();
}
emit FeesWithdrawn(to, address(i_link), amount);
emit FeesWithdrawn(address(i_link), to, amount);
}

function withdrawERC20Fees(address assetAddress, address to, uint256 amount) external {
function withdrawERC20Fees(IERC20 asset, address to, uint256 amount) external {
_onlyFinanceAdminAllowed();
if (to == ZERO_ADDRESS) revert InvalidRecipient();
uint256 available = asset.balanceOf(address(this)) - s_reserveAmounts[asset];
if (amount > available) revert InsufficientBalance(available, amount);

IERC20(assetAddress).safeTransfer(to, amount);

emit FeesWithdrawn(to, assetAddress, amount);
asset.safeTransfer(to, amount);
emit FeesWithdrawn(address(asset), to, amount);
}

/**
Expand Down
5 changes: 1 addition & 4 deletions contracts/src/v0.8/shared/token/ERC677/ERC677.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,16 @@ pragma solidity ^0.8.0;
import {IERC677} from "./IERC677.sol";
import {IERC677Receiver} from "../../interfaces/IERC677Receiver.sol";

import {Address} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol";
import {ERC20} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/ERC20.sol";

contract ERC677 is IERC677, ERC20 {
using Address for address;

constructor(string memory name, string memory symbol) ERC20(name, symbol) {}

/// @inheritdoc IERC677
function transferAndCall(address to, uint256 amount, bytes memory data) public returns (bool success) {
super.transfer(to, amount);
emit Transfer(msg.sender, to, amount, data);
if (to.isContract()) {
if (to.code.length > 0) {
IERC677Receiver(to).onTokenTransfer(msg.sender, amount, data);
}
return true;
Expand Down
51 changes: 27 additions & 24 deletions contracts/src/v0.8/vrf/dev/VRFV2PlusWrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,15 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
// charges for link payment.
uint32 private s_fulfillmentFlatFeeLinkDiscountPPM;

// s_wrapperNativePremiumPercentage is the premium ratio in percentage for native payment. For example, a value of 0
// indicates no premium. A value of 15 indicates a 15 percent premium.
uint8 private s_wrapperNativePremiumPercentage;
// s_coordinatorNativePremiumPercentage is the coordinator's premium ratio in percentage for native payment.
// For example, a value of 0 indicates no premium. A value of 15 indicates a 15 percent premium.
// Wrapper has no premium. This premium is for VRFCoordinator.
uint8 private s_coordinatorNativePremiumPercentage;

// s_wrapperLinkPremiumPercentage is the premium ratio in percentage for link payment. For example, a value of 0
// indicates no premium. A value of 15 indicates a 15 percent premium.
uint8 private s_wrapperLinkPremiumPercentage;
// s_coordinatorLinkPremiumPercentage is the premium ratio in percentage for link payment. For example, a
// value of 0 indicates no premium. A value of 15 indicates a 15 percent premium.
// Wrapper has no premium. This premium is for VRFCoordinator.
uint8 private s_coordinatorLinkPremiumPercentage;

// 10 bytes left
/* Storage Slot 6: END */
Expand Down Expand Up @@ -200,9 +202,9 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
* @param _coordinatorGasOverhead reflects the gas overhead of the coordinator's
* fulfillRandomWords function.
*
* @param _wrapperNativePremiumPercentage is the premium ratio in percentage for wrapper requests paid in native.
* @param _coordinatorNativePremiumPercentage is the coordinator's premium ratio in percentage for requests paid in native.
*
* @param _wrapperLinkPremiumPercentage is the premium ratio in percentage for wrapper requests paid in link.
* @param _coordinatorLinkPremiumPercentage is the coordinator's premium ratio in percentage for requests paid in link.
*
* @param _keyHash to use for requesting randomness.
* @param _maxNumWords is the max number of words that can be requested in a single wrapped VRF request
Expand All @@ -221,8 +223,8 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
function setConfig(
uint32 _wrapperGasOverhead,
uint32 _coordinatorGasOverhead,
uint8 _wrapperNativePremiumPercentage,
uint8 _wrapperLinkPremiumPercentage,
uint8 _coordinatorNativePremiumPercentage,
uint8 _coordinatorLinkPremiumPercentage,
bytes32 _keyHash,
uint8 _maxNumWords,
uint32 _stalenessSeconds,
Expand All @@ -233,17 +235,17 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
if (_fulfillmentFlatFeeLinkDiscountPPM > _fulfillmentFlatFeeNativePPM) {
revert LinkDiscountTooHigh(_fulfillmentFlatFeeLinkDiscountPPM, _fulfillmentFlatFeeNativePPM);
}
if (_wrapperNativePremiumPercentage > PREMIUM_PERCENTAGE_MAX) {
revert InvalidPremiumPercentage(_wrapperNativePremiumPercentage, PREMIUM_PERCENTAGE_MAX);
if (_coordinatorNativePremiumPercentage > PREMIUM_PERCENTAGE_MAX) {
revert InvalidPremiumPercentage(_coordinatorNativePremiumPercentage, PREMIUM_PERCENTAGE_MAX);
}
if (_wrapperLinkPremiumPercentage > PREMIUM_PERCENTAGE_MAX) {
revert InvalidPremiumPercentage(_wrapperLinkPremiumPercentage, PREMIUM_PERCENTAGE_MAX);
if (_coordinatorLinkPremiumPercentage > PREMIUM_PERCENTAGE_MAX) {
revert InvalidPremiumPercentage(_coordinatorLinkPremiumPercentage, PREMIUM_PERCENTAGE_MAX);
}

s_wrapperGasOverhead = _wrapperGasOverhead;
s_coordinatorGasOverhead = _coordinatorGasOverhead;
s_wrapperNativePremiumPercentage = _wrapperNativePremiumPercentage;
s_wrapperLinkPremiumPercentage = _wrapperLinkPremiumPercentage;
s_coordinatorNativePremiumPercentage = _coordinatorNativePremiumPercentage;
s_coordinatorLinkPremiumPercentage = _coordinatorLinkPremiumPercentage;
s_keyHash = _keyHash;
s_maxNumWords = _maxNumWords;
s_configured = true;
Expand All @@ -257,8 +259,8 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
emit ConfigSet(
_wrapperGasOverhead,
_coordinatorGasOverhead,
_wrapperNativePremiumPercentage,
_wrapperLinkPremiumPercentage,
_coordinatorNativePremiumPercentage,
_coordinatorLinkPremiumPercentage,
_keyHash,
_maxNumWords,
_stalenessSeconds,
Expand Down Expand Up @@ -324,8 +326,8 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume
s_fulfillmentFlatFeeLinkDiscountPPM,
s_wrapperGasOverhead,
s_coordinatorGasOverhead,
s_wrapperNativePremiumPercentage,
s_wrapperLinkPremiumPercentage,
s_coordinatorNativePremiumPercentage,
s_coordinatorLinkPremiumPercentage,
s_keyHash,
s_maxNumWords
);
Expand Down Expand Up @@ -390,8 +392,8 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume

// coordinatorCostWithPremiumAndFlatFeeWei is the coordinator cost with the percentage premium and flat fee applied
// coordinator cost * premium multiplier + flat fee
uint256 coordinatorCostWithPremiumAndFlatFeeWei = ((coordinatorCostWei * (s_wrapperNativePremiumPercentage + 100)) /
100) + (1e12 * uint256(s_fulfillmentFlatFeeNativePPM));
uint256 coordinatorCostWithPremiumAndFlatFeeWei = ((coordinatorCostWei *
(s_coordinatorNativePremiumPercentage + 100)) / 100) + (1e12 * uint256(s_fulfillmentFlatFeeNativePPM));

return wrapperCostWei + coordinatorCostWithPremiumAndFlatFeeWei;
}
Expand All @@ -413,8 +415,9 @@ contract VRFV2PlusWrapper is ConfirmedOwner, TypeAndVersionInterface, VRFConsume

// coordinatorCostWithPremiumAndFlatFeeWei is the coordinator cost with the percentage premium and flat fee applied
// coordinator cost * premium multiplier + flat fee
uint256 coordinatorCostWithPremiumAndFlatFeeWei = ((coordinatorCostWei * (s_wrapperLinkPremiumPercentage + 100)) /
100) + (1e12 * uint256(s_fulfillmentFlatFeeNativePPM - s_fulfillmentFlatFeeLinkDiscountPPM));
uint256 coordinatorCostWithPremiumAndFlatFeeWei = ((coordinatorCostWei *
(s_coordinatorLinkPremiumPercentage + 100)) / 100) +
(1e12 * uint256(s_fulfillmentFlatFeeNativePPM - s_fulfillmentFlatFeeLinkDiscountPPM));

// requestPrice is denominated in juels (link)
// (1e18 juels/link) * wei / (wei/link) = juels
Expand Down
Loading

0 comments on commit 3e1f506

Please sign in to comment.