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

Crosschain resolver #7

Merged
merged 44 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0971ed6
Make testResolver work
makoto Oct 20, 2023
383f29a
Update bun.lockb
makoto Oct 20, 2023
88dc259
Add coinType
makoto Oct 23, 2023
8a58674
Make text record work
makoto Oct 23, 2023
8cb5256
Add hardhat-storage-layout
makoto Oct 23, 2023
641e15b
Use the real Resolver contracts
makoto Oct 23, 2023
914c66e
Add contenthash
makoto Oct 23, 2023
9a1a74d
Add comment
makoto Oct 23, 2023
9a386ea
Update README
makoto Oct 23, 2023
db89d14
Change the license to ENS labs
makoto Oct 23, 2023
805b218
Remove console.log
makoto Oct 23, 2023
757d787
Add an explanation
makoto Oct 23, 2023
93ef4b7
typo
makoto Oct 23, 2023
b3141e5
Remove L1Verifier.sol
makoto Oct 23, 2023
80292c4
Remove L1Verifier.sol
makoto Oct 23, 2023
0a227de
Rename test
makoto Oct 23, 2023
7f086ce
Add hardhat-deploy
makoto Oct 23, 2023
749ccd0
Add deployment script
makoto Oct 23, 2023
6c9f241
Remove deployment addresses
makoto Oct 23, 2023
4871812
Merge branch 'crosschain-resolver' into crosschain-resolver-deployment
makoto Oct 23, 2023
50d48b6
Apply suggestions from code review
makoto Oct 25, 2023
84dd082
Add immutable and constants
makoto Oct 25, 2023
40be600
Remove getStatic
makoto Oct 25, 2023
415392b
Fix the wrong constants
makoto Oct 25, 2023
7987695
Merge branch 'main' into crosschain-resolver
makoto Oct 25, 2023
507bfb4
Update bun.lockb
makoto Oct 25, 2023
0ee53c8
Remove contracts-bedrock
makoto Oct 25, 2023
574297d
Update bun.lockb
makoto Oct 25, 2023
3e49356
Add bun
makoto Oct 25, 2023
98adc6e
Remove package-lock.json
makoto Oct 26, 2023
81c5f08
Revert test sript
makoto Oct 26, 2023
a4044bd
update bun.lockb
TateB Oct 26, 2023
519a193
Add ..
makoto Oct 27, 2023
780b416
Merge branch 'revert-test-script' into crosschain-resolver
makoto Oct 27, 2023
9ffd059
Use BrowserProvider
makoto Oct 27, 2023
73f286f
Revert to use fork
makoto Oct 27, 2023
c713107
Put immutable and constant together
makoto Oct 27, 2023
fc128af
Use bytes20 instead
makoto Oct 27, 2023
f013347
Apply suggestions from code review
makoto Oct 27, 2023
f9a056e
Remove gasLimit and gas
makoto Oct 27, 2023
8974854
Merge branch 'crosschain-resolver' of https://github.com/ensdomains/e…
makoto Oct 27, 2023
d84a766
Add test for the empty record
makoto Oct 27, 2023
e0ea493
Remove remote
makoto Oct 27, 2023
e20e6c3
Remove RECORD_VERSIONS_REF constant
makoto Oct 30, 2023
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
Binary file modified bun.lockb
Binary file not shown.
12 changes: 12 additions & 0 deletions crosschain-resolver/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
.env
coverage
coverage.json
typechain
typechain-types

# Hardhat files
cache
artifacts
deployments

21 changes: 21 additions & 0 deletions crosschain-resolver/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 ENS Labs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
86 changes: 86 additions & 0 deletions crosschain-resolver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# @ensdomains/crosschain-resolver

A resolver contract that is built on top of evm-verifier.

