diff --git a/CHANGELOG.md b/CHANGELOG.md index 46e682354..dfa49f1e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ changes. ### Fixed -- +- Fix certificates order where vote delegation cert is before the DRep registration cert [Issue 2333](https://github.com/IntersectMBO/govtool/issues/2333) ### Changed diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 596740a7a..db5b6e76e 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -184,6 +184,7 @@ interface CardanoContextType { setStakeKey: (key: string) => void; stakeKeys: string[]; walletApi?: CardanoApiWallet; + registeredStakeKeysListState: string[]; buildSignSubmitConwayCertTx: ({ certBuilder, govActionBuilder, @@ -192,8 +193,9 @@ interface CardanoContextType { votingBuilder, voter, }: BuildSignSubmitConwayCertTxArgs) => Promise; + buildStakeKeyRegCert: () => Promise; buildDRepRegCert: (url?: string, hash?: string) => Promise; - buildVoteDelegationCert: (vote: string) => Promise; + buildVoteDelegationCert: (vote: string) => Promise; buildDRepUpdateCert: (url?: string, hash?: string) => Promise; buildDRepRetirementCert: (voterDeposit: string) => Promise; buildVote: ( @@ -626,28 +628,34 @@ const CardanoProvider = (props: Props) => { [isPendingTransaction, stakeKey, updateTransaction, walletApi, walletState], ); + const buildStakeKeyRegCert = useCallback(async (): Promise => { + try { + if (!stakeKey) { + throw new Error(t("errors.noStakeKeySelected")); + } + const stakeKeyHash = Ed25519KeyHash.from_hex(stakeKey.substring(2)); + const stakeCred = Credential.from_keyhash(stakeKeyHash); + const stakeKeyRegCert = StakeRegistration.new_with_explicit_deposit( + stakeCred, + BigNum.from_str(`${epochParams.key_deposit}`), + ); + return Certificate.new_stake_registration(stakeKeyRegCert); + } catch (e) { + console.error(e); + throw e; + } + }, [epochParams]); + const buildVoteDelegationCert = useCallback( - async (target: string): Promise => { + async (target: string): Promise => { try { // Build Vote Delegation Certificate - const certBuilder = CertificatesBuilder.new(); - let stakeCred; if (!stakeKey) { throw new Error(t("errors.noStakeKeySelected")); } // Remove network tag from stake key hash const stakeKeyHash = Ed25519KeyHash.from_hex(stakeKey.substring(2)); - // if chosen stake key is registered use it, else register it - if (registeredStakeKeysListState.length > 0) { - stakeCred = Credential.from_keyhash(stakeKeyHash); - } else { - stakeCred = Credential.from_keyhash(stakeKeyHash); - const stakeKeyRegCert = StakeRegistration.new_with_explicit_deposit( - stakeCred, - BigNum.from_str(`${epochParams.key_deposit}`), - ); - certBuilder.add(Certificate.new_stake_registration(stakeKeyRegCert)); - } + const stakeCred = Credential.from_keyhash(stakeKeyHash); // Create correct DRep let targetDRep; @@ -665,9 +673,7 @@ const CardanoProvider = (props: Props) => { // Create cert object const voteDelegationCert = VoteDelegation.new(stakeCred, targetDRep); // add cert to tbuilder - certBuilder.add(Certificate.new_vote_delegation(voteDelegationCert)); - - return certBuilder; + return Certificate.new_vote_delegation(voteDelegationCert); } catch (e) { console.error(e); throw e; @@ -1039,24 +1045,26 @@ const CardanoProvider = (props: Props) => { buildDRepRegCert, buildDRepRetirementCert, buildDRepUpdateCert, + buildHardForkGovernanceAction, buildNewInfoGovernanceAction, + buildProtocolParameterChangeGovernanceAction, buildSignSubmitConwayCertTx, + buildStakeKeyRegCert, buildTreasuryGovernanceAction, - buildProtocolParameterChangeGovernanceAction, - buildHardForkGovernanceAction, buildVote, buildVoteDelegationCert, - disconnectWallet, - getChangeAddress, dRepID, + disconnectWallet, enable, error, - isEnabled, + getChangeAddress, isEnableLoading, + isEnabled, isMainnet, isPendingTransaction, pendingTransaction, pubDRepKey, + registeredStakeKeysListState, setStakeKey, stakeKey, stakeKeys, @@ -1067,24 +1075,26 @@ const CardanoProvider = (props: Props) => { buildDRepRegCert, buildDRepRetirementCert, buildDRepUpdateCert, + buildHardForkGovernanceAction, buildNewInfoGovernanceAction, + buildProtocolParameterChangeGovernanceAction, buildSignSubmitConwayCertTx, + buildStakeKeyRegCert, buildTreasuryGovernanceAction, - buildProtocolParameterChangeGovernanceAction, - buildHardForkGovernanceAction, buildVote, buildVoteDelegationCert, - disconnectWallet, - getChangeAddress, dRepID, + disconnectWallet, enable, error, - isEnabled, + getChangeAddress, isEnableLoading, + isEnabled, isMainnet, isPendingTransaction, pendingTransaction, pubDRepKey, + registeredStakeKeysListState, setStakeKey, stakeKey, stakeKeys, diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index 581169316..4d092afbe 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -5,6 +5,7 @@ import { useFormContext } from "react-hook-form"; import { blake2bHex } from "blakejs"; import * as Sentry from "@sentry/react"; import { NodeObject } from "jsonld"; +import { CertificatesBuilder } from "@emurgo/cardano-serialization-lib-asmjs"; import { CIP_119, @@ -53,6 +54,8 @@ export const useRegisterAsdRepForm = ( buildSignSubmitConwayCertTx, buildVoteDelegationCert, dRepID, + registeredStakeKeysListState, + buildStakeKeyRegCert, } = useCardano(); // App Management @@ -131,7 +134,7 @@ export const useRegisterAsdRepForm = ( if (!hash) return; const uri = data.storingURL; try { - const certBuilder = await buildVoteDelegationCert(dRepID); + const certBuilder = CertificatesBuilder.new(); const registerCert = voter?.isRegisteredAsSoleVoter ? await buildDRepUpdateCert(uri, hash) @@ -139,6 +142,14 @@ export const useRegisterAsdRepForm = ( certBuilder.add(registerCert); + if (!registeredStakeKeysListState.length) { + const stakeKeyRegCert = await buildStakeKeyRegCert(); + certBuilder.add(stakeKeyRegCert); + } + + const voteDelegationCert = await buildVoteDelegationCert(dRepID); + certBuilder.add(voteDelegationCert); + return certBuilder; } catch (error) { Sentry.setTag("hook", "useRegisterAsdRepForm"); diff --git a/govtool/frontend/src/hooks/useDelegateToDrep.ts b/govtool/frontend/src/hooks/useDelegateToDrep.ts index 963c782ec..31509d14d 100644 --- a/govtool/frontend/src/hooks/useDelegateToDrep.ts +++ b/govtool/frontend/src/hooks/useDelegateToDrep.ts @@ -3,12 +3,15 @@ import * as Sentry from "@sentry/react"; import { useCardano, useSnackbar } from "@context"; import { useGetVoterInfo, useTranslation, useWalletErrorModal } from "@hooks"; +import { CertificatesBuilder } from "@emurgo/cardano-serialization-lib-asmjs"; export const useDelegateTodRep = () => { const { buildSignSubmitConwayCertTx, buildVoteDelegationCert, buildDRepRetirementCert, + buildStakeKeyRegCert, + registeredStakeKeysListState, } = useCardano(); const { t } = useTranslation(); const { addSuccessAlert, addErrorAlert } = useSnackbar(); @@ -25,14 +28,23 @@ export const useDelegateTodRep = () => { if (voter?.isRegisteredAsSoleVoter && !voter?.deposit) { throw new Error(t("errors.appCannotGetDeposit")); } + const certBuilder = CertificatesBuilder.new(); - const certBuilder = await buildVoteDelegationCert(dRepId); if (voter?.isRegisteredAsSoleVoter) { const retirementCert = await buildDRepRetirementCert( voter?.deposit?.toString(), ); certBuilder.add(retirementCert); } + + if (!registeredStakeKeysListState.length) { + const stakeKeyRegCert = await buildStakeKeyRegCert(); + certBuilder.add(stakeKeyRegCert); + } + + const voteDelegationCert = await buildVoteDelegationCert(dRepId); + certBuilder.add(voteDelegationCert); + await buildSignSubmitConwayCertTx({ certBuilder, type: "delegate", diff --git a/govtool/frontend/src/pages/RegisterAsDirectVoter.tsx b/govtool/frontend/src/pages/RegisterAsDirectVoter.tsx index 53e50233c..70df1f78f 100644 --- a/govtool/frontend/src/pages/RegisterAsDirectVoter.tsx +++ b/govtool/frontend/src/pages/RegisterAsDirectVoter.tsx @@ -21,6 +21,7 @@ import { openInNewTab, } from "@utils"; import { WrongRouteInfo } from "@organisms"; +import { CertificatesBuilder } from "@emurgo/cardano-serialization-lib-asmjs"; export const RegisterAsDirectVoter = () => { const { cExplorerBaseUrl } = useAppContext(); @@ -32,11 +33,13 @@ export const RegisterAsDirectVoter = () => { const { voter } = useGetVoterInfo(); const openWalletErrorModal = useWalletErrorModal(); const { + buildStakeKeyRegCert, buildSignSubmitConwayCertTx, buildDRepRegCert, buildDRepUpdateCert, buildVoteDelegationCert, dRepID, + registeredStakeKeysListState, } = useCardano(); const { openModal, closeModal } = useModal(); @@ -44,11 +47,21 @@ export const RegisterAsDirectVoter = () => { setIsLoading(true); try { - const certBuilder = await buildVoteDelegationCert(dRepID); + const certBuilder = CertificatesBuilder.new(); + const registerCert = voter?.isRegisteredAsDRep ? await buildDRepUpdateCert() : await buildDRepRegCert(); certBuilder.add(registerCert); + + if (!registeredStakeKeysListState.length) { + const stakeKeyRegCert = await buildStakeKeyRegCert(); + certBuilder.add(stakeKeyRegCert); + } + + const voteDelegationCert = await buildVoteDelegationCert(dRepID); + certBuilder.add(voteDelegationCert); + const result = await buildSignSubmitConwayCertTx({ certBuilder, type: "registerAsDirectVoter",