Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0xA155c859Fe3aA782FbADc911947cEfcAC6C35D05
Balance 0 ETH
Nonce 1
Code Size 4608 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

4608 bytes
0x608060405234801561001057600080fd5b50600436106100b45760003560e01c80638da5cb5b116100715780638da5cb5b146101785780639061b923146101a3578063b4a85801146101b6578063da06e07f146101c9578063eb12d61e146101de578063f2fde38b146101f157600080fd5b806301ffc9a7146100b95780630e316ab7146100e15780636cc895a9146100f6578063736c0d5b14610109578063796676be1461012c5780637df73e271461014c575b600080fd5b6100cc6100c7366004610af0565b610204565b60405190151581526020015b60405180910390f35b6100f46100ef366004610b21565b61023b565b005b6100f4610104366004610be9565b61030e565b6100cc610117366004610b21565b60016020526000908152604090205460ff1681565b61013f61013a366004610cc1565b6103cf565b6040516100d89190610d20565b6100cc61015a366004610b21565b6001600160a01b031660009081526001602052604090205460ff1690565b60025461018b906001600160a01b031681565b6040516001600160a01b0390911681526020016100d8565b61013f6101b1366004610d7c565b61047b565b61013f6101c4366004610d7c565b6104d1565b6101d161065c565b6040516100d89190610de8565b6100f46101ec366004610b21565b610735565b6100f46101ff366004610b21565b610852565b6000639061b92360e01b6001600160e01b03198316148061023557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546001600160a01b0316331461026e5760405162461bcd60e51b815260040161026590610e4a565b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff166102c55760405162461bcd60e51b815260206004820152600c60248201526b2737ba10309039b4b3b732b960a11b6044820152606401610265565b6001600160a01b038116600081815260016020526040808220805460ff19169055517f3525e22824a8a7df2c9a6029941c824cf95b6447f1e13d5128fd3826d35afe8b9190a250565b6002546001600160a01b031633146103385760405162461bcd60e51b815260040161026590610e4a565b60008151116103815760405162461bcd60e51b81526020600482015260156024820152741399595908185d081b19585cdd081bdb9948155493605a1b6044820152606401610265565b8051610394906000906020840190610a2b565b507f8420b9370c99306e78c47378a2edbba7a1b63908514191a9db75d654e0b747a6816040516103c49190610de8565b60405180910390a150565b600081815481106103df57600080fd5b9060005260206000200160009150905080546103fa90610e6e565b80601f016020809104026020016040519081016040528092919081815260200182805461042690610e6e565b80156104735780601f1061044857610100808354040283529160200191610473565b820191906000526020600020905b81548152906001019060200180831161045657829003601f168201915b505050505081565b60606000858585856040516020016104969493929190610ea8565b60408051601f1981840301815290829052630556f18360e41b82529150610265903090600090849063b4a8580160e01b908290600401610eca565b6060600080806104e387890189611007565b925092509250428267ffffffffffffffff1610156105365760405162461bcd60e51b815260206004820152601060248201526f14995cdc1bdb9cd948195e1c1a5c995960821b6044820152606401610265565b60008686604051610548929190611084565b604080519182900382208651602080890191909120308286015267ffffffffffffffff8816858501526060850183905260808086018290528451808703909101815260a0860185528051908301207f19457468657265756d205369676e6564204d6573736167653a0a33320000000060c087015260dc8087018290528551808803909101815260fc9096019094528451949091019390932090935060006105ef82876108e6565b6001600160a01b03811660009081526001602052604090205490915060ff1661064b5760405162461bcd60e51b815260206004820152600e60248201526d24b73b30b634b21039b4b3b732b960911b6044820152606401610265565b50959b9a5050505050505050505050565b60606000805480602002602001604051908101604052809291908181526020016000905b8282101561072c57838290600052602060002001805461069f90610e6e565b80601f01602080910402602001604051908101604052809291908181526020018280546106cb90610e6e565b80156107185780601f106106ed57610100808354040283529160200191610718565b820191906000526020600020905b8154815290600101906020018083116106fb57829003601f168201915b505050505081526020019060010190610680565b50505050905090565b6002546001600160a01b0316331461075f5760405162461bcd60e51b815260040161026590610e4a565b6001600160a01b0381166107a75760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610265565b6001600160a01b03811660009081526001602052604090205460ff16156108035760405162461bcd60e51b815260206004820152601060248201526f20b63932b0b23c90309039b4b3b732b960811b6044820152606401610265565b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f47d1c22a25bb3a5d4e481b9b1e6944c2eade3181a0a20b495ed61d35b5323f249190a250565b6002546001600160a01b0316331461087c5760405162461bcd60e51b815260040161026590610e4a565b6001600160a01b0381166108c45760405162461bcd60e51b815260206004820152600f60248201526e496e76616c6964206164647265737360881b6044820152606401610265565b600280546001600160a01b0319166001600160a01b0392909216919091179055565b600081516041146109395760405162461bcd60e51b815260206004820152601860248201527f496e76616c6964207369676e6174757265206c656e67746800000000000000006044820152606401610265565b60208201516040830151606084015160001a601b8110156109625761095f601b82611094565b90505b8060ff16601b148061097757508060ff16601c145b6109c35760405162461bcd60e51b815260206004820152601960248201527f496e76616c6964207369676e617475726520762076616c7565000000000000006044820152606401610265565b60408051600081526020810180835288905260ff831691810191909152606081018490526080810183905260019060a0016020604051602081039080840390855afa158015610a16573d6000803e3d6000fd5b5050604051601f190151979650505050505050565b828054828255906000526020600020908101928215610a71579160200282015b82811115610a715782518290610a61908261110a565b5091602001919060010190610a4b565b50610a7d929150610a81565b5090565b80821115610a7d576000610a958282610a9e565b50600101610a81565b508054610aaa90610e6e565b6000825580601f10610aba575050565b601f016020900490600052602060002090810190610ad89190610adb565b50565b5b80821115610a7d5760008155600101610adc565b600060208284031215610b0257600080fd5b81356001600160e01b031981168114610b1a57600080fd5b9392505050565b600060208284031215610b3357600080fd5b81356001600160a01b0381168114610b1a57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f1916810167ffffffffffffffff81118282101715610b8957610b89610b4a565b604052919050565b600067ffffffffffffffff831115610bab57610bab610b4a565b610bbe601f8401601f1916602001610b60565b9050828152838383011115610bd257600080fd5b828260208301376000602084830101529392505050565b60006020808385031215610bfc57600080fd5b823567ffffffffffffffff80821115610c1457600080fd5b818501915085601f830112610c2857600080fd5b813581811115610c3a57610c3a610b4a565b8060051b610c49858201610b60565b9182528381018501918581019089841115610c6357600080fd5b86860192505b83831015610cb457823585811115610c815760008081fd5b8601603f81018b13610c935760008081fd5b610ca48b8983013560408401610b91565b8352509186019190860190610c69565b9998505050505050505050565b600060208284031215610cd357600080fd5b5035919050565b6000815180845260005b81811015610d0057602081850181015186830182015201610ce4565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610b1a6020830184610cda565b60008083601f840112610d4557600080fd5b50813567ffffffffffffffff811115610d5d57600080fd5b602083019150836020828501011115610d7557600080fd5b9250929050565b60008060008060408587031215610d9257600080fd5b843567ffffffffffffffff80821115610daa57600080fd5b610db688838901610d33565b90965094506020870135915080821115610dcf57600080fd5b50610ddc87828801610d33565b95989497509550505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015610e3d57603f19888603018452610e2b858351610cda565b94509285019290850190600101610e0f565b5092979650505050505050565b6020808252600a908201526927b7363c9037bbb732b960b11b604082015260600190565b600181811c90821680610e8257607f821691505b602082108103610ea257634e487b7160e01b600052602260045260246000fd5b50919050565b8385823760008482016000815283858237600093019283525090949350505050565b600060a0820160018060a01b0388168352602060a08185015281885480845260c0860191506005935060c081851b87010160008b8152848120815b84811015610f985760bf198a8503018652828254610f2281610e6e565b80875260018281168015610f3d5760018114610f5657610f81565b60ff198416898d01528215158d1b89018c019450610f81565b8688528b8820885b84811015610f795781548b82018f0152908301908d01610f5e565b8a018d019550505b50988a019892965050509190910190600101610f05565b5050508681036040880152610fad818b610cda565b945050505050610fc960608401866001600160e01b0319169052565b8281036080840152610fdb8185610cda565b98975050505050505050565b600082601f830112610ff857600080fd5b610b1a83833560208501610b91565b60008060006060848603121561101c57600080fd5b833567ffffffffffffffff8082111561103457600080fd5b61104087838801610fe7565b945060208601359150808216821461105757600080fd5b9092506040850135908082111561106d57600080fd5b5061107a86828701610fe7565b9150509250925092565b8183823760009101908152919050565b60ff818116838216019081111561023557634e487b7160e01b600052601160045260246000fd5b601f82111561110557600081815260208120601f850160051c810160208610156110e25750805b601f850160051c820191505b81811015611101578281556001016110ee565b5050505b505050565b815167ffffffffffffffff81111561112457611124610b4a565b611138816111328454610e6e565b846110bb565b602080601f83116001811461116d57600084156111555750858301515b600019600386901b1c1916600185901b178555611101565b600085815260208120601f198616915b8281101561119c5788860151825594840194600190910190840161117d565b50858210156111ba5787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220b848651ed822819ddc3c7dec022edb63a724454c75997c03e70189368f362b5c64736f6c63430008110033

