Skip to content

Commit a65682d

Browse files
committed
refactor(deps): upgrade @vocdoni/davinci-sdk@0.0.3
refs #36
1 parent 87eae4f commit a65682d

7 files changed

Lines changed: 134 additions & 107 deletions

File tree

src/components/create-vote-form.tsx

Lines changed: 63 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,16 @@ import {
1414
verticalListSortingStrategy,
1515
} from '@dnd-kit/sortable'
1616
import { CSS } from '@dnd-kit/utilities'
17-
import { sepolia } from '@reown/appkit/networks'
1817
import { useAppKitNetwork } from '@reown/appkit/react'
1918
import {
20-
createProcessSignatureMessage,
19+
CensusOrigin,
2120
ElectionResultsTypeNames,
22-
ProcessRegistryService,
23-
ProcessStatus,
24-
SmartContractService,
21+
TxStatus,
2522
type BallotMode,
2623
type ElectionMetadata,
2724
type ElectionResultsType,
2825
type ProtocolVersion,
2926
} from '@vocdoni/davinci-sdk'
30-
import { BrowserProvider, type Eip1193Provider } from 'ethers'
3127
import { CheckCircle, Clock, GripVertical, HelpCircle, Plus, Rocket, Users, X } from 'lucide-react'
3228
import { useEffect, useState } from 'react'
3329
import { Controller, useFieldArray, useForm, type Control } from 'react-hook-form'
@@ -43,9 +39,9 @@ import { Separator } from '~components/ui/separator'
4339
import { Textarea } from '~components/ui/textarea'
4440
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~components/ui/tooltip'
4541
import { useMiniApp } from '~contexts/MiniAppContext'
42+
import { useSequencerNetwork } from '~contexts/sequencer-network'
4643
import { useVocdoniApi } from '~contexts/vocdoni-api-context'
4744
import { useSnapshots, type Snapshot } from '~hooks/use-snapshots'
48-
import { useUnifiedProvider } from '~hooks/use-unified-provider'
4945
import { useUnifiedWallet } from '~hooks/use-unified-wallet'
5046
import { CustomAddressesManager } from './census-addresses'
5147
import { Snapshots } from './snapshots'
@@ -159,7 +155,6 @@ export function CreateVoteForm() {
159155
const [isLaunching, setIsLaunching] = useState(false)
160156
const [launchSuccess, setLaunchSuccess] = useState(false)
161157
const { address, isConnected } = useUnifiedWallet()
162-
const { getProvider } = useUnifiedProvider()
163158
const [error, setError] = useState<Error | null>(null)
164159

165160
const form = useForm<FormData>({
@@ -190,7 +185,7 @@ export function CreateVoteForm() {
190185
})
191186

192187
const formData = watch()
193-
const api = useVocdoniApi()
188+
const { api, sdk } = useVocdoniApi()
194189
const { data: snapshots, isLoading: isLoadingSnapshot, isError: isSnapshotError } = useSnapshots()
195190

196191
// Reset weighted voting when switching to non-proportional snapshots
@@ -318,7 +313,12 @@ export function CreateVoteForm() {
318313

319314
console.info('Census created:', census)
320315

321-
// Step 7: Create and push metadata
316+
// Check if SDK is available
317+
if (!sdk) {
318+
throw new Error('SDK not initialized. Please ensure your wallet is connected.')
319+
}
320+
321+
// Build metadata for ballot mode generation
322322
const metadata: ElectionMetadata = {
323323
title: { default: data.question },
324324
description: { default: '' },
@@ -355,79 +355,61 @@ export function CreateVoteForm() {
355355
}
356356
}
357357

358-
const metadataHash = await api.sequencer.pushMetadata(metadata)
359-
const metadataUrl = api.sequencer.getMetadataUrl(metadataHash)
360-
console.info('ℹ️ Metadata URL:', metadataUrl)
361-
362-
// Use provider for process creation (check wallet capabilities)
363-
const walletProvider = await getProvider()
364-
if (!walletProvider) {
365-
throw new Error('Wallet provider not available.')
366-
}
367-
const provider = new BrowserProvider(walletProvider as Eip1193Provider)
368-
console.info('ℹ️ Browser provider initialized:', provider)
369-
const signer = await provider.getSigner()
370-
371-
// Get contract address from sequencer info
372-
const info = await api.sequencer.getInfo()
373-
const processRegistryAddress = info.contracts.process
374-
375-
const registry = new ProcessRegistryService(processRegistryAddress, signer)
376-
const address = await signer.getAddress()
377-
const pid = await registry.getNextProcessId(address)
378-
console.info('ℹ️ Process ID:', pid)
379-
380-
const message = await createProcessSignatureMessage(pid)
381-
const signature = await signer.signMessage(message)
382-
383358
const ballotMode = generateBallotMode(metadata, data)
384359
console.info('ℹ️ Ballot mode:', ballotMode)
385360

386-
const { processId, encryptionPubKey, stateRoot } = await api.sequencer.createProcess({
387-
processId: pid,
388-
censusRoot: census.censusRoot,
389-
ballotMode,
390-
signature,
391-
censusOrigin: data.censusType === 'custom-addresses' ? 1 : 1, // CensusOrigin.CensusOriginMerkleTree
392-
})
393-
console.info('✅ Process created with ID:', processId, stateRoot)
394-
395-
console.info('ℹ️ Creating new process with data:', [
396-
ProcessStatus.READY,
397-
Math.floor(Date.now() / 1000) + 60,
398-
getDurationInSeconds(data.duration, data.durationUnit),
399-
ballotMode,
400-
{
401-
censusOrigin: 1,
402-
maxVotes: census.censusSize.toString(),
403-
censusRoot: census.censusRoot,
404-
censusURI: census.censusURI,
361+
// Create process using SDK stream API
362+
const stream = sdk.createProcessStream({
363+
title: data.question,
364+
description: '',
365+
census: {
366+
type: CensusOrigin.CensusOriginMerkleTree,
367+
root: census.censusRoot,
368+
size: census.censusSize,
369+
uri: census.censusURI,
405370
},
406-
metadataUrl,
407-
{ x: encryptionPubKey[0], y: encryptionPubKey[1] },
408-
BigInt(stateRoot),
409-
])
410-
411-
// Step 10: Submit newProcess on-chain
412-
await SmartContractService.executeTx(
413-
registry.newProcess(
414-
ProcessStatus.READY,
415-
Math.floor(Date.now() / 1000) + 60,
416-
getDurationInSeconds(data.duration, data.durationUnit),
417-
ballotMode,
371+
ballot: ballotMode,
372+
timing: {
373+
startDate: new Date(Date.now() + 60000), // +1 minute
374+
duration: getDurationInSeconds(data.duration, data.durationUnit),
375+
},
376+
questions: [
418377
{
419-
censusOrigin: 1,
420-
maxVotes: census.censusSize.toString(),
421-
censusRoot: census.censusRoot,
422-
censusURI: census.censusURI,
378+
title: data.question,
379+
description: '',
380+
choices: data.choices
381+
.filter((choice) => choice.text.trim() !== '')
382+
.map((choice, index) => ({
383+
title: choice.text,
384+
value: index,
385+
})),
423386
},
424-
metadataUrl,
425-
{ x: encryptionPubKey[0], y: encryptionPubKey[1] },
426-
BigInt(stateRoot)
427-
)
428-
)
387+
],
388+
})
389+
390+
// Handle transaction status events
391+
let processId = ''
392+
for await (const event of stream) {
393+
console.info('📡 Transaction event:', event.status, event)
394+
395+
switch (event.status) {
396+
case TxStatus.Pending:
397+
console.info('⏳ Transaction pending:', event.hash)
398+
break
399+
case TxStatus.Completed:
400+
processId = event.response.processId
401+
console.info('✅ Process created with ID:', processId)
402+
setLaunchSuccess(true)
403+
break
404+
case TxStatus.Failed:
405+
console.error('❌ Transaction failed:', event.error)
406+
throw new Error(event.error?.message || 'Transaction failed')
407+
case TxStatus.Reverted:
408+
console.error('↩️ Transaction reverted:', event.reason)
409+
throw new Error(event.reason || 'Transaction reverted')
410+
}
411+
}
429412

430-
setLaunchSuccess(true)
431413
console.info('ℹ️ Vote launched successfully with process ID:', processId)
432414

433415
// Wait to navigate
@@ -1058,6 +1040,7 @@ type LaunchVoteButtonProps = {
10581040
const LaunchVoteButton = ({ handleLaunch, isLaunching, isFormValid }: LaunchVoteButtonProps) => {
10591041
const { isConnected } = useUnifiedWallet()
10601042
const { isMiniApp, isExternalWallet, supportedChains, getFarcasterEthereumProvider } = useMiniApp()
1043+
const { sequencerNetwork } = useSequencerNetwork()
10611044
const { caipNetwork } = useAppKitNetwork()
10621045

10631046
// All hooks must be at the top before any conditional returns
@@ -1084,7 +1067,6 @@ const LaunchVoteButton = ({ handleLaunch, isLaunching, isFormValid }: LaunchVote
10841067
// PRIORITY 2: Check chain compatibility (only for external wallets)
10851068
// Convert hex chain ID to decimal for comparison
10861069
const actualChainIdDecimal = actualChainId ? parseInt(actualChainId, 16) : null
1087-
const isOnSepoliaActually = actualChainIdDecimal === sepolia.id
10881070

10891071
useEffect(() => {
10901072
console.info('🔍 Chain validation debug:', {
@@ -1096,15 +1078,7 @@ const LaunchVoteButton = ({ handleLaunch, isLaunching, isFormValid }: LaunchVote
10961078
supportedChains,
10971079
isExternalWallet,
10981080
})
1099-
}, [
1100-
caipNetwork,
1101-
actualChainId,
1102-
isMiniApp,
1103-
supportedChains,
1104-
isExternalWallet,
1105-
actualChainIdDecimal,
1106-
isOnSepoliaActually,
1107-
])
1081+
}, [caipNetwork, actualChainId, isMiniApp, supportedChains, isExternalWallet, actualChainIdDecimal])
11081082

11091083
if (!isConnected) {
11101084
return <ConnectWalletButtonMiniApp />
@@ -1164,9 +1138,9 @@ const LaunchVoteButton = ({ handleLaunch, isLaunching, isFormValid }: LaunchVote
11641138
</div>
11651139
)}
11661140
<div className='ml-6 text-left text-davinci-black-alt/80 text-sm'>
1167-
Creating a vote requires a tx on the Sepolia testnet. If you need ETH to run a vote, you can get some from{' '}
1168-
<Link href='https://cloud.google.com/application/web3/faucet/ethereum/sepolia'>this faucet</Link>. The tx is
1169-
only needed to create the vote, <span className='font-medium'>casting votes is gasless.</span>
1141+
Creating a vote requires a tx on the {sequencerNetwork?.name} network. If you need ETH to run a vote, you can
1142+
get some from <Link href='https://cloud.google.com/application/web3/faucet/ethereum/sepolia'>this faucet</Link>.
1143+
The tx is only needed to create the vote, <span className='font-medium'>casting votes is gasless.</span>
11701144
</div>
11711145
</>
11721146
)

src/contexts/MiniAppContext.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { sdk } from '@farcaster/miniapp-sdk'
22
import type { Provider } from 'ethers'
3-
import React, { createContext, useContext, useEffect, useState } from 'react'
3+
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
44

55
export interface FarcasterUser {
66
fid: number
@@ -373,7 +373,7 @@ export const MiniAppProvider: React.FC<{ children: React.ReactNode }> = ({ child
373373
}
374374

375375
// Get Farcaster Ethereum provider
376-
const getFarcasterEthereumProvider = async () => {
376+
const getFarcasterEthereumProvider = useCallback(async () => {
377377
if (!isMiniApp) {
378378
return null
379379
}
@@ -384,7 +384,7 @@ export const MiniAppProvider: React.FC<{ children: React.ReactNode }> = ({ child
384384
console.error('Error getting Farcaster Ethereum provider:', error)
385385
return null
386386
}
387-
}
387+
}, [isMiniApp])
388388

389389
const contextValue: MiniAppContextType = {
390390
// State

src/contexts/process-context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ type ProcessProviderProps = PropsWithChildren<{ process: Process }>
3333
const upfetch = up(fetch)
3434

3535
export const ProcessProvider: FC<ProcessProviderProps> = ({ children, process }) => {
36-
const api = useVocdoniApi()
36+
const { api } = useVocdoniApi()
3737
const { address } = useUnifiedWallet()
3838
const censusRoot = process.process.census.censusRoot
3939

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,82 @@
1-
import { VocdoniApiService } from '@vocdoni/davinci-sdk'
2-
import { createContext, useContext, useMemo, type FC, type ReactNode } from 'react'
1+
import { DavinciSDK, VocdoniApiService } from '@vocdoni/davinci-sdk'
2+
import { BrowserProvider, type Eip1193Provider } from 'ethers'
3+
import { createContext, useContext, useEffect, useMemo, useState, type FC, type ReactNode } from 'react'
4+
import { useUnifiedProvider } from '~hooks/use-unified-provider'
5+
import { useUnifiedWallet } from '~hooks/use-unified-wallet'
36

47
interface VocdoniApiContextValue {
58
api: VocdoniApiService
9+
sdk: DavinciSDK | null
610
}
711

812
// Creamos el contexto
913
const VocdoniApiContext = createContext<VocdoniApiContextValue | undefined>(undefined)
1014

1115
// Provider que inicializa la instancia de la API
1216
export const VocdoniApiProvider: FC<{ children: ReactNode }> = ({ children }) => {
17+
const { address, isConnected } = useUnifiedWallet()
18+
const { getProvider } = useUnifiedProvider()
19+
20+
// Old API instance (backwards compatible)
1321
const apiInstance = useMemo(() => {
1422
return new VocdoniApiService({
1523
sequencerURL: import.meta.env.SEQUENCER_URL,
1624
censusURL: import.meta.env.SEQUENCER_URL,
1725
})
1826
}, [])
1927

20-
return <VocdoniApiContext.Provider value={{ api: apiInstance }}>{children}</VocdoniApiContext.Provider>
28+
// New SDK instance with dynamic signer
29+
const [sdkInstance, setSdkInstance] = useState<DavinciSDK | null>(null)
30+
31+
// Re-initialize SDK when wallet connection changes
32+
useEffect(() => {
33+
const initializeSdk = async () => {
34+
try {
35+
if (isConnected && address) {
36+
// Get wallet provider and create signer
37+
const walletProvider = await getProvider()
38+
if (walletProvider) {
39+
const provider = new BrowserProvider(walletProvider as Eip1193Provider)
40+
const signer = await provider.getSigner()
41+
42+
// Initialize SDK with signer
43+
const sdk = new DavinciSDK({
44+
signer,
45+
sequencerUrl: import.meta.env.SEQUENCER_URL,
46+
censusUrl: import.meta.env.SEQUENCER_URL,
47+
useSequencerAddresses: true,
48+
})
49+
50+
await sdk.init()
51+
setSdkInstance(sdk)
52+
console.info('✅ DavinciSDK initialized with signer:', address)
53+
}
54+
} else {
55+
// Cannot initialize the sdk without a signer
56+
const sdk = null
57+
58+
setSdkInstance(sdk)
59+
console.info('ℹ️ No signer available to initialize DavinciSDK')
60+
}
61+
} catch (error) {
62+
console.error('Failed to initialize DavinciSDK:', error)
63+
setSdkInstance(null)
64+
}
65+
}
66+
67+
initializeSdk()
68+
}, [address, isConnected, getProvider])
69+
70+
return (
71+
<VocdoniApiContext.Provider value={{ api: apiInstance, sdk: sdkInstance }}>{children}</VocdoniApiContext.Provider>
72+
)
2173
}
2274

2375
// Hook para acceder al contexto
24-
export const useVocdoniApi = (): VocdoniApiService => {
76+
export const useVocdoniApi = () => {
2577
const context = useContext(VocdoniApiContext)
2678
if (!context) {
2779
throw new Error('useVocdoniApi must be used within a VocdoniApiProvider')
2880
}
29-
return context.api
81+
return context
3082
}

src/hooks/use-process-query.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { useQuery } from '@tanstack/react-query'
2-
import type { ElectionMetadata } from '@vocdoni/davinci-sdk'
2+
import type { ElectionMetadata, VocdoniApiService } from '@vocdoni/davinci-sdk'
33
import { useEffect } from 'react'
44
import { up } from 'up-fetch'
55
import { useVocdoniApi } from '~contexts/vocdoni-api-context'
66
import type { Process } from '~src/types'
77

88
const upfetch = up(fetch)
99

10-
export const getProcessQuery = (id: string, api: ReturnType<typeof useVocdoniApi>) => ({
10+
export const getProcessQuery = (id: string, api: VocdoniApiService) => ({
1111
queryKey: ['process', id],
1212
queryFn: async (): Promise<Process> => {
1313
const process = await api.sequencer.getProcess(id)
@@ -27,7 +27,7 @@ export const getProcessQuery = (id: string, api: ReturnType<typeof useVocdoniApi
2727
})
2828

2929
export const useProcessQuery = (id: string) => {
30-
const api = useVocdoniApi()
30+
const { api } = useVocdoniApi()
3131
const query = useQuery(getProcessQuery(id, api))
3232

3333
const isAcceptingVotes = query.data?.process.isAcceptingVotes
@@ -58,7 +58,7 @@ export const useProcessQuery = (id: string) => {
5858
* This hook will directly return `string[]`, not the wrapped object.
5959
*/
6060
export const useProcessList = () => {
61-
const api = useVocdoniApi()
61+
const { api } = useVocdoniApi()
6262

6363
return useQuery<string[]>({
6464
queryKey: ['processList'],

0 commit comments

Comments
 (0)