Skip to content

Commit

Permalink
Auto 23 more tests (#12661)
Browse files Browse the repository at this point in the history
* add link liquidity test for WithdrawERC20Fees in auto2.3

* add migration tests for auto 2.3

* update changesets
  • Loading branch information
RyanRHall authored Apr 2, 2024
1 parent c85435e commit 3b02047
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 170 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-beds-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

more auto 2.3 tests
5 changes: 5 additions & 0 deletions contracts/.changeset/quiet-cars-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@chainlink/contracts": patch
---

more auto 2.3 tests
168 changes: 167 additions & 1 deletion contracts/src/v0.8/automation/dev/test/AutomationRegistry2_3.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -251,14 +251,25 @@ contract Withdraw is SetUp {
registry.withdrawERC20Fees(address(usdToken), FINANCE_ADMIN, 1);
}

function test_WithdrawERC20Fees_RevertsWhenAttemptingToWithdrawLINK() public {
function test_WithdrawERC20Fees_RevertsWhen_AttemptingToWithdrawLINK() public {
_mintLink(address(registry), 1e10);
vm.startPrank(FINANCE_ADMIN);
vm.expectRevert(Registry.InvalidToken.selector);
registry.withdrawERC20Fees(address(linkToken), FINANCE_ADMIN, 1); // should revert
registry.withdrawLink(FINANCE_ADMIN, 1); // but using link withdraw functions succeeds
}

function test_WithdrawERC20Fees_RevertsWhen_LinkAvailableForPaymentIsNegative() public {
_transmit(usdUpkeepID, registry); // adds USD token to finance withdrawable, and gives NOPs a LINK balance
require(registry.linkAvailableForPayment() < 0, "linkAvailableForPayment should be negative");
vm.expectRevert(Registry.InsufficientLinkLiquidity.selector);
vm.prank(FINANCE_ADMIN);
registry.withdrawERC20Fees(address(usdToken), FINANCE_ADMIN, 1); // should revert
_mintLink(address(registry), uint256(registry.linkAvailableForPayment() * -10)); // top up LINK liquidity pool
vm.prank(FINANCE_ADMIN);
registry.withdrawERC20Fees(address(usdToken), FINANCE_ADMIN, 1); // now finance can withdraw
}

function testWithdrawERC20FeeSuccess() public {
// deposit excess USDToken to the registry (this goes to the "finance withdrawable" pool be default)
uint256 startReserveAmount = registry.getReserveAmount(address(usdToken));
Expand Down Expand Up @@ -1356,3 +1367,158 @@ contract Transmit is SetUp {
);
}
}

contract MigrateReceive is SetUp {
event UpkeepMigrated(uint256 indexed id, uint256 remainingBalance, address destination);
event UpkeepReceived(uint256 indexed id, uint256 startingBalance, address importedFrom);

Registry newRegistry;
uint256[] idsToMigrate;

function setUp() public override {
super.setUp();
(newRegistry, ) = deployAndConfigureRegistryAndRegistrar(AutoBase.PayoutMode.ON_CHAIN);
idsToMigrate.push(linkUpkeepID);
idsToMigrate.push(usdUpkeepID);
idsToMigrate.push(nativeUpkeepID);
registry.setPeerRegistryMigrationPermission(address(newRegistry), 1);
newRegistry.setPeerRegistryMigrationPermission(address(registry), 2);
}

function test_RevertsWhen_PermissionsNotSet() external {
// no permissions
registry.setPeerRegistryMigrationPermission(address(newRegistry), 0);
newRegistry.setPeerRegistryMigrationPermission(address(registry), 0);
vm.expectRevert(Registry.MigrationNotPermitted.selector);
vm.prank(UPKEEP_ADMIN);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));

// only outgoing permissions
registry.setPeerRegistryMigrationPermission(address(newRegistry), 1);
newRegistry.setPeerRegistryMigrationPermission(address(registry), 0);
vm.expectRevert(Registry.MigrationNotPermitted.selector);
vm.prank(UPKEEP_ADMIN);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));

