Skip to content

Commit

Permalink
Merge pull request #13 from ConsenSys/feat/test-on-goerli-fork
Browse files Browse the repository at this point in the history
feat: working version with latest ERC721 L2 resolver
  • Loading branch information
Julink-eth authored Apr 12, 2023
2 parents 5de00e0 + 9ffe2fb commit ca6f427
Show file tree
Hide file tree
Showing 24 changed files with 666 additions and 1,412 deletions.
50 changes: 35 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This repository contains smart contracts and a node.js gateway server that together allow storing ENS names on Linea using [EIP 3668](https://eips.ethereum.org/EIPS/eip-3668) and [ENSIP 10](https://docs.ens.domains/ens-improvement-proposals/ensip-10-wildcard-resolution).

## Usage
## Test in a mixed local/L2 mode

### Setup local node

Expand All @@ -16,12 +16,32 @@ yarn hardhat node --fork YOUR_GOERLI_L1_RPC_URL

### Deploy contracts

In second terminal, deploy L1 and L2 smart contracts:
In second terminal, deploy L1 and L2 smart contracts.

Compile
Set your `.env` config file. You can copy [env.example](./packages/contracts/.env.example):

```bash
cd packages/contracts
cp .env.example .env
```

Edit `.env` and set your config:

| Var | Description | Default values |
| ----------------- | ------------------------- | --------------------------------------------------------- |
| GOERLI_URL | Goerli provider URL | |
| GOERLI_LINEA_URL | Linea Goerli provider URL | |
| PRIVATE_KEY | Wallet private key | |
| ETHERSCAN_API_KEY | Etherscan API key | |
| L1_ENS_NAME | L1 ENS name | lineatest.eth |
| L2_ENS_NAME | L2 ENS name | julink.lineatest.eth |
| GATEWAY_URL | Primary gateway URL | https://www.ensgateway.amineharty.me/{sender}/{data}.json |

For local/L2 mode, `GOERLI_URL` is not required.

Compile smart contracts:

```bash
yarn hardhat compile
```

Expand All @@ -31,21 +51,23 @@ Deploy L2 contracts first:
npx hardhat run --network goerliLinea scripts/deployL2.ts
```

Get the resolver address, then deploy L1 contracts.
> **_Imporant:_** Wait 10 minutes for Linea to synchronize with Goerli. This will allow the domain registered on Linea to be recognized by the state hash written in Goerli.
Get the `L2_RESOLVER_ADDRESS` resolver address, then deploy L1 contracts:

```
L2_RESOLVER_ADDRESS=$L2_RESOLVER_ADDRESS npx hardhat run --network goerli scripts/deployL1.ts
L2_RESOLVER_ADDRESS=$L2_RESOLVER_ADDRESS npx hardhat run --network localhost scripts/deployL1.ts
```

### Start Gateway server

Then start the gateway.
Once smart contracts are deployed, start the gateway:

```bash
cd ../gateway
yarn
yarn build
yarn start --l2_resolver_address $L2_RESOLVER_ADDRESS --helper_address $HELPER_ADDRESS
yarn start --l2_resolver_address $L2_RESOLVER_ADDRESS --l1_provider_url http://127.0.0.1:8545/ --l2_provider_url $GOERLI_LINEA_URL
```

### Run Client test script
Expand All @@ -54,15 +76,13 @@ In a third terminal, run the demo app:

```bash
cd packages/clients
yarn
yarn build
yarn start -r $ENS_REGISTRY_ADDRESS test.test
yarn start julink.lineatest.eth
```

If sucessful, it should show the following output
If successful, it should show the following output:

```
[...]
```bash
ethAddress 0xF110a41f75edEb224227747b64Be7f6A7f140abc
```

## How to deploy to public net (goerli for example)
Expand All @@ -75,14 +95,14 @@ If sucessful, it should show the following output

## Deploy gateway

Create secret.yaml and update credentials
Create secret.yaml and update credentials:

```
cd gateway
cp secret.yaml.org secret.yaml
```

Deploy to app engine
Deploy to app engine:

```
gcloud app deploy goeril.app.yml
Expand Down
1 change: 1 addition & 0 deletions packages/client/abi/ENSRegistry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"contract ENS","name":"_old","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"label","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"old","outputs":[{"internalType":"contract ENS","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"recordExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"label","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"bytes32","name":"label","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setSubnodeRecord","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"}]
18 changes: 8 additions & 10 deletions packages/client/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"version": "0.2.1",
"name": "linea-resolver-client",
"version": "1.0.0",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -32,8 +33,6 @@
"singleQuote": true,
"trailingComma": "es5"
},
"name": "@ensdomains/op-resolver-client",
"author": "Makoto Inoue",
"module": "dist/client.esm.js",
"size-limit": [
{
Expand All @@ -51,14 +50,13 @@
"size-limit": "^7.0.5",
"tsdx": "^0.14.1",
"tslib": "^2.3.1",
"typescript": "^4.5.4"
"typescript": "^4.9.4"
},
"dependencies": {
"@ensdomains/address-encoder": "^0.2.17",
"commander": "^8.3.0",
"eth-ens-namehash": "^2.0.8",
"@types/node": "^18.11.17",
"commander": "^9.4.1",
"dotenv": "^16.0.3",
"ethers": "^5.7.2",
"isomorphic-fetch": "^3.0.0",
"node-fetch": "^3.2.10"
"ts-node": "^10.9.1"
}
}
}
1 change: 1 addition & 0 deletions packages/client/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const REGISTRY_ADDRESS = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e";
141 changes: 33 additions & 108 deletions packages/client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { Command } from 'commander';
import { ethers } from 'ethers';
import 'isomorphic-fetch';
import { REGISTRY_ADDRESS } from './constants';

const namehash = require('eth-ens-namehash');
const StubAbi = require('../../contracts/artifacts/contracts/l1/LineaResolverStub.sol/LineaResolverStub.json')
.abi;
const IResolverAbi = require('../../contracts/artifacts/contracts/l1/LineaResolverStub.sol/IResolverService.json')
.abi;
const program = new Command();
const { defaultAbiCoder, hexConcat } = require('ethers/lib/utils');
program
.requiredOption('-r --registry <address>', 'ENS registry address')
.requiredOption(
'-r --registry <address>',
'ENS registry address',
REGISTRY_ADDRESS
)
.option(
'-l1 --l1_provider_url <url1>',
'L1_PROVIDER_URL',
'http://localhost:8545'
'http://127.0.0.1:8545/'
)
.option('-l2 --l2_provider_url <url2>', 'http://localhost:8545')
.option('-i --chainId <chainId>', 'chainId', '31337')
.option('-n --chainName <name>', 'chainName', 'unknown')
.option('-n --chainName <chainName>', 'chainName', 'unknown')
.option('-d --debug', 'debug', false)
.argument('<name>');

Expand All @@ -27,104 +24,32 @@ const options = program.opts();
const ensAddress = options.registry;
const chainId = parseInt(options.chainId);
const { chainName, l1_provider_url, debug } = options;
console.log({ l1_provider_url, ensAddress, chainId, chainName, debug });
let provider;
if (chainId && chainName) {
provider = new ethers.providers.JsonRpcProvider(l1_provider_url, {
chainId,
name: chainName,
ensAddress,
});
} else {
provider = new ethers.providers.JsonRpcProvider(options.l1_provider_url);
}
// provider.on("debug", console.log)
const l2provider = new ethers.providers.JsonRpcProvider(
options.l2_provider_url
);
console.log('options', {
l1_provider_url,
ensAddress,
chainId,
chainName,
debug,
});

(async () => {
const l1ChainId = parseInt(await provider.send('eth_chainId', []));
const l2ChainId = parseInt(await l2provider.send('eth_chainId', []));
const provider = new ethers.providers.JsonRpcProvider(l1_provider_url, {
chainId,
name: chainName,
ensAddress,
});

(async () => {
const name = program.args[0];
const node = namehash.hash(name);
console.log({ l1ChainId, l2ChainId, name, node });
let r = await provider.getResolver(name);
if (r) {
const resolver = new ethers.Contract(r.address, StubAbi, provider);
const iresolver = new ethers.Contract(r.address, IResolverAbi, provider);
try {
if (debug) {
// this will throw OffchainLookup error
console.log(await resolver.callStatic['addr(bytes32)'](node));
} else {
const beforeTime = new Date().getTime();
console.log('getAddress ', await r.getAddress());
const afterTime = new Date().getTime();
console.log('(call time=', afterTime - beforeTime, ')');
console.log('getAddress(60) ', await r.getAddress(60));
console.log(
'_fetchBytes ',
await r._fetchBytes(
'0xf1cb7e06',
'0x000000000000000000000000000000000000000000000000000000000000003c'
)
);
console.log(
'addr(bytes32) ',
await resolver.callStatic['addr(bytes32)'](node, {
ccipReadEnabled: true,
})
);
console.log(
'addr(bytes32,uint256)',
await resolver.callStatic['addr(bytes32,uint256)'](node, 60, {
ccipReadEnabled: true,
})
);
console.log('resolveName', await provider.resolveName(name));
}
} catch (e) {
// Manually calling the gateway
console.log('error', e);
if (e.errorArgs) {
const {
sender,
urls,
callData,
callbackFunction,
extraData,
} = e.errorArgs;
console.log(1, { sender, urls, callData, callbackFunction, extraData });
const url = urls[0]
.replace(/{sender}/, sender)
.replace(/{data}/, callData);
console.log(2, { url });
const fetched = await fetch(url);
const responseData: any = await fetched.json();
if (responseData) {
try {
const encoded = defaultAbiCoder.encode(
['bytes', 'bytes'],
[responseData.data, extraData]
);
const data = hexConcat([callbackFunction, encoded]);
const result = await resolver.provider.call({
to: resolver.address,
data,
});
console.log(4, { result });
const decodedResult = resolver.interface.decodeFunctionResult(
'addrWithProof',
result
);
console.log(5, { decodedResult });
} catch (ee) {
console.log(6, { ee });
}
}
}
}
console.log('name', name);
const resolverFound = await provider.getResolver(name);
console.log('resolverFound address', resolverFound?.address);
const node = ethers.utils.namehash(name);
console.log('node', node);

if (resolverFound) {
let ethAddress = await resolverFound.getAddress();
console.log('ethAddress', ethAddress);
} else {
console.log('No resolver found for:', name);
}
})();
})();
Loading

0 comments on commit ca6f427

Please sign in to comment.