Skip to content
Merged
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
50 changes: 17 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ The smart contracts are the backbone of the External Resolver. They include the

#### Database Resolver

A smart contract that stores ENS domain data off-chain in a database. It enables cost-effective domain management by handling storage operations through the Gateway.

#### L1 Resolver

A smart contract that redirects requests to specified external contract deployed to any EVM compatible protocol.
Expand Down Expand Up @@ -149,19 +151,7 @@ try {

To run the External Resolver project in its entirety, you'll need to complete the installation process. Since we provide an off-chain resolver solution, it's essential to set up both the database and the Arbitrum Layer 2 environment. This will enable you to run comprehensive end-to-end tests and verify the functionality of the entire project.

### Prerequisites

- [Foundry](https://book.getfoundry.sh)
- Run local node by calling `anvil`

### Setup

1. Clone this repository to your local machine.
2. Copy the `env.example` file to `.env` in the root directory.
3. Install dependencies: `npm install`
4. Build the contracts: `npm run build`

#### Database Setup
### Database Setup

1. Run a local PostgreSQL instance (no initial data is inserted):

Expand All @@ -184,7 +174,7 @@ To run the External Resolver project in its entirety, you'll need to complete th
4. Write properties to a given domain:

```bash
npm run client start:write:db
npm run client start:write:db
```

5. Request domain properties through the client:
Expand All @@ -193,7 +183,7 @@ To run the External Resolver project in its entirety, you'll need to complete th
npm run client read
```

##### Migrations
#### Migrations

This repository relies on migrations to manage the database schema. To create a new migration, run the following command:

Expand All @@ -207,7 +197,7 @@ To apply the migration, run the following command:
npm run migration:generate -- -n <migration_name>
```

#### Layer 2 Setup
### Layer 2 Setup

1. Deploy the contracts to the local Arbitrum node (follow the [Arbitrum's local node setup tutorial](https://docs.arbitrum.io/run-arbitrum-node/run-local-dev-node)):

Expand All @@ -229,40 +219,34 @@ npm run migration:generate -- -n <migration_name>
npm run client start
```

## Deployment

### Gateway

Ensure you have the [Railway CLI](https://docs.railway.app/guides/cli) installed.
### Layer 1 Setup

1. Install the Railway CLI:
1. Run the local node:

```bash
npm i -g @railway/cli
anvil
```

2. Log in to your Railway account:
2. Deploy the contracts to the local node:

```bash
railway login
npm run contracts dev:eth
```

3. Link the repo to the project:
3. Gather the contract address from the terminal and add it [here](https://github.com/blockful-io/external-resolver/blob/main/packages/contracts/script/local/L1ArbitrumResolver.s.sol#L56) so the L1 domain gets resolved by the L2 contract you just deployed.

4. Start the gateway:

```bash
railway link
npm run gateway dev:eth
```

4. Deploy the Gateway:
5. Request domain properties through the client:

```bash
railway up
npm run client read
```

### Contracts

1. `npm run contracts deploy:db -- --rpc-url <RPC_URL>`

## Architecture

### High-Level Overview
Expand Down
5 changes: 5 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"packages/contracts/lib/optimism-bedrock-contracts": {
"rev": "934341571f50fd676c07014a0b31652cb105e41d"
}
}
24 changes: 23 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"test:arb": "hardhat test ./test/arb.e2e.spec.ts --network localhost",
"test:db": "hardhat test ./test/db.e2e.spec.ts --network localhost",
"test:metadata": "hardhat test ./test/metadata.e2e.spec.ts --network localhost",
"start:read": "ts-node src/read.ts",
"start:write": "ts-node src/write.ts",
"read": "ts-node src/read.ts",
"write": "ts-node src/write.ts",
"lint": "eslint . --ext .ts --fix"
},
"devDependencies": {
Expand All @@ -27,7 +27,8 @@
"reflect-metadata": "^0.2.2",
"solidity-coverage": "^0.8.12",
"typeorm": "^0.3.20",
"viem": "^2.21.42"
"viem": "^2.21.42",
"zod": "^4"
},
"peerDependencies": {
"@blockful/tsconfig": "*"
Expand Down
34 changes: 34 additions & 0 deletions packages/client/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { z } from 'zod'
import { config } from 'dotenv'
import { isAddress, getAddress, isHex } from 'viem'

config({ path: process.env.ENV_FILE || '../.env' })

export const readEnvSchema = z.object({
CHAIN_ID: z.coerce.number().default(31337),
RPC_URL: z.url().default('http://127.0.0.1:8545/'),
GATEWAY_URL: z
.url()
.default('http://127.0.0.1:3000/{sender}/{data}.json')
.transform((val) => [val]),
UNIVERSAL_RESOLVER_ADDRESS: z
.string()
.refine((val) => isAddress(val, { strict: false }), 'Invalid address')
.transform((val) => getAddress(val))
.optional(),
})

export const writeEnvSchema = z
.object({
RESOLVER_ADDRESS: z
.string()
.refine((val) => isAddress(val, { strict: false }), 'Invalid address')
.transform((val) => getAddress(val)),
L2_RPC_URL: z.url().optional().default('http://127.0.0.1:8545/'),
PRIVATE_KEY: z
.string()
.min(64)
.max(66)
.refine((val) => isHex(val), 'Private key must be a valid hex string'),
})
.extend(readEnvSchema.shape)
44 changes: 20 additions & 24 deletions packages/client/src/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,57 @@
* Blockchain Node and to redirect the request to a Gateway whenever necessary.
*/

import { config } from 'dotenv'
import { Hex, createPublicClient, http } from 'viem'
import { createPublicClient, http } from 'viem'
import { normalize } from 'viem/ens'
import { getChain } from './client'

config({
path: process.env.ENV_FILE || '../.env',
})
import { getChain } from './client'
import { readEnvSchema } from './env'

const {
CHAIN_ID: chainId = '31337',
RPC_URL: provider = 'http://127.0.0.1:8545/',
GATEWAY_URL: gateway = 'http://127.0.0.1:3000/{sender}/{data}.json',
CHAIN_ID,
RPC_URL,
GATEWAY_URL: gatewayUrls,
UNIVERSAL_RESOLVER_ADDRESS: universalResolverAddress,
} = process.env
} = readEnvSchema.parse(process.env)

const chain = getChain(parseInt(chainId))
const chain = getChain(CHAIN_ID)
console.log(`Connecting to ${chain?.name}.`)

const client = createPublicClient({
chain,
transport: http(provider),
transport: http(RPC_URL),
})

// eslint-disable-next-line
const _ = (async () => {
const name = normalize('lucas.arb.eth')
const name = normalize('blockful.eth')

const twitter = await client.getEnsText({
name,
key: 'com.twitter',
universalResolverAddress: universalResolverAddress as Hex,
gatewayUrls: [gateway],
universalResolverAddress,
gatewayUrls,
})
const avatar = await client.getEnsAvatar({
name,
universalResolverAddress: universalResolverAddress as Hex,
gatewayUrls: [gateway],
universalResolverAddress,
gatewayUrls,
})

const address = await client.getEnsAddress({
name,
universalResolverAddress: universalResolverAddress as Hex,
gatewayUrls: [gateway],
universalResolverAddress,
gatewayUrls,
})
const addressBtc = await client.getEnsAddress({
name,
coinType: 1,
universalResolverAddress: universalResolverAddress as Hex,
gatewayUrls: [gateway],
universalResolverAddress,
gatewayUrls,
})
const domainName = await client.getEnsName({
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
universalResolverAddress: universalResolverAddress as Hex,
gatewayUrls: [gateway],
universalResolverAddress,
gatewayUrls,
})

console.log({
Expand Down
Loading
Loading