Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/generated/FlareFtsoWords.pointers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pragma solidity ^0.8.25;
// file needs the contract to exist so that it can be compiled.

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0x69ffc4160d6551b77f501f51e6e53d7825dcd7318519779f877261ebd37b7da0);
bytes32 constant BYTECODE_HASH = bytes32(0x068dba29da80c86736f8a1ea3cc5bae76f15405f03046cda8e9b99fe0a654acf);

/// @dev The hash of the meta that describes the contract.
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0x4ccb316fd35a2abbee0016f38926a4c95b6270b229c85a9acf8a3a52b36bfedc);
Expand Down
27 changes: 21 additions & 6 deletions src/lib/registry/LibFlareContractRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,38 @@ string constant FTSO_V2_LTS_NAME = "FtsoV2";
/// contract registry.
string constant FEE_CALCULATOR_NAME = "FeeCalculator";

/// @notice Thrown when a name lookup in the Flare contract registry returns
/// address(0). IFlareContractRegistry documents address(0) as the not-found
/// sentinel; propagating a zero-typed contract handle silently would cause
/// every subsequent call to revert with a confusing low-level error.
/// @param name The registry name that resolved to address(0).
error ContractNotRegistered(string name);

library LibFlareContractRegistry {
/// Sugar for getting the FTSO registry address from the Flare contract
/// registry.
/// registry. Reverts with ContractNotRegistered if the name is not found.
function getFtsoRegistry() internal view returns (IFtsoRegistry) {
return IFtsoRegistry(FLARE_CONTRACT_REGISTRY.getContractAddressByName(FTSO_REGISTRY_NAME));
address addr = FLARE_CONTRACT_REGISTRY.getContractAddressByName(FTSO_REGISTRY_NAME);
if (addr == address(0)) revert ContractNotRegistered(FTSO_REGISTRY_NAME);
return IFtsoRegistry(addr);
}

/// Sugar for getting the FTSO V2 LTS contract address from the Flare
/// contract registry.
/// contract registry. Reverts with ContractNotRegistered if the name is not
/// found.
//forge-lint: disable-next-line(mixed-case-function)
function getFtsoV2LTS() internal view returns (FtsoV2Interface) {
return FtsoV2Interface(FLARE_CONTRACT_REGISTRY.getContractAddressByName(FTSO_V2_LTS_NAME));
address addr = FLARE_CONTRACT_REGISTRY.getContractAddressByName(FTSO_V2_LTS_NAME);
if (addr == address(0)) revert ContractNotRegistered(FTSO_V2_LTS_NAME);
return FtsoV2Interface(addr);
}

/// Sugar for getting the FeeCalculator contract address from the Flare
/// contract registry.
/// contract registry. Reverts with ContractNotRegistered if the name is not
/// found.
function getFeeCalculator() internal view returns (IFeeCalculator) {
return IFeeCalculator(FLARE_CONTRACT_REGISTRY.getContractAddressByName(FEE_CALCULATOR_NAME));
address addr = FLARE_CONTRACT_REGISTRY.getContractAddressByName(FEE_CALCULATOR_NAME);
if (addr == address(0)) revert ContractNotRegistered(FEE_CALCULATOR_NAME);
return IFeeCalculator(addr);
}
}
45 changes: 44 additions & 1 deletion test/src/lib/registry/LibFlareContractRegistry.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ import {LibFork} from "test/fork/LibFork.sol";
import {
LibFlareContractRegistry,
IFtsoRegistry,
FLARE_CONTRACT_REGISTRY,
FTSO_REGISTRY_NAME,
FTSO_V2_LTS_NAME,
FEE_CALCULATOR_NAME
FEE_CALCULATOR_NAME,
ContractNotRegistered
} from "src/lib/registry/LibFlareContractRegistry.sol";
import {LibFlareContractRegistryExternal} from "test/src/lib/registry/LibFlareContractRegistryExternal.sol";
import {FtsoV2Interface} from "src/vendor/flare-smart-contracts-v2/userInterfaces/LTS/FtsoV2Interface.sol";
import {IFeeCalculator} from "src/vendor/flare-smart-contracts-v2/userInterfaces/IFeeCalculator.sol";