For a detailed readme and usage instructions, see the [monorepo readme](https://github.com/ensdomains/evmgateway/tree/main).


## How it is defined

When the resolver has the following storage layout,

```
contract Resolver {
// node => version
mapping(bytes32 => uint64) public recordVersions;
// versionable_addresses[recordVersions[node]][node][coinType]
// version => node => cointype
mapping(uint64 => mapping(bytes32 => mapping(uint256 => bytes))) versionable_addresses;
```

Run `yarn storage` to find out storage slot for each variable

```
// ┌─────────────────────┬──────────────────────────────┬──────────────┬
// │ contract │ state_variable │ storage_slot │
// ├─────────────────────┼──────────────────────────────┼──────────────┼
// | OwnedResolver │ _owner │ 0 │
// │ OwnedResolver │ recordVersions │ 1 │
// │ OwnedResolver │ versionable_abis │ 2 │
// │ OwnedResolver │ versionable_addresses │ 3 │
```

Then define the l1 function

```
function addr(
bytes32 node,
uint256 coinType
) public view returns (bytes memory) {
EVMFetcher.newFetchRequest(verifier, target)
.getStatic(1) // storage_slot of recordVersions
.element(node)
.getDynamic(3) // storage_slot of versionable_addresses
.ref(0) // Referencing the result of `.getStatic(0)`
.element(node)
.element(coinType)
.fetch(this.addrCoinTypeCallback.selector, ''); // recordVersions
```

Storage verificaton can only verify the data of l2. When the function result needs some transformation, transform inside the callback function as follows.

```
function addrCallback(
bytes[] memory values,
bytes memory
) public pure returns (address) {
return bytesToAddress(values[1]);
}
```



## Deploying (Goerli)

Create `.env` and set the following variables


- DEPLOYER_PRIVATE_KEY
- L1_PROVIDER_URL
- L2_PROVIDER_URL
- L1_ETHERSCAN_API_KEY
- L2_ETHERSCAN_API_KEY
- OP_VERIFIER_ADDRESS=0x0c2746F20C9c97DBf718de10c04943cf408230A3

```
bun run hardhat deploy --network optimismGoerli
```

Followed by the L1 contract:

```
bun run hardhat deploy --network goerli
```


## Deployments
122 changes: 122 additions & 0 deletions crosschain-resolver/contracts/L1Resolver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import {EVMFetcher} from '@ensdomains/evm-verifier/contracts/EVMFetcher.sol';
import {EVMFetchTarget} from '@ensdomains/evm-verifier/contracts/EVMFetchTarget.sol';
import {IEVMVerifier} from '@ensdomains/evm-verifier/contracts/IEVMVerifier.sol';

contract L1Resolver is EVMFetchTarget {
using EVMFetcher for EVMFetcher.EVMFetchRequest;
IEVMVerifier immutable verifier;
address immutable target;
uint256 constant COIN_TYPE_ETH = 60;
uint256 constant RECORD_VERSIONS_SLOT = 1;
uint256 constant VERSINABLE_ADDRESSES_SLOT = 3;
uint256 constant VERSINABLE_HASHES_SLOT = 4;
uint256 constant VERSINABLE_TEXTS_SLOT = 11;

constructor(IEVMVerifier _verifier, address _target) {
verifier = _verifier;
target = _target;
}

/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @return The associated address.
*/
function addr(bytes32 node) public view returns (address) {
EVMFetcher.newFetchRequest(verifier, target)
.getStatic(RECORD_VERSIONS_SLOT)
.element(node)
.getDynamic(VERSINABLE_ADDRESSES_SLOT)
.ref(0)
.element(node)
.element(COIN_TYPE_ETH)
.fetch(this.addrCallback.selector, ''); // recordVersions
}

function addrCallback(
bytes[] memory values,
bytes memory
) public pure returns (address) {
return address(bytes20(values[1]));
}

/**
* Returns the address associated with an ENS node.
* @param node The ENS node to query.
* @param coinType The cointype to query
* @return The associated address.
*/
function addr(
bytes32 node,
uint256 coinType
) public view returns (bytes memory) {
EVMFetcher.newFetchRequest(verifier, target)
.getStatic(RECORD_VERSIONS_SLOT)
.element(node)
.getDynamic(VERSINABLE_ADDRESSES_SLOT)
.ref(0)
.element(node)
.element(coinType)
.fetch(this.addrCoinTypeCallback.selector, '');
}

function addrCoinTypeCallback(
bytes[] memory values,
bytes memory
) public pure returns (bytes memory) {
return values[1];
}

/**
* Returns the text data associated with an ENS node and key.
* @param node The ENS node to query.
* @param key The text data key to query.
* @return The associated text data.
*/
function text(
bytes32 node,
string calldata key
) public view returns (string memory) {
EVMFetcher.newFetchRequest(verifier, target)
.getStatic(RECORD_VERSIONS_SLOT)
.element(node)
.getDynamic(VERSINABLE_TEXTS_SLOT)
.ref(0)
.element(node)
.element(key)
.fetch(this.textCallback.selector, '');
}

function textCallback(
bytes[] memory values,
bytes memory
) public pure returns (string memory) {
return string(values[1]);
}

/**
* Returns the contenthash associated with an ENS node.
* @param node The ENS node to query.
* @return The associated contenthash.
*/
function contenthash(bytes32 node) public view returns (bytes memory) {
EVMFetcher.newFetchRequest(verifier, target)
.getStatic(RECORD_VERSIONS_SLOT)
.element(node)
.getDynamic(VERSINABLE_HASHES_SLOT)
.ref(0)
.element(node)
.fetch(this.contenthashCallback.selector, '');
}

function contenthashCallback(
bytes[] memory values,
bytes memory
) public pure returns (bytes memory) {
return values[1];
}

}
19 changes: 19 additions & 0 deletions crosschain-resolver/contracts/deps.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {L1Verifier} from '@ensdomains/l1-verifier/contracts/L1Verifier.sol';
import '@ensdomains/ens-contracts/contracts/resolvers/OwnedResolver.sol';

// Storage slot
// ┌─────────────────────┬──────────────────────────────┬──────────────┬
// │ contract │ state_variable │ storage_slot │
// ├─────────────────────┼──────────────────────────────┼──────────────┼
// | OwnedResolver │ _owner │ 0 │
// │ OwnedResolver │ recordVersions │ 1 │
// │ OwnedResolver │ versionable_abis │ 2 │
// │ OwnedResolver │ versionable_addresses │ 3 │
// │ OwnedResolver │ versionable_hashes │ 4 │
// │ OwnedResolver │ versionable_zonehashes │ 5 │
// │ OwnedResolver │ versionable_records │ 6 │
// │ OwnedResolver │ versionable_nameEntriesCount │ 7 │
// │ OwnedResolver │ versionable_interfaces │ 8 │
// │ OwnedResolver │ versionable_names │ 9 │
// │ OwnedResolver │ versionable_pubkeys │ 10 │
// │ OwnedResolver │ versionable_texts │ 11 │
22 changes: 22 additions & 0 deletions crosschain-resolver/deploy_l1/10_l1resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';


const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre;
const {deploy} = deployments;

const {deployer} = await getNamedAccounts();

const OP_VERIFIER_ADDRESS = process.env.OP_VERIFIER_ADDRESS
const OwnedResolver = await hre.companionNetworks['l2'].deployments.get('OwnedResolver');
if(!OP_VERIFIER_ADDRESS) throw ('Set $OP_VERIFIER_ADDRESS')
console.log({OP_VERIFIER_ADDRESS, OWNED_RESOLVER_ADDRESS:OwnedResolver.address})
await deploy('L1Resolver', {
from: deployer,
args: [OP_VERIFIER_ADDRESS, OwnedResolver.address],
log: true,
});
};
export default func;
func.tags = ['L1Resolver'];
17 changes: 17 additions & 0 deletions crosschain-resolver/deploy_l2/01_l2resolver .ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const {deployments, getNamedAccounts} = hre;
const {deploy} = deployments;

