Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

finance can withdraw erc20s in offchain mode #13058

Merged
merged 1 commit into from
May 3, 2024
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
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
Loading