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

feat: universalresolver v3 #350

Open
wants to merge 24 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 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
61 changes: 20 additions & 41 deletions contracts/dnsregistrar/OffchainDNSResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,9 @@ import "../utils/BytesUtils.sol";

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {LowLevelCallUtils} from "../utils/LowLevelCallUtils.sol";
import {ERC3668Utils, OffchainLookup, OffchainLookupData} from "../utils/ERC3668Utils.sol";

error InvalidOperation();
error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);

interface IDNSGateway {
function resolve(
Expand Down Expand Up @@ -268,28 +262,14 @@ contract OffchainDNSResolver is IExtendedResolver, IERC165 {
revertWithDefaultOffchainLookup(name, innerdata);
}

bool result = LowLevelCallUtils.functionStaticCall(
address(target),
data
);
uint256 size = LowLevelCallUtils.returnDataSize();
if (result) {
bytes memory returnData = LowLevelCallUtils.readReturnData(0, size);
return abi.decode(returnData, (bytes));
}
// Failure
if (size >= 4) {
bytes memory errorId = LowLevelCallUtils.readReturnData(0, 4);
if (bytes4(errorId) == OffchainLookup.selector) {
// Offchain lookup. Decode the revert message and create our own that nests it.
bytes memory revertData = LowLevelCallUtils.readReturnData(
4,
size - 4
);
handleOffchainLookupError(revertData, target, name);
}
}
LowLevelCallUtils.propagateRevert();
(bool success, bytes4 errorId, bytes memory result) = ERC3668Utils
.callWithNormalisedResult(target, data);
if (success) return abi.decode(result, (bytes));

if (errorId == OffchainLookup.selector)
handleOffchainLookupError(result, target, name);

LowLevelCallUtils.propagateRevert(bytes.concat(errorId, result));
}

function revertWithDefaultOffchainLookup(
Expand All @@ -313,24 +293,23 @@ contract OffchainDNSResolver is IExtendedResolver, IERC165 {
address target,
bytes memory name
) internal view {
(
address sender,
string[] memory urls,
bytes memory callData,
bytes4 innerCallbackFunction,
bytes memory extraData
) = abi.decode(returnData, (address, string[], bytes, bytes4, bytes));

if (sender != target) {
OffchainLookupData memory caughtLookup = ERC3668Utils
.getOffchainLookupData(returnData);

if (caughtLookup.sender != target) {
revert InvalidOperation();
}

revert OffchainLookup(
address(this),
urls,
callData,
caughtLookup.urls,
caughtLookup.callData,
OffchainDNSResolver.resolveCallback.selector,
abi.encode(name, extraData, innerCallbackFunction)
abi.encode(
name,
caughtLookup.extraData,
caughtLookup.callbackFunction
)
);
}
}
79 changes: 79 additions & 0 deletions contracts/test/TestBytesArrayValidator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../universalResolver/BytesArrayValidator.sol";

contract TestBytesArrayValidator {
function testValidBytesArray() public pure {
// Create a valid bytes array
bytes[] memory validArray = new bytes[](5);
validArray[0] = "Hello";
validArray[1] = "World";
validArray[2] = "Two";
validArray[3] = "Three";
validArray[4] = "Four";
bytes memory encodedValidArray = abi.encode(validArray);

bool isValid = BytesArrayValidator.isValidBytesArray(encodedValidArray);
require(isValid, "Should be a valid bytes array");
}

function testInvalidBytesArray() public pure {
// Create an invalid bytes array (too short)
bytes memory invalidArray = new bytes(16);

bool isValid = BytesArrayValidator.isValidBytesArray(invalidArray);
require(!isValid, "Should be an invalid bytes array");
}

function testEmptyBytesArray() public pure {
// Create an empty bytes array
bytes[] memory emptyArray = new bytes[](0);
bytes memory encodedEmptyArray = abi.encode(emptyArray);

bool isValid = BytesArrayValidator.isValidBytesArray(encodedEmptyArray);
require(isValid, "Empty array should be valid");
}

function testEmptyItemInBytesArray() public pure {
// Create an empty bytes array
bytes[] memory emptyArray = new bytes[](1);
emptyArray[0] = "";
bytes memory encodedEmptyArray = abi.encode(emptyArray);

bool isValid = BytesArrayValidator.isValidBytesArray(encodedEmptyArray);
require(isValid, "Empty array should be valid");
}

function largeEmptyBytesArray() public pure {
bytes[] memory largeArray = new bytes[](1000);
bytes memory encodedLargeArray = abi.encode(largeArray);

bool isValid = BytesArrayValidator.isValidBytesArray(encodedLargeArray);
require(isValid, "Large array should be valid");
}

function testLargeBytesArray() public pure {
// Create a large bytes array
bytes[] memory largeArray = new bytes[](1000);
for (uint i = 0; i < 1000; i++) {
largeArray[i] = new bytes(100);
}
bytes memory encodedLargeArray = abi.encode(largeArray);

bool isValid = BytesArrayValidator.isValidBytesArray(encodedLargeArray);
require(isValid, "Large array should be valid");
}

function testInvalidOffsets() public pure {
// Create an invalid bytes array with incorrect offsets
bytes memory invalidOffsets = new bytes(128);
// Set an invalid offset
assembly {
mstore(add(invalidOffsets, 64), 0x20)
}

bool isValid = BytesArrayValidator.isValidBytesArray(invalidOffsets);
require(!isValid, "Array with invalid offsets should be invalid");
}
}
32 changes: 32 additions & 0 deletions contracts/test/TestUserCallbackFunctions.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../universalResolver/ERC3668Caller.sol";
import "hardhat/console.sol";

contract TestUserCallbackFunctions is ERC3668Caller {
function testCreateUserCallbackFunctions() public view {
bytes4 internalCallbackFunction = bytes4(0x11111111);
bytes4 calldataRewriteFunction = bytes4(0x22222222);
bytes4 failureCallbackFunction = bytes4(0x33333333);
bytes4 validateResponseFunction = bytes4(0x44444444);

uint256 gasBefore = gasleft();
uint256 callbackFunctions = createUserCallbackFunctions(
internalCallbackFunction,
calldataRewriteFunction,
failureCallbackFunction,
validateResponseFunction
);
uint256 gasAfter = gasleft();
console.log("gas", gasBefore - gasAfter);

console.logBytes32(bytes32(callbackFunctions));

require(
callbackFunctions ==
0x0000000000000000000000000000000044444444333333332222222211111111,
"Callback functions should be correct"
);
}
}
49 changes: 49 additions & 0 deletions contracts/test/mocks/DummyAddrOffchainResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../../../contracts/resolvers/profiles/IAddrResolver.sol";

error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);

contract DummyAddrOffchainResolver is IAddrResolver, ERC165 {
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
interfaceId == type(IAddrResolver).interfaceId ||
super.supportsInterface(interfaceId);
}

function addr(bytes32) external view returns (address payable) {
string[] memory urls = new string[](1);
urls[0] = "https://example.com/";

bytes memory data = abi.encode(address(this));

revert OffchainLookup(
address(this),
urls,
data,
this.addrCallback.selector,
data
);
}

function addrOnchain(bytes32) external view returns (address) {
return address(this);
}

function addrCallback(
bytes calldata response,
bytes calldata /* extraData */
) external pure returns (address) {
return abi.decode(response, (address));
}
}
52 changes: 52 additions & 0 deletions contracts/test/mocks/DummyAddressOffchainResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../../../contracts/resolvers/profiles/IAddressResolver.sol";

