Skip to content

Commit 87edfac

Browse files
authored
Merge pull request #248 from kleros/reality-kleros-on-base
Reality Kleros module on Base + fixes in development mode
2 parents 37d4b65 + 85c4d00 commit 87edfac

File tree

12 files changed

+167
-62
lines changed

12 files changed

+167
-62
lines changed

packages/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"react-select": "^5.4.0",
3434
"styled-components": "^6.1.11",
3535
"timeago-react": "^3.0.2",
36-
"viem": "^2.17.4",
36+
"viem": "^2.41.2",
3737
"vite": "^5.3.3",
3838
"vite-plugin-node-polyfills": "^0.22.0",
3939
"vite-plugin-top-level-await": "^1.4.1",

packages/app/src/components/input/ArbitratorSelect.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const klerosAvailability: number[] = [
1717
NETWORK.MAINNET,
1818
NETWORK.GNOSIS_CHAIN,
1919
NETWORK.POLYGON,
20+
NETWORK.BASE,
2021
NETWORK.SEPOLIA,
2122
]
2223

packages/app/src/hooks/useEns.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,26 @@ const useEns = () => {
1414
useEffect(() => {
1515
const initializeClient = async () => {
1616
try {
17+
const chain = mode === 'development' ? sepolia : mainnet
18+
19+
// Add ENS registry contract (required by ENS client but missing in viem chain definitions)
20+
const chainWithEns = {
21+
...chain,
22+
contracts: {
23+
...chain.contracts,
24+
ensRegistry: {
25+
// https://docs.ens.domains/resolution/#reverse-resolution
26+
address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e' as const,
27+
},
28+
},
29+
} as const
30+
1731
const client = createEnsPublicClient({
18-
chain: mode === 'development' ? sepolia : mainnet,
32+
chain: chainWithEns,
1933
transport: http(),
2034
})
2135
const walletClient = createEnsWalletClient({
22-
chain: mode === 'development' ? sepolia : mainnet,
36+
chain: chainWithEns,
2337
transport: http(),
2438
})
2539
setEnsClient(client as EnsPublicClient<any, any>)

packages/app/src/services/ens.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { BrowserProvider, Provider, ethers, getAddress } from 'ethers'
22
import { BaseTransaction } from '@gnosis.pm/safe-apps-sdk'
33
import { EnsPublicClient } from '@ensdomains/ensjs'
44
import { namehash, normalize } from 'viem/ens'
5+
6+
const isDev = import.meta.env.MODE === 'development'
7+
58
//Some apps may show the contract address as the owner. This doesn't affect your ownership.
69
enum EnsWrappedContract {
710
MAINNET = '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401',
@@ -80,22 +83,27 @@ export const checkIfIsOwner = async (
8083
}
8184
}
8285

83-
export const getEnsTextRecord = async (ensName: string, recordId: string, provider: Provider) => {
86+
export const getEnsTextRecord = async (
87+
ensName: string,
88+
recordId: string,
89+
mainnetProvider: Provider,
90+
sepoliaProvider: Provider) => {
8491
const nameHash = namehash(ensName)
85-
const ensRegistryContract = new ethers.Contract(ensRegistry, abiRegistry, provider)
92+
const ensRegistryContract = new ethers.Contract(ensRegistry, abiRegistry, getProvider(mainnetProvider, sepoliaProvider))
8693
const ensResolverAddress = await ensRegistryContract.resolver(nameHash)
87-
const ensResolverContract = new ethers.Contract(ensResolverAddress, abiPublicResolver, provider)
94+
const ensResolverContract = new ethers.Contract(ensResolverAddress, abiPublicResolver, getProvider(mainnetProvider, sepoliaProvider))
8895
const record = ensResolverContract.text(nameHash, recordId)
89-
return record
96+
return record
9097
}
9198

9299
export const checkIfIsController = async (
93-
provider: BrowserProvider,
100+
mainnetProvider: Provider,
101+
sepoliaProvider: Provider,
94102
ensClient: EnsPublicClient<any, any>,
95103
ensName: string,
96104
safeAddress: string,
97105
) => {
98-
if (!provider || !ensClient || !ensName || !safeAddress) {
106+
if (!mainnetProvider || !sepoliaProvider || !ensClient || !ensName || !safeAddress) {
99107
throw new Error('all parameter are required')
100108
}
101109

@@ -106,11 +114,13 @@ export const checkIfIsController = async (
106114
return false
107115
}
108116
const node = namehash(ensName)
109-
const resolverContract = new ethers.Contract(resolverAddress, resolverAbi, provider)
117+
const resolverContract = new ethers.Contract(resolverAddress, resolverAbi, getProvider(mainnetProvider, sepoliaProvider))
110118
const controller = await resolverContract.addr(node)
111119
return getAddress(controller) === getAddress(safeAddress)
112120
} catch (error) {
113121
console.error('Error verifying the ENS controller:', error)
114122
return false
115123
}
116124
}
125+
126+
const getProvider = (mainnetProvider: Provider, sepoliaProvider: Provider) => isDev ? sepoliaProvider : mainnetProvider;

packages/app/src/services/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export function getDefaultOracle(chainId: number): string {
119119
case NETWORK.SEPOLIA:
120120
return '0xaf33DcB6E8c5c4D9dDF579f53031b514d19449CA'
121121
case NETWORK.BASE:
122-
return '' // TODO
122+
return '0x2F39f464d16402Ca3D8527dA89617b73DE2F60e8'
123123
}
124124
return ''
125125
}
@@ -201,6 +201,8 @@ export function getKlerosAddress(chainId: number): string {
201201
return '0x29f39de98d750eb77b5fafb31b2837f079fce222'
202202
case NETWORK.POLYGON:
203203
return '0x5AFa42b30955f137e10f89dfb5EF1542a186F90e'
204+
case NETWORK.BASE:
205+
return '0xBeeB211CfE6632E75992488A66F65b0477FBe96B'
204206
case NETWORK.SEPOLIA:
205207
return '0x05b942faecfb3924970e3a28e0f230910cedff45'
206208
}

packages/app/src/services/snapshot.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import snapshot from '@snapshot-labs/snapshot.js'
22
import * as R from 'ramda'
33
import { NETWORK } from 'utils/networks'
44

5+
const isDev = import.meta.env.MODE === 'development'
6+
57
const SNAPSHOT_HUB = 'https://hub.snapshot.org'
68
const SNAPSHOT_HUB_TEST = 'https://testnet.hub.snapshot.org'
79
const SNAPSHOT_SPACE = 'https://snapshot.org'
@@ -10,7 +12,7 @@ const SNAPSHOT_SPACE_TEST = 'https://testnet.snapshot.org'
1012
// Returns snapshot space settings, or undefined if no space was found for the ENS name.
1113
export const getSnapshotSpaceSettings = async (ensName: string, chainId: number) => {
1214
await updateSnapshotCache(ensName, chainId) // make sure that the returned snapshot space settings is the newest version
13-
const res = await fetch(`${getHubUrl(chainId)}/api/spaces/${ensName}`)
15+
const res = await fetch(`${getHubUrl()}/api/spaces/${ensName}`)
1416
if (res.ok) {
1517
try {
1618
return await res.json().then((res) => {
@@ -30,7 +32,7 @@ export const validateSchema = (spaceSettings: any) =>
3032
snapshot.utils.validateSchema(snapshot.schemas.space, spaceSettings)
3133

3234
export const updateSnapshotCache = (ensName: string, chainId: number) =>
33-
fetch(`${getHubUrl(chainId)}/api/spaces/${ensName}/poke`)
35+
fetch(`${getHubUrl()}/api/spaces/${ensName}/poke`)
3436

3537
export const verifyNewSnapshotSettings = (originalSettings: any, newSettings: any) =>
3638
R.and(
@@ -44,8 +46,8 @@ export const verifyNewSnapshotSettings = (originalSettings: any, newSettings: an
4446
validateSchema(newSettings) === true,
4547
)
4648

47-
const getHubUrl = (chainId: number) =>
48-
chainId === NETWORK.SEPOLIA ? SNAPSHOT_HUB_TEST : SNAPSHOT_HUB
49+
const getHubUrl = () =>
50+
isDev ? SNAPSHOT_HUB_TEST : SNAPSHOT_HUB
4951

50-
export const getSnapshotSpaceUrl = (chainId: number, ensName: string) =>
51-
(chainId === NETWORK.SEPOLIA ? SNAPSHOT_SPACE_TEST : SNAPSHOT_SPACE) + `/#/${ensName}`
52+
export const getSnapshotSpaceUrl = (ensName: string) =>
53+
isDev ? SNAPSHOT_SPACE_TEST : SNAPSHOT_SPACE + `/#/${ensName}`

packages/app/src/views/AddModule/wizards/KlerosRealityModule/KlerosRealityModuleModal.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,8 @@ export const KlerosRealityModuleModal = ({ open, onClose, onSubmit }: RealityMod
231231
const { sdk, safe, provider } = useSafeAppsSDKWithProvider()
232232
const { ensClient } = useEns()
233233
// hack to resolve mainnet ENS
234-
const mainnetProvider = useMemo(() => new InfuraProvider(1, import.meta.env.VITE_INFURA_ID), [])
235-
const goerliProvider = useMemo(() => new InfuraProvider(5, import.meta.env.VITE_INFURA_ID), [])
234+
const mainnetProvider = useMemo(() => new InfuraProvider(NETWORK.MAINNET, import.meta.env.VITE_INFURA_ID), [])
235+
const sepoliaProvider = useMemo(() => new InfuraProvider(NETWORK.SEPOLIA, import.meta.env.VITE_INFURA_ID), [])
236236

237237
const bondToken = NETWORKS[safe.chainId as NETWORK].nativeAsset
238238
const [params, setParams] = useState<RealityModuleParams>({
@@ -303,14 +303,16 @@ export const KlerosRealityModuleModal = ({ open, onClose, onSubmit }: RealityMod
303303
const daorequirements = await getEnsTextRecord(
304304
params.snapshotEns,
305305
'daorequirements',
306-
provider,
306+
mainnetProvider,
307+
sepoliaProvider,
307308
)
308309
setDaorequirements(daorequirements[0])
309310
setValidEns(snapshotSpace !== undefined)
310311
if (snapshotSpace !== undefined) {
311312
setIsSafesnapInstalled(!!snapshotSpace.plugins?.safeSnap)
312313
const isController = await checkIfIsController(
313-
provider,
314+
mainnetProvider,
315+
sepoliaProvider,
314316
ensClient,
315317
params.snapshotEns,
316318
safe.safeAddress,
@@ -324,7 +326,7 @@ export const KlerosRealityModuleModal = ({ open, onClose, onSubmit }: RealityMod
324326
}
325327
} else {
326328
}
327-
}, [params.snapshotEns, safe.chainId, safe.safeAddress, mainnetProvider, goerliProvider])
329+
}, [params.snapshotEns, safe.chainId, safe.safeAddress, mainnetProvider, sepoliaProvider])
328330

329331
const debouncedSnapshotEnsValidation = debounce(() => {
330332
setValidEns(false)

packages/app/src/views/AddModule/wizards/RealityModule/sections/Oracle/components/OracleInstance/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Grid, makeStyles, Typography } from "@material-ui/core"
33
import { Dropdown } from "components/Dropdown"
44
import React, { useEffect, useState } from "react"
55
import { colors, ZodiacTextField } from "zodiac-ui-components"
6-
import { InputPartProps, ORACLE_MAINNET_OPTIONS, ORACLE_GOERLI_OPTIONS } from "../.."
6+
import { InputPartProps, ORACLE_MAINNET_OPTIONS, ORACLE_SEPOLIA_OPTIONS } from "../.."
77

88
const useStyles = makeStyles((theme) => ({
99
container: {
@@ -32,7 +32,7 @@ export type Data = {
3232
export const OracleInstance: React.FC<InputPartProps> = ({ data, setData }) => {
3333
const classes = useStyles()
3434
const { safe } = useSafeAppsSDK()
35-
const options = safe.chainId === 1 ? ORACLE_MAINNET_OPTIONS : ORACLE_GOERLI_OPTIONS
35+
const options = safe.chainId === 1 ? ORACLE_MAINNET_OPTIONS : ORACLE_SEPOLIA_OPTIONS
3636
const [selectedOracle, setSelectedOracle] = useState<string>("")
3737

3838
useEffect(() => {

packages/app/src/views/AddModule/wizards/RealityModule/sections/Oracle/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ export const ORACLE_MAINNET_OPTIONS = [
7171
// { label: "Add Custom Instance", value: "custom" },
7272
]
7373

74-
export const ORACLE_GOERLI_OPTIONS = [
74+
export const ORACLE_SEPOLIA_OPTIONS = [
7575
{
76-
label: 'ETH-0x6F80C5cBCF9FbC2dA2F0675E56A5900BB70Df72f',
77-
value: 'ETH-0x6F80C5cBCF9FbC2dA2F0675E56A5900BB70Df72f',
76+
label: 'ETH-0xaf33DcB6E8c5c4D9dDF579f53031b514d19449CA',
77+
value: 'ETH-0xaf33DcB6E8c5c4D9dDF579f53031b514d19449CA',
7878
},
7979
// { label: "Add Custom Instance", value: "custom" },
8080
]
@@ -95,7 +95,7 @@ export type OracleSectionData = {
9595
export const OracleSection: React.FC<SectionProps> = ({ handleBack, handleNext, setupData }) => {
9696
const classes = useStyles()
9797
const { safe } = useSafeAppsSDK()
98-
const options = safe.chainId === 1 ? ORACLE_MAINNET_OPTIONS : ORACLE_GOERLI_OPTIONS
98+
const options = safe.chainId === 1 ? ORACLE_MAINNET_OPTIONS : ORACLE_SEPOLIA_OPTIONS
9999
const [showModal, setShowModal] = useState<boolean>(false)
100100
if (setupData?.proposal.ensName == null) {
101101
throw new Error('ENS name is not set')

packages/app/src/views/AddModule/wizards/RealityModule/sections/Proposal/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from '@material-ui/core'
1313

1414
import { Link } from 'components/text/Link'
15-
import React, { useEffect, useState } from 'react'
15+
import React, { useEffect, useMemo, useState } from 'react'
1616
// import useSpace from "services/snapshot/hooks/useSpace"
1717
import { checkIfIsController, checkIfIsOwner } from 'services/ens'
1818
import { SectionProps } from 'views/AddModule/wizards/RealityModule'
@@ -25,9 +25,11 @@ import DoneIcon from '@material-ui/icons/Done'
2525
import ErrorOutlineIcon from '@material-ui/icons/ErrorOutline'
2626
import useSafeAppsSDKWithProvider from 'hooks/useSafeAppsSDKWithProvider'
2727
import { safeAppUrl } from 'utils/url'
28+
import { NETWORK } from 'utils/networks'
2829
import useEns from 'hooks/useEns'
2930
import useDebouncedState from 'hooks/useDebouncedState'
3031
import { getAddressRecord } from '@ensdomains/ensjs/public'
32+
import { InfuraProvider } from 'ethers'
3133

3234
const useStyles = makeStyles((theme) => ({
3335
container: {
@@ -128,6 +130,10 @@ export const ProposalSection: React.FC<SectionProps> = ({ handleNext, handleBack
128130
const [validSnapshot, setValidSnapshot] = useState<boolean>(false)
129131
const [loading, setLoading] = useState<boolean>(false)
130132
const [ensIsValid, setEnsIsValid] = useState<boolean>(false)
133+
134+
// hack to resolve mainnet ENS
135+
const mainnetProvider = useMemo(() => new InfuraProvider(NETWORK.MAINNET, import.meta.env.VITE_INFURA_ID), [])
136+
const sepoliaProvider = useMemo(() => new InfuraProvider(NETWORK.SEPOLIA, import.meta.env.VITE_INFURA_ID), [])
131137

132138
useEffect(() => {
133139
if (provider && setupData && setupData.proposal) {
@@ -154,7 +160,7 @@ export const ProposalSection: React.FC<SectionProps> = ({ handleNext, handleBack
154160
}
155161
}
156162
checkEns()
157-
}, [debouncedEnsName, ensClient])
163+
}, [debouncedEnsName, ensClient, mainnetProvider, sepoliaProvider])
158164

159165
const validEns = async () => {
160166
if (!ensClient) return
@@ -165,7 +171,7 @@ export const ProposalSection: React.FC<SectionProps> = ({ handleNext, handleBack
165171
console.log(snapshotSpace)
166172
// const snapshotSpaceValidation = snapshot.validateSchema(snapshotSpace)
167173
const isOwner = await checkIfIsOwner(ensClient, ensName, safe.safeAddress)
168-
const isController = await checkIfIsController(provider, ensClient, ensName, safe.safeAddress)
174+
const isController = await checkIfIsController(mainnetProvider, sepoliaProvider, ensClient, ensName, safe.safeAddress)
169175
const plugins = snapshotSpace?.plugins
170176
if (plugins) {
171177
setIsSafesnapInstalled(plugins.safeSnap ? true : false) // comment out for easy testing

0 commit comments

Comments
 (0)