// only incoming permissions
registry.setPeerRegistryMigrationPermission(address(newRegistry), 0);
newRegistry.setPeerRegistryMigrationPermission(address(registry), 2);
vm.expectRevert(Registry.MigrationNotPermitted.selector);
vm.prank(UPKEEP_ADMIN);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));

// permissions opposite direction
registry.setPeerRegistryMigrationPermission(address(newRegistry), 2);
newRegistry.setPeerRegistryMigrationPermission(address(registry), 1);
vm.expectRevert(Registry.MigrationNotPermitted.selector);
vm.prank(UPKEEP_ADMIN);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));
}

function test_RevertsWhen_ReceivingRegistryDoesNotSupportToken() external {
_removeBillingTokenConfig(newRegistry, address(weth));
vm.expectRevert(Registry.InvalidToken.selector);
vm.prank(UPKEEP_ADMIN);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));
idsToMigrate.pop(); // remove native upkeep id
vm.prank(UPKEEP_ADMIN);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry)); // should succeed now
}

function test_RevertsWhen_CalledByNonAdmin() external {
vm.expectRevert(Registry.OnlyCallableByAdmin.selector);
vm.prank(STRANGER);
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));
}

function test_Success() external {
vm.startPrank(UPKEEP_ADMIN);

// add some changes in upkeep data to the mix
registry.pauseUpkeep(usdUpkeepID);
registry.setUpkeepTriggerConfig(linkUpkeepID, randomBytes(100));
registry.setUpkeepCheckData(nativeUpkeepID, randomBytes(25));

// record previous state
uint256[] memory prevUpkeepBalances = new uint256[](3);
prevUpkeepBalances[0] = registry.getBalance(linkUpkeepID);
prevUpkeepBalances[1] = registry.getBalance(usdUpkeepID);
prevUpkeepBalances[2] = registry.getBalance(nativeUpkeepID);
uint256[] memory prevReserveBalances = new uint256[](3);
prevReserveBalances[0] = registry.getReserveAmount(address(linkToken));
prevReserveBalances[1] = registry.getReserveAmount(address(usdToken));
prevReserveBalances[2] = registry.getReserveAmount(address(weth));
uint256[] memory prevTokenBalances = new uint256[](3);
prevTokenBalances[0] = linkToken.balanceOf(address(registry));
prevTokenBalances[1] = usdToken.balanceOf(address(registry));
prevTokenBalances[2] = weth.balanceOf(address(registry));
bytes[] memory prevUpkeepData = new bytes[](3);
prevUpkeepData[0] = abi.encode(registry.getUpkeep(linkUpkeepID));
prevUpkeepData[1] = abi.encode(registry.getUpkeep(usdUpkeepID));
prevUpkeepData[2] = abi.encode(registry.getUpkeep(nativeUpkeepID));
bytes[] memory prevUpkeepTriggerData = new bytes[](3);
prevUpkeepTriggerData[0] = registry.getUpkeepTriggerConfig(linkUpkeepID);
prevUpkeepTriggerData[1] = registry.getUpkeepTriggerConfig(usdUpkeepID);
prevUpkeepTriggerData[2] = registry.getUpkeepTriggerConfig(nativeUpkeepID);

// event expectations
vm.expectEmit(address(registry));
emit UpkeepMigrated(linkUpkeepID, prevUpkeepBalances[0], address(newRegistry));
vm.expectEmit(address(registry));
emit UpkeepMigrated(usdUpkeepID, prevUpkeepBalances[1], address(newRegistry));
vm.expectEmit(address(registry));
emit UpkeepMigrated(nativeUpkeepID, prevUpkeepBalances[2], address(newRegistry));
vm.expectEmit(address(newRegistry));
emit UpkeepReceived(linkUpkeepID, prevUpkeepBalances[0], address(registry));
vm.expectEmit(address(newRegistry));
emit UpkeepReceived(usdUpkeepID, prevUpkeepBalances[1], address(registry));
vm.expectEmit(address(newRegistry));
emit UpkeepReceived(nativeUpkeepID, prevUpkeepBalances[2], address(registry));

// do the thing
registry.migrateUpkeeps(idsToMigrate, address(newRegistry));

// assert upkeep balances have been migrated
assertEq(registry.getBalance(linkUpkeepID), 0);
assertEq(registry.getBalance(usdUpkeepID), 0);
assertEq(registry.getBalance(nativeUpkeepID), 0);
assertEq(newRegistry.getBalance(linkUpkeepID), prevUpkeepBalances[0]);
assertEq(newRegistry.getBalance(usdUpkeepID), prevUpkeepBalances[1]);
assertEq(newRegistry.getBalance(nativeUpkeepID), prevUpkeepBalances[2]);

// assert reserve balances have been adjusted
assertEq(newRegistry.getReserveAmount(address(linkToken)), newRegistry.getBalance(linkUpkeepID));
assertEq(newRegistry.getReserveAmount(address(usdToken)), newRegistry.getBalance(usdUpkeepID));
assertEq(newRegistry.getReserveAmount(address(weth)), newRegistry.getBalance(nativeUpkeepID));
assertEq(
newRegistry.getReserveAmount(address(linkToken)),
prevReserveBalances[0] - registry.getReserveAmount(address(linkToken))
);
assertEq(
newRegistry.getReserveAmount(address(usdToken)),
prevReserveBalances[1] - registry.getReserveAmount(address(usdToken))
);
assertEq(
newRegistry.getReserveAmount(address(weth)),
prevReserveBalances[2] - registry.getReserveAmount(address(weth))
);

// assert token have been transfered
assertEq(linkToken.balanceOf(address(newRegistry)), newRegistry.getBalance(linkUpkeepID));
assertEq(usdToken.balanceOf(address(newRegistry)), newRegistry.getBalance(usdUpkeepID));
assertEq(weth.balanceOf(address(newRegistry)), newRegistry.getBalance(nativeUpkeepID));
assertEq(linkToken.balanceOf(address(registry)), prevTokenBalances[0] - linkToken.balanceOf(address(newRegistry)));
assertEq(usdToken.balanceOf(address(registry)), prevTokenBalances[1] - usdToken.balanceOf(address(newRegistry)));
assertEq(weth.balanceOf(address(registry)), prevTokenBalances[2] - weth.balanceOf(address(newRegistry)));

// assert upkeep data matches
assertEq(prevUpkeepData[0], abi.encode(newRegistry.getUpkeep(linkUpkeepID)));
assertEq(prevUpkeepData[1], abi.encode(newRegistry.getUpkeep(usdUpkeepID)));
assertEq(prevUpkeepData[2], abi.encode(newRegistry.getUpkeep(nativeUpkeepID)));
assertEq(prevUpkeepTriggerData[0], newRegistry.getUpkeepTriggerConfig(linkUpkeepID));
assertEq(prevUpkeepTriggerData[1], newRegistry.getUpkeepTriggerConfig(usdUpkeepID));
assertEq(prevUpkeepTriggerData[2], newRegistry.getUpkeepTriggerConfig(nativeUpkeepID));

vm.stopPrank();
}
}
39 changes: 38 additions & 1 deletion contracts/src/v0.8/automation/dev/test/BaseTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {LinkToken} from "../../../shared/token/ERC677/LinkToken.sol";
import {ERC20Mock} from "../../../vendor/openzeppelin-solidity/v4.8.3/contracts/mocks/ERC20Mock.sol";
import {MockV3Aggregator} from "../../../tests/MockV3Aggregator.sol";
import {AutomationForwarderLogic} from "../../AutomationForwarderLogic.sol";
import {UpkeepTranscoder5_0 as Transcoder} from "../v2_3/UpkeepTranscoder5_0.sol";
import {AutomationRegistry2_3} from "../v2_3/AutomationRegistry2_3.sol";
import {AutomationRegistryBase2_3 as AutoBase} from "../v2_3/AutomationRegistryBase2_3.sol";
import {AutomationRegistryLogicA2_3} from "../v2_3/AutomationRegistryLogicA2_3.sol";
Expand Down Expand Up @@ -47,6 +48,7 @@ contract BaseTest is Test {
MockV3Aggregator internal FAST_GAS_FEED;
MockUpkeep internal TARGET1;
MockUpkeep internal TARGET2;
Transcoder internal TRANSCODER;

// roles
address internal constant OWNER = address(uint160(uint256(keccak256("OWNER"))));
Expand Down Expand Up @@ -82,6 +84,8 @@ contract BaseTest is Test {
TARGET1 = new MockUpkeep();
TARGET2 = new MockUpkeep();

TRANSCODER = new Transcoder();

SIGNERS[0] = vm.addr(SIGNING_KEY0); //0xc110458BE52CaA6bB68E66969C3218A4D9Db0211
SIGNERS[1] = vm.addr(SIGNING_KEY1); //0xc110a19c08f1da7F5FfB281dc93630923F8E3719
SIGNERS[2] = vm.addr(SIGNING_KEY2); //0xc110fdF6e8fD679C7Cc11602d1cd829211A18e9b
Expand Down Expand Up @@ -233,7 +237,7 @@ contract BaseTest is Test {
fallbackGasPrice: 20_000_000_000,
fallbackLinkPrice: 2_000_000_000, // $20
fallbackNativePrice: 400_000_000_000, // $4,000
transcoder: 0xB1e66855FD67f6e85F0f0fA38cd6fBABdf00923c,
transcoder: address(TRANSCODER),
registrars: registrars,
upkeepPrivilegeManager: PRIVILEGE_MANAGER,
chainModule: address(new ChainModuleBase()),
Expand Down Expand Up @@ -265,6 +269,7 @@ contract BaseTest is Test {
(, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState();
AutomationRegistryBase2_3.OnchainConfig memory config = registry.getConfig();
address[] memory billingTokens = registry.getBillingTokens();

AutomationRegistryBase2_3.BillingConfig[]
memory billingTokenConfigs = new AutomationRegistryBase2_3.BillingConfig[](billingTokens.length);

Expand All @@ -291,6 +296,38 @@ contract BaseTest is Test {
);
}

/// @notice this function removes a billing token from the registry
function _removeBillingTokenConfig(Registry registry, address billingToken) internal {
(, , address[] memory signers, address[] memory transmitters, uint8 f) = registry.getState();
AutomationRegistryBase2_3.OnchainConfig memory config = registry.getConfig();
address[] memory billingTokens = registry.getBillingTokens();

address[] memory newBillingTokens = new address[](billingTokens.length - 1);
AutomationRegistryBase2_3.BillingConfig[]
memory billingTokenConfigs = new AutomationRegistryBase2_3.BillingConfig[](billingTokens.length - 1);

uint256 j = 0;
for (uint256 i = 0; i < billingTokens.length; i++) {
if (billingTokens[i] != billingToken) {
if (j == newBillingTokens.length) revert("could not find billing token provided on registry");
newBillingTokens[j] = billingTokens[i];
billingTokenConfigs[j] = registry.getBillingTokenConfig(billingTokens[i]);
j++;
}
}

registry.setConfigTypeSafe(
signers,
transmitters,
f,
config,
OFFCHAIN_CONFIG_VERSION,
"",
newBillingTokens,
billingTokenConfigs
);
}

function _transmit(uint256 id, Registry registry) internal {
uint256[] memory ids = new uint256[](1);
ids[0] = id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ contract AutomationRegistryLogicA2_3 is AutomationRegistryBase2_3, Chainable {
* @dev migration permissions must be set on *both* sending and receiving registries
* @dev only an upkeep admin can migrate their upkeeps
* @dev this function is most gas-efficient if upkeepIDs are sorted by billing token
* @dev TODO - this needs better multi-token testing
*/
function migrateUpkeeps(uint256[] calldata ids, address destination) external {
if (
Expand Down
Loading

0 comments on commit 3b02047

Please sign in to comment.