Skip to content

Commit

Permalink
finance can withdraw erc20s in offchain mode (#13058)
Browse files Browse the repository at this point in the history
  • Loading branch information
shileiwill authored May 3, 2024
1 parent 29b1636 commit a34a17a
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-birds-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

withdraw in offchain mode #bugfix
5 changes: 5 additions & 0 deletions contracts/.changeset/calm-maps-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

withdraw in offchain mode #bugfix
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ contract Withdraw is SetUp {
registry.withdrawLink(FINANCE_ADMIN, 1); // but using link withdraw functions succeeds
}

// default is ON_CHAIN mode
function test_WithdrawERC20Fees_RevertsWhen_LinkAvailableForPaymentIsNegative() public {
_transmit(usdUpkeepID18, registry); // adds USD token to finance withdrawable, and gives NOPs a LINK balance
require(registry.linkAvailableForPayment() < 0, "linkAvailableForPayment should be negative");
Expand All @@ -362,6 +363,27 @@ contract Withdraw is SetUp {
registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, 1); // now finance can withdraw
}

function test_WithdrawERC20Fees_InOffChainMode_Happy() public {
// deploy and configure a registry with OFF_CHAIN payout
(Registry registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN);

// register an upkeep and add funds
uint256 id = registry.registerUpkeep(address(TARGET1), 1000000, UPKEEP_ADMIN, 0, address(usdToken18), "", "", "");
_mintERC20_18Decimals(UPKEEP_ADMIN, 1e20);
vm.startPrank(UPKEEP_ADMIN);
usdToken18.approve(address(registry), 1e20);
registry.addFunds(id, 1e20);

// manually create a transmit so transmitters earn some rewards
_transmit(id, registry);
require(registry.linkAvailableForPayment() < 0, "linkAvailableForPayment should be negative");
vm.prank(FINANCE_ADMIN);
registry.withdrawERC20Fees(address(usdToken18), aMockAddress, 1); // finance can withdraw

// recipient should get the funds
assertEq(usdToken18.balanceOf(address(aMockAddress)), 1);
}

function testWithdrawERC20FeeSuccess() public {
// deposit excess USDToken to the registry (this goes to the "finance withdrawable" pool be default)
uint256 startReserveAmount = registry.getReserveAmount(address(usdToken18));
Expand Down Expand Up @@ -965,7 +987,8 @@ contract NOPsSettlement is SetUp {
registry.settleNOPsOffchain();
}

function testSettleNOPsOffchainSuccessTransmitterBalanceZeroed() public {
// 1. transmitter balance zeroed after settlement, 2. admin can withdraw ERC20, 3. switch to onchain mode, 4. link amount owed to NOPs stays the same
function testSettleNOPsOffchainSuccessWithERC20MultiSteps() public {
// deploy and configure a registry with OFF_CHAIN payout
(Registry registry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.OFF_CHAIN);

Expand Down Expand Up @@ -1007,6 +1030,42 @@ contract NOPsSettlement is SetUp {

// after the offchain settlement, the total reserve amount of LINK should be 0
assertEq(registry.getReserveAmount(address(linkToken)), 0);
// should have some ERC20s in registry after transmit
uint256 erc20ForPayment1 = registry.getAvailableERC20ForPayment(address(usdToken18));
require(erc20ForPayment1 > 0, "ERC20AvailableForPayment should be positive");

vm.startPrank(UPKEEP_ADMIN);
vm.roll(100 + block.number);
// manually create a transmit so transmitters earn some rewards
_transmit(id, registry);

uint256 erc20ForPayment2 = registry.getAvailableERC20ForPayment(address(usdToken18));
require(erc20ForPayment2 > erc20ForPayment1, "ERC20AvailableForPayment should be greater after another transmit");

// finance admin comes to withdraw all available ERC20s
vm.startPrank(FINANCE_ADMIN);
registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, erc20ForPayment2);

uint256 erc20ForPayment3 = registry.getAvailableERC20ForPayment(address(usdToken18));
require(erc20ForPayment3 == 0, "ERC20AvailableForPayment should be 0 now after withdrawal");

uint256 reservedLink = registry.getReserveAmount(address(linkToken));
require(reservedLink > 0, "Reserve amount of LINK should be positive since there was another transmit");

// owner comes to disable offchain mode
vm.startPrank(registry.owner());
registry.disableOffchainPayments();

// finance admin comes to withdraw all available ERC20s, should revert bc of insufficient link liquidity
vm.startPrank(FINANCE_ADMIN);
uint256 erc20ForPayment4 = registry.getAvailableERC20ForPayment(address(usdToken18));
vm.expectRevert(abi.encodeWithSelector(Registry.InsufficientLinkLiquidity.selector));
registry.withdrawERC20Fees(address(usdToken18), FINANCE_ADMIN, erc20ForPayment4);

// reserved link amount to NOPs should stay the same after switching to onchain mode
assertEq(registry.getReserveAmount(address(linkToken)), reservedLink);
// available ERC20 for payment should be 0 since finance admin withdrew all already
assertEq(erc20ForPayment4, 0);
}

function testSettleNOPsOffchainForDeactivatedTransmittersSuccess() public {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,14 @@ contract AutomationRegistryLogicB2_3 is AutomationRegistryBase2_3, Chainable {
* @param asset the asset to withdraw
* @param to the address to send the fees to
* @param amount the amount to withdraw
* @dev we prevent withdrawing non-LINK fees unless there is sufficient LINK liquidity
* @dev in ON_CHAIN mode, we prevent withdrawing non-LINK fees unless there is sufficient LINK liquidity
* to cover all outstanding debts on the registry
*/
function withdrawERC20Fees(IERC20 asset, address to, uint256 amount) external {
_onlyFinanceAdminAllowed();
if (to == ZERO_ADDRESS) revert InvalidRecipient();
if (address(asset) == address(i_link)) revert InvalidToken();
if (_linkAvailableForPayment() < 0) revert InsufficientLinkLiquidity();
if (_linkAvailableForPayment() < 0 && s_payoutMode == PayoutMode.ON_CHAIN) revert InsufficientLinkLiquidity();
uint256 available = asset.balanceOf(address(this)) - s_reserveAmounts[asset];
if (amount > available) revert InsufficientBalance(available, amount);

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ automation_registrar_wrapper2_3: ../../contracts/solc/v0.8.19/AutomationRegistra
automation_registry_logic_a_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_2/AutomationRegistryLogicA2_2.bin 2f267fb8467a15c587ce4586ac56069f7229344ad3936430d7c7624c0528a171
automation_registry_logic_a_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_3/AutomationRegistryLogicA2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicA2_3/AutomationRegistryLogicA2_3.bin e8ae5a25765092049f79eb8344db8e572bacb43c6c6e284b3d00f82667c248f7
automation_registry_logic_b_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_2/AutomationRegistryLogicB2_2.bin a6d33dfbbfb0ff253eb59a51f4f6d6d4c22ea5ec95aae52d25d49a312b37a22f
automation_registry_logic_b_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.bin 2d0f45d2087f6f3c8bfa0a16b26a1c8c1d5c64b89859478c609201535c96eeed
automation_registry_logic_b_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistryLogicB2_3/AutomationRegistryLogicB2_3.bin f2bb8dc7fdb8b48ba0a0501e3c195676e73d0e56d94c9061edf12830ebbce495
automation_registry_wrapper_2_2: ../../contracts/solc/v0.8.19/AutomationRegistry2_2/AutomationRegistry2_2.abi ../../contracts/solc/v0.8.19/AutomationRegistry2_2/AutomationRegistry2_2.bin de60f69878e9b32a291a001c91fc8636544c2cfbd9b507c8c1a4873b602bfb62
automation_registry_wrapper_2_3: ../../contracts/solc/v0.8.19/AutomationRegistry2_3/AutomationRegistry2_3.abi ../../contracts/solc/v0.8.19/AutomationRegistry2_3/AutomationRegistry2_3.bin fa9159b9dd36e37209d9805d0fde82f1cba7d0e2674ecc5a7595d48eb40203c4
automation_utils_2_1: ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.abi ../../contracts/solc/v0.8.16/AutomationUtils2_1/AutomationUtils2_1.bin 815b17b63f15d26a0274b962eefad98cdee4ec897ead58688bbb8e2470e585f5
Expand Down

0 comments on commit a34a17a

Please sign in to comment.