Skip to content

Commit 1f64ce5

Browse files
authored
[xc-admin] multisig hook (#496)
* Fix next config * Checkpoint * Checkpoint * Cleanup * Cleanup
1 parent 5e8b9f8 commit 1f64ce5

File tree

5 files changed

+167
-5
lines changed

5 files changed

+167
-5
lines changed

governance/xc-admin/package-lock.json

Lines changed: 16 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import SquadsMesh from '@sqds/mesh'
2+
import { TransactionAccount } from '@sqds/mesh/lib/types'
3+
import React, { createContext, useContext, useMemo } from 'react'
4+
import { useMultisig } from '../hooks/useMultisig'
5+
6+
// TODO: fix any
7+
interface MultisigContextProps {
8+
isLoading: boolean
9+
error: any // TODO: fix any
10+
squads: SquadsMesh | undefined
11+
proposals: TransactionAccount[]
12+
}
13+
14+
const MultisigContext = createContext<MultisigContextProps>({
15+
proposals: [],
16+
isLoading: true,
17+
error: null,
18+
squads: undefined,
19+
})
20+
21+
export const useMultisigContext = () => useContext(MultisigContext)
22+
23+
interface MultisigContextProviderProps {
24+
children?: React.ReactNode
25+
}
26+
27+
export const MultisigContextProvider: React.FC<
28+
MultisigContextProviderProps
29+
> = ({ children }) => {
30+
const { isLoading, error, squads, proposals } = useMultisig()
31+
32+
const value = useMemo(
33+
() => ({
34+
proposals,
35+
isLoading,
36+
error,
37+
squads,
38+
}),
39+
[squads, isLoading, error, proposals]
40+
)
41+
42+
return (
43+
<MultisigContext.Provider value={value}>
44+
{children}
45+
</MultisigContext.Provider>
46+
)
47+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Wallet } from '@coral-xyz/anchor'
2+
import { PythCluster } from '@pythnetwork/client/lib/cluster'
3+
import { Cluster, Connection, Keypair, PublicKey } from '@solana/web3.js'
4+
import SquadsMesh from '@sqds/mesh'
5+
import { TransactionAccount } from '@sqds/mesh/lib/types'
6+
import { useContext, useEffect, useRef, useState } from 'react'
7+
import { getProposals } from 'xc-admin-common'
8+
import { ClusterContext } from '../contexts/ClusterContext'
9+
import { pythClusterApiUrls } from '../utils/pythClusterApiUrl'
10+
11+
export function getMultisigCluster(cluster: PythCluster): Cluster | 'localnet' {
12+
switch (cluster) {
13+
case 'pythnet':
14+
return 'mainnet-beta'
15+
case 'pythtest':
16+
return 'devnet'
17+
default:
18+
return cluster
19+
}
20+
}
21+
22+
export const UPGRADE_MUTLTISIG: Record<Cluster | 'localnet', PublicKey> = {
23+
'mainnet-beta': new PublicKey('FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj'),
24+
testnet: new PublicKey('FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj'),
25+
devnet: new PublicKey('6baWtW1zTUVMSJHJQVxDUXWzqrQeYBr6mu31j3bTKwY3'),
26+
localnet: new PublicKey('FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj'),
27+
}
28+
29+
interface MultisigHookData {
30+
isLoading: boolean
31+
error: any // TODO: fix any
32+
squads: SquadsMesh | undefined
33+
proposals: TransactionAccount[]
34+
}
35+
36+
export const useMultisig = (): MultisigHookData => {
37+
const connectionRef = useRef<Connection>()
38+
const { cluster } = useContext(ClusterContext)
39+
const [isLoading, setIsLoading] = useState(true)
40+
const [error, setError] = useState(null)
41+
const [proposals, setProposals] = useState<TransactionAccount[]>([])
42+
const [squads, setSquads] = useState<SquadsMesh>()
43+
const [urlsIndex, setUrlsIndex] = useState(0)
44+
45+
useEffect(() => {
46+
setIsLoading(true)
47+
setError(null)
48+
}, [urlsIndex, cluster])
49+
50+
useEffect(() => {
51+
let cancelled = false
52+
const urls = pythClusterApiUrls(getMultisigCluster(cluster))
53+
const connection = new Connection(urls[urlsIndex].rpcUrl, {
54+
commitment: 'confirmed',
55+
wsEndpoint: urls[urlsIndex].wsUrl,
56+
})
57+
58+
connectionRef.current = connection
59+
;(async () => {
60+
try {
61+
const squads = new SquadsMesh({
62+
connection,
63+
wallet: new Keypair() as unknown as Wallet,
64+
})
65+
setProposals(
66+
await getProposals(
67+
squads,
68+
UPGRADE_MUTLTISIG[getMultisigCluster(cluster)]
69+
)
70+
)
71+
setSquads(squads)
72+
setIsLoading(false)
73+
} catch (e) {
74+
if (cancelled) return
75+
if (urlsIndex === urls.length - 1) {
76+
// @ts-ignore
77+
setError(e)
78+
setIsLoading(false)
79+
console.warn(`Failed to fetch accounts`)
80+
} else if (urlsIndex < urls.length - 1) {
81+
setUrlsIndex((urlsIndex) => urlsIndex + 1)
82+
console.warn(
83+
`Failed with ${urls[urlsIndex]}, trying with ${urls[urlsIndex + 1]}`
84+
)
85+
}
86+
}
87+
})()
88+
89+
return () => {}
90+
}, [urlsIndex, cluster])
91+
92+
return {
93+
isLoading,
94+
error,
95+
squads,
96+
proposals,
97+
}
98+
}

governance/xc-admin/packages/xc-admin-frontend/next.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/** @type {import('next').NextConfig} */
22
const nextConfig = {
33
reactStrictMode: true,
4+
experimental: {
5+
externalDir: true,
6+
},
47
webpack(config) {
58
const fileLoaderRule = config.module.rules.find(
69
(rule) => rule.test && rule.test.test('.svg')

governance/xc-admin/packages/xc-admin-frontend/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@solana/wallet-adapter-react-ui": "^0.9.27",
1717
"@solana/wallet-adapter-wallets": "^0.19.10",
1818
"@solana/web3.js": "^1.73.0",
19+
"@sqds/mesh": "^1.0.6",
1920
"@types/node": "18.11.18",
2021
"@types/react": "18.0.26",
2122
"@types/react-dom": "18.0.10",
@@ -27,7 +28,8 @@
2728
"react-dom": "18.2.0",
2829
"react-hot-toast": "^2.4.0",
2930
"typescript": "4.9.4",
30-
"use-debounce": "^9.0.2"
31+
"use-debounce": "^9.0.2",
32+
"xc-admin-common": "*"
3133
},
3234
"devDependencies": {
3335
"@svgr/webpack": "^6.3.1",

0 commit comments

Comments
 (0)