Verified Source Code Full Match

Compiler: v0.8.17+commit.8df45f5f EVM: london Optimization: Yes (200 runs)
OffchainResolver.sol 267 lines
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title OffchainResolver
 * @notice ENS resolver with CCIP-Read offchain resolution support
 * @dev Implements EIP-3668 (CCIP-Read) and ENSIP-10 (Wildcard Resolution)
 *
 * This resolver delegates resolution to an offchain gateway server,
 * which returns cryptographically signed responses that are verified onchain.
 *
 * Features:
 * - Offchain resolution via CCIP-Read
 * - Multiple authorized signers
 * - Wildcard subdomain support
 * - Multi-coin address resolution
 * - Text records, contenthash, and more
 *
 * Gateway Response Format:
 * {
 *   "data": "0x...",     // ABI-encoded response
 *   "expires": 123456789, // Unix timestamp
 *   "sig": "0x..."       // EIP-191 signature
 * }
 */

/**
 * @dev Interface for ENS registry to resolve names
 */
interface IExtendedResolver {
    function resolve(bytes calldata name, bytes calldata data)
        external
        view
        returns (bytes memory);
}

contract OffchainResolver is IExtendedResolver {
    error OffchainLookup(
        address sender,
        string[] urls,
        bytes callData,
        bytes4 callbackFunction,
        bytes extraData
    );

    event SignerAdded(address indexed signer);
    event SignerRemoved(address indexed signer);
    event UrlUpdated(string[] urls);

    string[] public urls;
    mapping(address => bool) public signers;
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner");
        _;
    }

    /**
     * @notice Create a new OffchainResolver
     * @param _url Gateway URL template (use {sender} and {data} placeholders)
     * @param _signers Array of authorized signer addresses
     *
     * Example URL: "https://gateway.example.com/{sender}/{data}.json"
     */
    constructor(string memory _url, address[] memory _signers) {
        owner = msg.sender;
        urls = new string[](1);
        urls[0] = _url;

        for (uint256 i = 0; i < _signers.length; i++) {
            signers[_signers[i]] = true;
            emit SignerAdded(_signers[i]);
        }

        emit UrlUpdated(urls);
    }

    /**
     * @notice Main resolution function implementing ENSIP-10 wildcard resolution
     * @param name DNS-encoded name (e.g., "\x07vitalik\x03eth\x00")
     * @param data ABI-encoded function call (e.g., addr(bytes32))
     * @return Resolved data from gateway
     *
     * This function always reverts with OffchainLookup, triggering CCIP-Read.
     * The client then queries the gateway and calls the callback function.
     */
    function resolve(bytes calldata name, bytes calldata data)
        external
        view
        returns (bytes memory)
    {
        // Construct the request for the gateway
        bytes memory callData = abi.encodePacked(name, data);

        // Revert with OffchainLookup to trigger CCIP-Read
        revert OffchainLookup(
            address(this),           // sender: this contract
            urls,                    // urls: gateway endpoints
            callData,                // callData: what to send to gateway
            this.resolveCallback.selector, // callback function
            callData                 // extraData: passed to callback
        );
    }

    /**
     * @notice Callback function called after gateway returns data
     * @param response Gateway response (ABI-encoded: bytes data, uint64 expires, bytes sig)
     * @param extraData Original request data (for verification)
     * @return Resolved data after signature verification
     *
     * Response format from gateway:
     * abi.encode(responseData, expirationTimestamp, signature)
     *
     * The signature is over: keccak256(abi.encodePacked(
     *   target (this contract),
     *   expires (uint64),
     *   request (original callData),
     *   result (responseData)
     * ))
     */
    function resolveCallback(bytes calldata response, bytes calldata extraData)
        external
        view
        returns (bytes memory)
    {
        // Decode gateway response
        (bytes memory responseData, uint64 expires, bytes memory sig) = abi.decode(
            response,
            (bytes, uint64, bytes)
        );

        // Check expiration
        require(expires >= block.timestamp, "Response expired");

        // Verify signature
        // Hash the request and result first
        bytes32 requestHash = keccak256(extraData);
        bytes32 resultHash = keccak256(responseData);

        // Create message hash using abi.encode (not encodePacked)
        bytes32 messageHash = keccak256(
            abi.encode(
                address(this),  // target
                expires,        // expires
                requestHash,    // keccak256(request)
                resultHash      // keccak256(result)
            )
        );

        // EIP-191 message hash
        bytes32 ethSignedMessageHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)
        );

        // Recover signer
        address signer = recoverSigner(ethSignedMessageHash, sig);

        // Verify signer is authorized
        require(signers[signer], "Invalid signer");

        return responseData;
    }

    /**
     * @notice Recover signer address from signature
     * @param ethSignedMessageHash EIP-191 formatted message hash
     * @param sig Signature bytes (65 bytes: r + s + v)
     * @return Signer address
     */
    function recoverSigner(bytes32 ethSignedMessageHash, bytes memory sig)
        internal
        pure
        returns (address)
    {
        require(sig.length == 65, "Invalid signature length");

        bytes32 r;
        bytes32 s;
        uint8 v;

        assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            v := byte(0, mload(add(sig, 96)))
        }

        // Adjust v if needed (some libraries use 0/1 instead of 27/28)
        if (v < 27) {
            v += 27;
        }

        require(v == 27 || v == 28, "Invalid signature v value");

        return ecrecover(ethSignedMessageHash, v, r, s);
    }

    /**
     * @notice Check if address is an authorized signer
     * @param _signer Address to check
     * @return True if authorized
     */
    function isSigner(address _signer) external view returns (bool) {
        return signers[_signer];
    }

    /**
     * @notice Add a new authorized signer (owner only)
     * @param _signer Address to authorize
     */
    function addSigner(address _signer) external onlyOwner {
        require(_signer != address(0), "Invalid address");
        require(!signers[_signer], "Already a signer");

        signers[_signer] = true;
        emit SignerAdded(_signer);
    }

    /**
     * @notice Remove an authorized signer (owner only)
     * @param _signer Address to remove
     */
    function removeSigner(address _signer) external onlyOwner {
        require(signers[_signer], "Not a signer");

        signers[_signer] = false;
        emit SignerRemoved(_signer);
    }

    /**
     * @notice Update gateway URLs (owner only)
     * @param _urls New array of gateway URLs
     */
    function setUrls(string[] memory _urls) external onlyOwner {
        require(_urls.length > 0, "Need at least one URL");
        urls = _urls;
        emit UrlUpdated(_urls);
    }

    /**
     * @notice Get all gateway URLs
     * @return Array of gateway URLs
     */
    function getUrls() external view returns (string[] memory) {
        return urls;
    }

    /**
     * @notice Transfer ownership
     * @param newOwner New owner address
     */
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "Invalid address");
        owner = newOwner;
    }

    /**
     * @notice Check if contract supports an interface (EIP-165)
     * @param interfaceID Interface identifier
     * @return True if supported
     */
    function supportsInterface(bytes4 interfaceID) external pure returns (bool) {
        return
            interfaceID == 0x9061b923 || // IExtendedResolver
            interfaceID == 0x01ffc9a7;   // EIP-165
    }
}

Read Contract

getUrls 0xda06e07f → string[]
isSigner 0x7df73e27 → bool
owner 0x8da5cb5b → address
resolve 0x9061b923 → bytes
resolveCallback 0xb4a85801 → bytes
signers 0x736c0d5b → bool
supportsInterface 0x01ffc9a7 → bool
urls 0x796676be → string

Write Contract 4 functions

These functions modify contract state and require a wallet transaction to execute.

addSigner 0xeb12d61e
address _signer
removeSigner 0x0e316ab7
address _signer
setUrls 0x6cc895a9
string[] _urls
transferOwnership 0xf2fde38b
address newOwner

Recent Transactions

No transactions found for this address