uint256 constant BLOCK_NUMBER = 31843105;

contract LibFlareContractRegistryTest is Test {
LibFlareContractRegistryExternal internal external_ = new LibFlareContractRegistryExternal();

constructor() {
vm.createSelectFork(LibFork.rpcUrlFlare(vm), BLOCK_NUMBER);
}
Expand All @@ -26,6 +31,44 @@ contract LibFlareContractRegistryTest is Test {
assertEq(address(ftsoRegistry), address(0x13DC2b5053857AE17a4f95aFF55530b267F3E040));
}

/// When the Flare contract registry resolves the FtsoRegistry name to
/// address(0) (the documented not-found sentinel), getFtsoRegistry MUST
/// revert with the specific ContractNotRegistered error rather than
/// returning a zero-typed handle. Removing the guard in the library makes
/// this test fail (the getter would return address(0) instead of
/// reverting).
function testGetFtsoRegistryZeroAddressReverts() external {
vm.mockCall(
address(FLARE_CONTRACT_REGISTRY),
abi.encodeWithSelector(FLARE_CONTRACT_REGISTRY.getContractAddressByName.selector, FTSO_REGISTRY_NAME),
abi.encode(address(0))
);
vm.expectRevert(abi.encodeWithSelector(ContractNotRegistered.selector, FTSO_REGISTRY_NAME));
external_.getFtsoRegistry();
}

/// As above, for the FtsoV2 LTS getter.
function testGetFtsoV2LTSZeroAddressReverts() external {
vm.mockCall(
address(FLARE_CONTRACT_REGISTRY),
abi.encodeWithSelector(FLARE_CONTRACT_REGISTRY.getContractAddressByName.selector, FTSO_V2_LTS_NAME),
abi.encode(address(0))
);
vm.expectRevert(abi.encodeWithSelector(ContractNotRegistered.selector, FTSO_V2_LTS_NAME));
external_.getFtsoV2LTS();
}

/// As above, for the FeeCalculator getter.
function testGetFeeCalculatorZeroAddressReverts() external {
vm.mockCall(
address(FLARE_CONTRACT_REGISTRY),
abi.encodeWithSelector(FLARE_CONTRACT_REGISTRY.getContractAddressByName.selector, FEE_CALCULATOR_NAME),
abi.encode(address(0))
);
vm.expectRevert(abi.encodeWithSelector(ContractNotRegistered.selector, FEE_CALCULATOR_NAME));
external_.getFeeCalculator();
}

function testGetFtsoV2LTS() external view {
FtsoV2Interface ftsoV2 = LibFlareContractRegistry.getFtsoV2LTS();
assertEq(address(ftsoV2), address(0xB18d3A5e5A85C65cE47f977D7F486B79F99D3d32));
Expand Down
23 changes: 23 additions & 0 deletions test/src/lib/registry/LibFlareContractRegistryExternal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity =0.8.25;

import {LibFlareContractRegistry, IFtsoRegistry} from "src/lib/registry/LibFlareContractRegistry.sol";

/// External wrapper around the internal library getters so that calling them via
/// `this.<fn>()` produces a dedicated external call frame. `vm.expectRevert`
/// asserts the *next call frame* reverts; an inlined internal library call has
/// no frame of its own, so the wrapper is required to observe the revert.
contract LibFlareContractRegistryExternal {
function getFtsoRegistry() external view returns (address) {
return address(LibFlareContractRegistry.getFtsoRegistry());
}

function getFtsoV2LTS() external view returns (address) {
return address(LibFlareContractRegistry.getFtsoV2LTS());
}

function getFeeCalculator() external view returns (address) {
return address(LibFlareContractRegistry.getFeeCalculator());
}
}
Loading