error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);

contract DummyAddressOffchainResolver is IAddressResolver, ERC165 {
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
interfaceId == type(IAddressResolver).interfaceId ||
super.supportsInterface(interfaceId);
}

function addr(
bytes32 node,
uint256 coinType
) external view returns (bytes memory) {
string[] memory urls = new string[](1);
urls[0] = "https://example.com/";

bytes memory data = abi.encodeWithSelector(
this.addr.selector,
node,
coinType
);

revert OffchainLookup(
address(this),
urls,
data,
this.addrCallback.selector,
data
);
}

function addrCallback(
bytes calldata response,
bytes calldata /* extraData */
) external pure returns (bytes memory) {
return abi.decode(response, (bytes));
}
}
10 changes: 10 additions & 0 deletions contracts/test/mocks/DummyENSIP10ResolverFinderImplementer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.13;

import {ENS} from "../../registry/ENS.sol";
import {ENSIP10ResolverFinder} from "../../universalResolver/ENSIP10ResolverFinder.sol";

contract DummyENSIP10ResolverFinderImplementer is ENSIP10ResolverFinder {
constructor(ENS ensRegistry) ENSIP10ResolverFinder(ensRegistry) {}
}
45 changes: 45 additions & 0 deletions contracts/test/mocks/DummyNameOffchainResolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "../../../contracts/resolvers/profiles/INameResolver.sol";

error OffchainLookup(
address sender,
string[] urls,
bytes callData,
bytes4 callbackFunction,
bytes extraData
);

contract DummyNameOffchainResolver is INameResolver, ERC165 {
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
interfaceId == type(INameResolver).interfaceId ||
super.supportsInterface(interfaceId);
}

function name(bytes32) external view returns (string memory) {
string[] memory urls = new string[](1);
urls[0] = "https://example.com/";

bytes memory data = abi.encode("name-offchain.eth");

revert OffchainLookup(
address(this),
urls,
data,
this.nameCallback.selector,
data
);
}

function nameCallback(
bytes calldata response,
bytes calldata /* extraData */
) external pure returns (string memory) {
return abi.decode(response, (string));
}
}
Loading