const {deployer} = await getNamedAccounts();
console.log({deployer})
await deploy('OwnedResolver', {
from: deployer,
args: [],
log: true,
});
};
export default func;
func.tags = ['OwnedResolver'];
52 changes: 52 additions & 0 deletions crosschain-resolver/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import '@nomicfoundation/hardhat-toolbox';
import { HardhatUserConfig } from 'hardhat/config';
import "hardhat-storage-layout";
import 'hardhat-deploy';
import 'hardhat-deploy-ethers';
const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY ?? "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
const L1_PROVIDER_URL = process.env.L1_PROVIDER_URL || '';
const L1_ETHERSCAN_API_KEY = process.env.L1_ETHERSCAN_API_KEY || '';
const L2_ETHERSCAN_API_KEY = process.env.L2_ETHERSCAN_API_KEY || '';

const config: HardhatUserConfig = {
solidity: '0.8.19',
networks: {
ganache: {
url: `http://localhost:${parseInt(process.env['RPC_PORT'] || '8545')}`,
},
goerli: {
url: L1_PROVIDER_URL,
accounts: [DEPLOYER_PRIVATE_KEY],
deploy: [ "deploy_l1/" ],
companionNetworks: {
l2: "optimismGoerli",
},
},
optimismGoerli: {
url: "https://goerli.optimism.io",
accounts: [DEPLOYER_PRIVATE_KEY],
deploy: [ "deploy_l2/" ],
}
},
etherscan: {
apiKey: {
goerli: L1_ETHERSCAN_API_KEY,
optimismGoerli: L2_ETHERSCAN_API_KEY
},
customChains: [
{
network: "optimismGoerli",
chainId: 420,
urls: {
apiURL: "https://api-goerli-optimism.etherscan.io/api",
browserURL: "https://goerli-optimism.etherscan.io"
}
}
]
},
namedAccounts: {
'deployer': 0,
}
};

export default config;
Loading
Loading