diff --git a/.github/workflows/e2e-tests-linux.yml b/.github/workflows/e2e-tests-linux.yml
index 71eec19cf4..47f32d5fc3 100644
--- a/.github/workflows/e2e-tests-linux.yml
+++ b/.github/workflows/e2e-tests-linux.yml
@@ -50,6 +50,9 @@ jobs:
uses: ./.github/shared/build
with:
LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }}
+ CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }}
+ CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }}
+ CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }}
- name: Start XVFB
run: |
Xvfb :99 &
diff --git a/.github/workflows/smoke-tests.yml b/.github/workflows/smoke-tests.yml
index 5c2526ad4b..5066293a93 100644
--- a/.github/workflows/smoke-tests.yml
+++ b/.github/workflows/smoke-tests.yml
@@ -26,6 +26,9 @@ jobs:
uses: ./.github/shared/build
with:
LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }}
+ CARDANO_SERVICES_URL_MAINNET: ${{ secrets.CARDANO_SERVICES_DEV_URL_MAINNET }}
+ CARDANO_SERVICES_URL_PREPROD: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREPROD }}
+ CARDANO_SERVICES_URL_PREVIEW: ${{ secrets.CARDANO_SERVICES_DEV_URL_PREVIEW }}
- name: Start XVFB
run: |
Xvfb :99 &
diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults
index fd8968c01a..0ebb89d0bc 100644
--- a/apps/browser-extension-wallet/.env.defaults
+++ b/apps/browser-extension-wallet/.env.defaults
@@ -4,7 +4,7 @@ DEFAULT_CHAIN=Mainnet
WALLET_SYNC_TIMEOUT_IN_SEC=60
WALLET_INTERVAL_IN_SEC=30
DROP_CONSOLE_IN_PRODUCTION=false
-AVAILABLE_CHAINS=Preprod,Preview,Mainnet
+AVAILABLE_CHAINS=Preprod,Preview,Mainnet,Sanchonet
ADA_PRICE_POLLING_IN_SEC=60
TOKEN_PRICE_POLLING_IN_SEC=300
SAVED_PRICE_DURATION_IN_MINUTES=720
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx
index e17e48769d..03980ee8bb 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable no-console */
import React, { useMemo, useState } from 'react';
import cn from 'classnames';
import { Button, PostHogAction } from '@lace/common';
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx
index 03fea860f4..65379c1058 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransactionContent.tsx
@@ -1,8 +1,10 @@
+/* eslint-disable complexity */
import React from 'react';
import { Skeleton } from 'antd';
+import { Wallet } from '@lace/cardano';
+import { SignTxData } from './types';
import { ConfirmDRepRegistrationContainer } from './ConfirmDRepRegistrationContainer';
import { DappTransactionContainer } from './DappTransactionContainer';
-import { SignTxData } from './types';
import { ConfirmDRepRetirementContainer } from './ConfirmDRepRetirementContainer';
import { ConfirmVoteDelegationContainer } from './ConfirmVoteDelegationContainer';
import { VotingProceduresContainer } from './VotingProceduresContainer';
@@ -11,7 +13,7 @@ import { ConfirmVoteRegistrationDelegationContainer } from './ConfirmVoteRegistr
import { ConfirmStakeRegistrationDelegationContainer } from './ConfirmStakeRegistrationDelegationContainer';
import { ConfirmStakeVoteRegistrationDelegationContainer } from './ConfirmStakeVoteRegistrationDelegationContainer';
import { ConfirmStakeVoteDelegationContainer } from './ConfirmStakeVoteDelegationContainer';
-import { Wallet } from '@lace/cardano';
+import { ProposalProceduresContainer } from './ProposalProceduresContainer';
interface Props {
txType?: Wallet.Cip30TxType;
@@ -51,6 +53,9 @@ export const ConfirmTransactionContent = ({ txType, signTxData, onError, errorMe
if (txType === Wallet.Cip30TxType.StakeVoteDelegation) {
return ;
}
+ if (txType === Wallet.Cip30TxType.ProposalProcedures) {
+ return ;
+ }
return ;
};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx
new file mode 100644
index 0000000000..7c3df2b028
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ProposalProceduresContainer.tsx
@@ -0,0 +1,86 @@
+/* eslint-disable consistent-return */
+/* eslint-disable complexity */
+import React, { useMemo } from 'react';
+import { Wallet } from '@lace/cardano';
+import { SignTxData } from './types';
+import { proposalProceduresInspector } from './utils';
+import { HardForkInitiationActionContainer } from './proposal-procedures/HardForkInitiationActionContainer';
+import { InfoActionContainer } from './proposal-procedures/InfoActionContainer';
+import { NewConstitutionActionContainer } from './proposal-procedures/NewConstitutionActionContainer';
+import { NoConfidenceActionContainer } from './proposal-procedures/NoConfidenceActionContainer';
+import { ParameterChangeActionContainer } from './proposal-procedures/ParameterChangeActionContainer';
+import { TreasuryWithdrawalsActionContainer } from './proposal-procedures/TreasuryWithdrawalsActionContainer';
+import { UpdateCommitteeActionContainer } from './proposal-procedures/UpdateCommitteeActionContainer';
+
+interface Props {
+ signTxData: SignTxData;
+ errorMessage?: string;
+}
+
+export const ProposalProceduresContainer = ({
+ signTxData: { dappInfo, tx },
+ errorMessage
+}: Props): React.ReactElement => {
+ const proposalProcedures = proposalProceduresInspector(tx);
+
+ const props = useMemo(() => ({ dappInfo, errorMessage }), [dappInfo, errorMessage]);
+
+ return (
+ <>
+ {proposalProcedures.map(({ deposit, rewardAccount, anchor, governanceAction }) => {
+ const key = `${governanceAction.__typename}_${anchor.dataHash}`;
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action) {
+ return (
+
+ );
+ }
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.info_action) {
+ return ;
+ }
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.new_constitution) {
+ return (
+
+ );
+ }
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.no_confidence) {
+ return (
+
+ );
+ }
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.parameter_change_action) {
+ return (
+
+ );
+ }
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action) {
+ return (
+
+ );
+ }
+ if (governanceAction.__typename === Wallet.Cardano.GovernanceActionType.update_committee) {
+ return (
+
+ );
+ }
+ })}
+ >
+ );
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx
index 0ca9fc967d..8ebeefc4da 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/VotingProceduresContainer.tsx
@@ -1,11 +1,10 @@
-import React, { useMemo } from 'react';
+import React from 'react';
import { useTranslation } from 'react-i18next';
import { VotingProcedures } from '@lace/core';
import { SignTxData } from './types';
import { drepIDasBech32FromHash, votingProceduresInspector } from './utils';
import { Wallet } from '@lace/cardano';
-import { useWalletStore } from '@src/stores';
-import { config } from '@src/config';
+import { useCExpolorerBaseUrl } from './hooks';
interface Props {
signTxData: SignTxData;
@@ -53,13 +52,8 @@ export const getVote = (vote: Wallet.Cardano.Vote): Votes => {
export const VotingProceduresContainer = ({ signTxData, errorMessage }: Props): React.ReactElement => {
const { t } = useTranslation();
const votingProcedures = votingProceduresInspector(signTxData.tx);
- const { environmentName } = useWalletStore();
- const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config();
- const explorerBaseUrl = useMemo(
- () => (environmentName === 'Sanchonet' ? '' : `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`),
- [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName]
- );
+ const explorerBaseUrl = useCExpolorerBaseUrl();
return (
{
};
});
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- );
-
describe('Testing ConfirmDRepRegistrationContainer component', () => {
beforeEach(() => {
mockUseWalletStore.mockReset();
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx
index 570bbdb16e..c7bdc6d5ae 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx
@@ -21,24 +21,12 @@ import * as React from 'react';
import { cleanup, render } from '@testing-library/react';
import { ConfirmDRepRetirementContainer } from '../ConfirmDRepRetirementContainer';
import '@testing-library/jest-dom';
-import { I18nextProvider } from 'react-i18next';
-import { StoreProvider } from '@src/stores';
-import {
- AnalyticsProvider,
- AppSettingsProvider,
- BackgroundServiceAPIProvider,
- BackgroundServiceAPIProviderProps,
- DatabaseProvider
-} from '@src/providers';
-import { APP_MODE_BROWSER } from '@src/utils/constants';
-import i18n from '@lib/i18n';
import { BehaviorSubject } from 'rxjs';
import { act } from 'react-dom/test-utils';
-import { PostHogClientProvider } from '@providers/PostHogClientProvider';
-import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
import { buildMockTx } from '@src/utils/mocks/tx';
import { Wallet } from '@lace/cardano';
import BigNumber from 'bignumber.js';
+import { getWrapper } from '../testing.utils';
const LOVELACE_VALUE = 1_000_000;
const DEFAULT_DECIMALS = 2;
@@ -114,30 +102,6 @@ jest.mock('react-i18next', () => {
};
});
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- );
-
describe('Testing ConfirmDRepRetirementContainer component', () => {
beforeEach(() => {
mockUseWalletStore.mockReset();
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx
index eba21c3337..62aaaec965 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepUpdateContainer.test.tsx
@@ -9,23 +9,11 @@ import * as React from 'react';
import { cleanup, render } from '@testing-library/react';
import { ConfirmDRepUpdateContainer } from '../ConfirmDRepUpdateContainer';
import '@testing-library/jest-dom';
-import { I18nextProvider } from 'react-i18next';
-import { StoreProvider } from '@src/stores';
-import {
- AnalyticsProvider,
- AppSettingsProvider,
- BackgroundServiceAPIProvider,
- BackgroundServiceAPIProviderProps,
- DatabaseProvider
-} from '@src/providers';
-import { APP_MODE_BROWSER } from '@src/utils/constants';
-import i18n from '@lib/i18n';
import { BehaviorSubject } from 'rxjs';
import { act } from 'react-dom/test-utils';
-import { PostHogClientProvider } from '@providers/PostHogClientProvider';
-import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
import { buildMockTx } from '@src/utils/mocks/tx';
import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
const { Cardano, Crypto, HexBlob } = Wallet;
@@ -68,30 +56,6 @@ jest.mock('react-i18next', () => {
};
});
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- );
-
describe('Testing ConfirmDRepUpdateContainer component', () => {
beforeEach(() => {
mockUseWalletStore.mockReset();
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx
new file mode 100644
index 0000000000..f8252136c2
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeRegistrationDelegationContainer.test.tsx
@@ -0,0 +1,149 @@
+/* eslint-disable unicorn/no-null */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable import/imports-first */
+const mockUseWalletStore = jest.fn();
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockConfirmStakeRegistrationDelegation = jest.fn();
+const mockLovelacesToAdaString = jest.fn();
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import { ConfirmStakeRegistrationDelegationContainer } from '../ConfirmStakeRegistrationDelegationContainer';
+import '@testing-library/jest-dom';
+import { BehaviorSubject } from 'rxjs';
+import { act } from 'react-dom/test-utils';
+import { buildMockTx } from '@src/utils/mocks/tx';
+import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
+
+const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT);
+
+const assetInfo$ = new BehaviorSubject(new Map());
+const available$ = new BehaviorSubject([]);
+
+const inMemoryWallet = {
+ assetInfo$,
+ balance: {
+ utxo: {
+ available$
+ }
+ }
+};
+
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmStakeRegistrationDelegation: mockConfirmStakeRegistrationDelegation
+ };
+});
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+describe('Testing ConfirmStakeRegistrationDelegationContainer component', () => {
+ beforeEach(() => {
+ mockUseWalletStore.mockReset();
+ mockUseWalletStore.mockImplementation(() => ({
+ inMemoryWallet,
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+ }));
+ mockConfirmStakeRegistrationDelegation.mockReset();
+ mockConfirmStakeRegistrationDelegation.mockReturnValue();
+ mockUseTranslation.mockReset();
+ mockUseTranslation.mockImplementation(() => ({ t }));
+ mockLovelacesToAdaString.mockReset();
+ mockLovelacesToAdaString.mockImplementation((val) => val);
+ });
+
+ afterEach(() => {
+ jest.resetModules();
+ jest.resetAllMocks();
+ cleanup();
+ });
+
+ test('should render ConfirmStakeRegistrationDelegation component with proper props', async () => {
+ let queryByTestId: any;
+
+ const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+ };
+ const certificate: Wallet.Cardano.Certificate = {
+ __typename: Wallet.Cardano.CertificateType.StakeRegistrationDelegation,
+ poolId: Wallet.Cardano.PoolId('pool126zlx7728y7xs08s8epg9qp393kyafy9rzr89g4qkvv4cv93zem'),
+ stakeCredential: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH)
+ },
+ deposit: BigInt('100000')
+ };
+ const tx = buildMockTx({
+ certificates: [certificate]
+ });
+ const errorMessage = 'errorMessage';
+ const props = { signTxData: { dappInfo, tx }, errorMessage };
+
+ await act(async () => {
+ ({ queryByTestId } = render(, {
+ wrapper: getWrapper()
+ }));
+ });
+
+ expect(queryByTestId('ConfirmStakeRegistrationDelegation')).toBeInTheDocument();
+ expect(mockConfirmStakeRegistrationDelegation).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ metadata: {
+ poolId: certificate.poolId,
+ stakeKeyHash: certificate.stakeCredential.hash,
+ depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`
+ },
+ translations: {
+ metadata: t('core.StakeRegistrationDelegation.metadata'),
+ labels: {
+ poolId: t('core.StakeRegistrationDelegation.poolId'),
+ stakeKeyHash: t('core.StakeRegistrationDelegation.stakeKeyHash'),
+ depositPaid: t('core.StakeRegistrationDelegation.depositPaid')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx
new file mode 100644
index 0000000000..ccd3dba70f
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteDelegationContainer.test.tsx
@@ -0,0 +1,168 @@
+/* eslint-disable unicorn/no-null */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable import/imports-first */
+const mockUseWalletStore = jest.fn();
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockConfirmStakeVoteDelegation = jest.fn();
+const mockIsDRepAlwaysAbstain = jest.fn();
+const mockIsDRepAlwaysNoConfidence = jest.fn();
+const mockIsDRepCredential = jest.fn();
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import { ConfirmStakeVoteDelegationContainer } from '../ConfirmStakeVoteDelegationContainer';
+import '@testing-library/jest-dom';
+import { BehaviorSubject } from 'rxjs';
+import { act } from 'react-dom/test-utils';
+import { buildMockTx } from '@src/utils/mocks/tx';
+import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
+
+const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT);
+
+const assetInfo$ = new BehaviorSubject(new Map());
+const available$ = new BehaviorSubject([]);
+
+const inMemoryWallet = {
+ assetInfo$,
+ balance: {
+ utxo: {
+ available$
+ }
+ }
+};
+
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmStakeVoteDelegation: mockConfirmStakeVoteDelegation
+ };
+});
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ Cardano: {
+ ...actual.Wallet.Cardano,
+ isDRepAlwaysAbstain: mockIsDRepAlwaysAbstain,
+ isDRepAlwaysNoConfidence: mockIsDRepAlwaysNoConfidence,
+ isDRepCredential: mockIsDRepCredential
+ }
+ }
+ };
+});
+
+const isDRepAlwaysAbstainMocked = 'isDRepAlwaysAbstainMocked';
+const isDRepAlwaysNoConfidenceMocked = 'isDRepAlwaysNoConfidenceMocked';
+
+describe('Testing ConfirmStakeVoteDelegationContainer component', () => {
+ beforeEach(() => {
+ mockUseWalletStore.mockReset();
+ mockUseWalletStore.mockImplementation(() => ({
+ inMemoryWallet,
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+ }));
+ mockConfirmStakeVoteDelegation.mockReset();
+ mockConfirmStakeVoteDelegation.mockReturnValue();
+ mockUseTranslation.mockReset();
+ mockUseTranslation.mockImplementation(() => ({ t }));
+ mockIsDRepAlwaysAbstain.mockReset();
+ mockIsDRepAlwaysAbstain.mockImplementation(() => isDRepAlwaysAbstainMocked);
+ mockIsDRepAlwaysNoConfidence.mockReset();
+ mockIsDRepAlwaysNoConfidence.mockImplementation(() => isDRepAlwaysNoConfidenceMocked);
+ mockIsDRepCredential.mockReset();
+ mockIsDRepCredential.mockImplementation(() => true);
+ });
+
+ afterEach(() => {
+ jest.resetModules();
+ jest.resetAllMocks();
+ cleanup();
+ });
+
+ test('should render ConfirmStakeVoteDelegation component with proper props', async () => {
+ let queryByTestId: any;
+
+ const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+ };
+ const certificate: Wallet.Cardano.Certificate = {
+ __typename: Wallet.Cardano.CertificateType.StakeVoteDelegation,
+ poolId: Wallet.Cardano.PoolId('pool126zlx7728y7xs08s8epg9qp393kyafy9rzr89g4qkvv4cv93zem'),
+ stakeCredential: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH)
+ },
+ dRep: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex'))
+ }
+ };
+ const tx = buildMockTx({
+ certificates: [certificate]
+ });
+ const errorMessage = 'errorMessage';
+ const props = { signTxData: { dappInfo, tx }, errorMessage };
+
+ await act(async () => {
+ ({ queryByTestId } = render(, {
+ wrapper: getWrapper()
+ }));
+ });
+
+ expect(queryByTestId('ConfirmStakeVoteDelegation')).toBeInTheDocument();
+ expect(mockConfirmStakeVoteDelegation).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ metadata: {
+ poolId: certificate.poolId,
+ stakeKeyHash: certificate.stakeCredential.hash,
+ alwaysAbstain: isDRepAlwaysAbstainMocked,
+ alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked,
+ drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString()
+ },
+ translations: {
+ metadata: t('core.StakeVoteDelegation.metadata'),
+ option: t('core.StakeVoteDelegation.option'),
+ labels: {
+ poolId: t('core.StakeVoteDelegation.poolId'),
+ stakeKeyHash: t('core.StakeVoteDelegation.stakeKeyHash'),
+ drepId: t('core.StakeVoteDelegation.drepId'),
+ alwaysAbstain: t('core.StakeVoteDelegation.alwaysAbstain'),
+ alwaysNoConfidence: t('core.StakeVoteDelegation.alwaysNoConfidence')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx
new file mode 100644
index 0000000000..37aa8cb203
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmStakeVoteRegistrationDelegationContainer.test.tsx
@@ -0,0 +1,180 @@
+/* eslint-disable unicorn/no-null */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable import/imports-first */
+const mockUseWalletStore = jest.fn();
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockConfirmStakeVoteRegistrationDelegation = jest.fn();
+const mockIsDRepAlwaysAbstain = jest.fn();
+const mockIsDRepAlwaysNoConfidence = jest.fn();
+const mockIsDRepCredential = jest.fn();
+const mockLovelacesToAdaString = jest.fn();
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import { ConfirmStakeVoteRegistrationDelegationContainer } from '../ConfirmStakeVoteRegistrationDelegationContainer';
+import '@testing-library/jest-dom';
+import { BehaviorSubject } from 'rxjs';
+import { act } from 'react-dom/test-utils';
+import { buildMockTx } from '@src/utils/mocks/tx';
+import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
+
+const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT);
+
+const assetInfo$ = new BehaviorSubject(new Map());
+const available$ = new BehaviorSubject([]);
+
+const inMemoryWallet = {
+ assetInfo$,
+ balance: {
+ utxo: {
+ available$
+ }
+ }
+};
+
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmStakeVoteRegistrationDelegation: mockConfirmStakeVoteRegistrationDelegation
+ };
+});
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ },
+ Cardano: {
+ ...actual.Wallet.Cardano,
+ isDRepAlwaysAbstain: mockIsDRepAlwaysAbstain,
+ isDRepAlwaysNoConfidence: mockIsDRepAlwaysNoConfidence,
+ isDRepCredential: mockIsDRepCredential
+ }
+ }
+ };
+});
+
+const isDRepAlwaysAbstainMocked = 'isDRepAlwaysAbstainMocked';
+const isDRepAlwaysNoConfidenceMocked = 'isDRepAlwaysNoConfidenceMocked';
+
+describe('Testing ConfirmStakeVoteRegistrationDelegationContainer component', () => {
+ beforeEach(() => {
+ mockUseWalletStore.mockReset();
+ mockUseWalletStore.mockImplementation(() => ({
+ inMemoryWallet,
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+ }));
+ mockConfirmStakeVoteRegistrationDelegation.mockReset();
+ mockConfirmStakeVoteRegistrationDelegation.mockReturnValue(
+
+ );
+ mockUseTranslation.mockReset();
+ mockUseTranslation.mockImplementation(() => ({ t }));
+ mockLovelacesToAdaString.mockReset();
+ mockLovelacesToAdaString.mockImplementation((val) => val);
+ mockIsDRepAlwaysAbstain.mockReset();
+ mockIsDRepAlwaysAbstain.mockImplementation(() => isDRepAlwaysAbstainMocked);
+ mockIsDRepAlwaysNoConfidence.mockReset();
+ mockIsDRepAlwaysNoConfidence.mockImplementation(() => isDRepAlwaysNoConfidenceMocked);
+ mockIsDRepCredential.mockReset();
+ mockIsDRepCredential.mockImplementation(() => true);
+ });
+
+ afterEach(() => {
+ jest.resetModules();
+ jest.resetAllMocks();
+ cleanup();
+ });
+
+ test('should render ConfirmStakeVoteRegistrationDelegation component with proper props', async () => {
+ let queryByTestId: any;
+
+ const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+ };
+ const certificate: Wallet.Cardano.Certificate = {
+ __typename: Wallet.Cardano.CertificateType.StakeVoteRegistrationDelegation,
+ poolId: Wallet.Cardano.PoolId('pool126zlx7728y7xs08s8epg9qp393kyafy9rzr89g4qkvv4cv93zem'),
+ stakeCredential: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH)
+ },
+ dRep: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex'))
+ },
+ deposit: BigInt('100000')
+ };
+ const tx = buildMockTx({
+ certificates: [certificate]
+ });
+ const errorMessage = 'errorMessage';
+ const props = { signTxData: { dappInfo, tx }, errorMessage };
+
+ await act(async () => {
+ ({ queryByTestId } = render(, {
+ wrapper: getWrapper()
+ }));
+ });
+
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegation')).toBeInTheDocument();
+ expect(mockConfirmStakeVoteRegistrationDelegation).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ metadata: {
+ poolId: certificate.poolId,
+ stakeKeyHash: certificate.stakeCredential.hash,
+ depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`,
+ alwaysAbstain: isDRepAlwaysAbstainMocked,
+ alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked,
+ drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString()
+ },
+ translations: {
+ metadata: t('core.StakeVoteDelegationRegistration.metadata'),
+ option: t('core.StakeVoteDelegationRegistration.option'),
+ labels: {
+ poolId: t('core.StakeVoteDelegationRegistration.poolId'),
+ stakeKeyHash: t('core.StakeVoteDelegationRegistration.stakeKeyHash'),
+ drepId: t('core.StakeVoteDelegationRegistration.drepId'),
+ alwaysAbstain: t('core.StakeVoteDelegationRegistration.alwaysAbstain'),
+ alwaysNoConfidence: t('core.StakeVoteDelegationRegistration.alwaysNoConfidence'),
+ depositPaid: t('core.StakeVoteDelegationRegistration.depositPaid')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx
index 6be8a2a2c9..d8b854952a 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx
@@ -18,23 +18,9 @@ import * as React from 'react';
import { cleanup, render, act, fireEvent } from '@testing-library/react';
import { ConfirmTransaction } from '../ConfirmTransaction';
import '@testing-library/jest-dom';
-import { I18nextProvider } from 'react-i18next';
-import { StoreProvider } from '@src/stores';
-import {
- AnalyticsProvider,
- AppSettingsProvider,
- BackgroundServiceAPIProvider,
- BackgroundServiceAPIProviderProps,
- DatabaseProvider,
- ViewFlowProvider
-} from '@src/providers';
-import { APP_MODE_BROWSER } from '@src/utils/constants';
-import i18n from '@lib/i18n';
import { BehaviorSubject } from 'rxjs';
-import { sendViewsFlowState } from '../../../config';
-import { PostHogClientProvider } from '@providers/PostHogClientProvider';
-import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
const assetInfo$ = new BehaviorSubject(new Map());
const available$ = new BehaviorSubject([]);
@@ -134,32 +120,6 @@ const testIds = {
dappTransactionCancel: 'dapp-transaction-cancel'
};
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
- );
-
describe('Testing ConfirmTransaction component', () => {
window.ResizeObserver = ResizeObserver;
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx
index c2ad9259a2..3386aff6be 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransactionContent.test.tsx
@@ -9,6 +9,19 @@ const mockConfirmDRepRetirementContainer = jest.fn(() => );
const mockConfirmVoteDelegationContainer = jest.fn(() => );
const mockVotingProceduresContainer = jest.fn(() => );
+const mockProposalProceduresContainer = jest.fn(() => );
+const mockConfirmVoteRegistrationDelegationContainer = jest.fn(() => (
+
+));
+const mockConfirmStakeRegistrationDelegationContainer = jest.fn(() => (
+
+));
+const mockConfirmStakeVoteRegistrationDelegationContainer = jest.fn(() => (
+
+));
+const mockConfirmStakeVoteDelegationContainer = jest.fn(() => (
+
+));
const mockDappTransactionContainer = jest.fn(() => );
import * as React from 'react';
import { cleanup, render } from '@testing-library/react';
@@ -71,6 +84,51 @@ jest.mock('../VotingProceduresContainer', () => {
};
});
+jest.mock('../ProposalProceduresContainer', () => {
+ const original = jest.requireActual('../ProposalProceduresContainer');
+ return {
+ __esModule: true,
+ ...original,
+ ProposalProceduresContainer: mockProposalProceduresContainer
+ };
+});
+
+jest.mock('../ConfirmVoteRegistrationDelegationContainer', () => {
+ const original = jest.requireActual('../ConfirmVoteRegistrationDelegationContainer');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmVoteRegistrationDelegationContainer: mockConfirmVoteRegistrationDelegationContainer
+ };
+});
+
+jest.mock('../ConfirmStakeRegistrationDelegationContainer', () => {
+ const original = jest.requireActual('../ConfirmStakeRegistrationDelegationContainer');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmStakeRegistrationDelegationContainer: mockConfirmStakeRegistrationDelegationContainer
+ };
+});
+
+jest.mock('../ConfirmStakeVoteRegistrationDelegationContainer', () => {
+ const original = jest.requireActual('../ConfirmStakeVoteRegistrationDelegationContainer');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmStakeVoteRegistrationDelegationContainer: mockConfirmStakeVoteRegistrationDelegationContainer
+ };
+});
+
+jest.mock('../ConfirmStakeVoteDelegationContainer', () => {
+ const original = jest.requireActual('../ConfirmStakeVoteDelegationContainer');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmStakeVoteDelegationContainer: mockConfirmStakeVoteDelegationContainer
+ };
+});
+
jest.mock('../DappTransactionContainer', () => {
const original = jest.requireActual('../DappTransactionContainer');
return {
@@ -106,6 +164,11 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
expect(mockSkeleton).toHaveBeenLastCalledWith({ loading: true }, {});
});
@@ -125,6 +188,11 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
expect(mockConfirmDRepRegistrationContainer).toHaveBeenLastCalledWith(props, {});
});
@@ -144,6 +212,11 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
expect(mockConfirmDRepRetirementContainer).toHaveBeenLastCalledWith(props, {});
});
@@ -163,6 +236,11 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
expect(mockConfirmDRepUpdateContainer).toHaveBeenLastCalledWith(props, {});
});
@@ -182,6 +260,11 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
expect(mockConfirmVoteDelegationContainer).toHaveBeenLastCalledWith(props, {});
});
@@ -201,10 +284,135 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
expect(mockVotingProceduresContainer).toHaveBeenLastCalledWith(props, {});
});
+ test('should render ProposalProceduresContainer with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+
+ ));
+ });
+
+ expect(queryByTestId('skeleton')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
+ expect(mockProposalProceduresContainer).toHaveBeenLastCalledWith(props, {});
+ });
+
+ test('should render ConfirmVoteRegistrationDelegationContainer with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+
+ ));
+ });
+
+ expect(queryByTestId('skeleton')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
+ expect(mockConfirmVoteRegistrationDelegationContainer).toHaveBeenLastCalledWith(props, {});
+ });
+
+ test('should render ConfirmStakeRegistrationDelegationContainer with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+
+ ));
+ });
+
+ expect(queryByTestId('skeleton')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
+ expect(mockConfirmStakeRegistrationDelegationContainer).toHaveBeenLastCalledWith(props, {});
+ });
+
+ test('should render ConfirmStakeVoteRegistrationDelegationContainer with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+
+ ));
+ });
+
+ expect(queryByTestId('skeleton')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
+ expect(mockConfirmStakeVoteRegistrationDelegationContainer).toHaveBeenLastCalledWith(props, {});
+ });
+
+ test('should render ConfirmStakeVoteDelegationContainer with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+
+ ));
+ });
+
+ expect(queryByTestId('skeleton')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRegistrationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepRetirementContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).toBeInTheDocument();
+ expect(queryByTestId('DappTransactionContainer')).not.toBeInTheDocument();
+ expect(mockConfirmStakeVoteDelegationContainer).toHaveBeenLastCalledWith(props, {});
+ });
+
test('should render DappTransactionContainer with proper props', async () => {
let queryByTestId: any;
@@ -220,6 +428,11 @@ describe('Testing ConfirmTransactionContent component', () => {
expect(queryByTestId('ConfirmDRepUpdateContainer')).not.toBeInTheDocument();
expect(queryByTestId('ConfirmVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('VotingProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ProposalProceduresContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteRegistrationDelegationContainer')).not.toBeInTheDocument();
+ expect(queryByTestId('ConfirmStakeVoteDelegationContainer')).not.toBeInTheDocument();
expect(queryByTestId('DappTransactionContainer')).toBeInTheDocument();
expect(mockDappTransactionContainer).toHaveBeenLastCalledWith(props, {});
});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx
index 3f2c77c6d7..1b4c2bdbe2 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteDelegationContainer.test.tsx
@@ -9,23 +9,11 @@ import * as React from 'react';
import { cleanup, render } from '@testing-library/react';
import { ConfirmVoteDelegationContainer } from '../ConfirmVoteDelegationContainer';
import '@testing-library/jest-dom';
-import { I18nextProvider } from 'react-i18next';
-import { StoreProvider } from '@src/stores';
-import {
- AnalyticsProvider,
- AppSettingsProvider,
- BackgroundServiceAPIProvider,
- BackgroundServiceAPIProviderProps,
- DatabaseProvider
-} from '@src/providers';
-import { APP_MODE_BROWSER } from '@src/utils/constants';
-import i18n from '@lib/i18n';
import { BehaviorSubject } from 'rxjs';
import { act } from 'react-dom/test-utils';
-import { PostHogClientProvider } from '@providers/PostHogClientProvider';
-import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
import { buildMockTx } from '@src/utils/mocks/tx';
import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT);
@@ -69,30 +57,6 @@ jest.mock('react-i18next', () => {
};
});
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- );
-
describe('Testing ConfirmVoteDelegationContainer component', () => {
beforeEach(() => {
mockUseWalletStore.mockReset();
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx
new file mode 100644
index 0000000000..ffc8debe9f
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmVoteRegistrationDelegationContainer.test.tsx
@@ -0,0 +1,175 @@
+/* eslint-disable unicorn/no-null */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable import/imports-first */
+const mockUseWalletStore = jest.fn();
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockConfirmVoteRegistrationDelegation = jest.fn();
+const mockIsDRepAlwaysAbstain = jest.fn();
+const mockIsDRepAlwaysNoConfidence = jest.fn();
+const mockIsDRepCredential = jest.fn();
+const mockLovelacesToAdaString = jest.fn();
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import { ConfirmVoteRegistrationDelegationContainer } from '../ConfirmVoteRegistrationDelegationContainer';
+import '@testing-library/jest-dom';
+import { BehaviorSubject } from 'rxjs';
+import { act } from 'react-dom/test-utils';
+import { buildMockTx } from '@src/utils/mocks/tx';
+import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
+
+const REWARD_ACCOUNT = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const STAKE_KEY_HASH = Wallet.Cardano.RewardAccount.toHash(REWARD_ACCOUNT);
+
+const assetInfo$ = new BehaviorSubject(new Map());
+const available$ = new BehaviorSubject([]);
+
+const inMemoryWallet = {
+ assetInfo$,
+ balance: {
+ utxo: {
+ available$
+ }
+ }
+};
+
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ ConfirmVoteRegistrationDelegation: mockConfirmVoteRegistrationDelegation
+ };
+});
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ },
+ Cardano: {
+ ...actual.Wallet.Cardano,
+ isDRepAlwaysAbstain: mockIsDRepAlwaysAbstain,
+ isDRepAlwaysNoConfidence: mockIsDRepAlwaysNoConfidence,
+ isDRepCredential: mockIsDRepCredential
+ }
+ }
+ };
+});
+
+const isDRepAlwaysAbstainMocked = 'isDRepAlwaysAbstainMocked';
+const isDRepAlwaysNoConfidenceMocked = 'isDRepAlwaysNoConfidenceMocked';
+
+describe('Testing ConfirmVoteRegistrationDelegationContainer component', () => {
+ beforeEach(() => {
+ mockUseWalletStore.mockReset();
+ mockUseWalletStore.mockImplementation(() => ({
+ inMemoryWallet,
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+ }));
+ mockConfirmVoteRegistrationDelegation.mockReset();
+ mockConfirmVoteRegistrationDelegation.mockReturnValue();
+ mockUseTranslation.mockReset();
+ mockUseTranslation.mockImplementation(() => ({ t }));
+ mockLovelacesToAdaString.mockReset();
+ mockLovelacesToAdaString.mockImplementation((val) => val);
+ mockIsDRepAlwaysAbstain.mockReset();
+ mockIsDRepAlwaysAbstain.mockImplementation(() => isDRepAlwaysAbstainMocked);
+ mockIsDRepAlwaysNoConfidence.mockReset();
+ mockIsDRepAlwaysNoConfidence.mockImplementation(() => isDRepAlwaysNoConfidenceMocked);
+ mockIsDRepCredential.mockReset();
+ mockIsDRepCredential.mockImplementation(() => true);
+ });
+
+ afterEach(() => {
+ jest.resetModules();
+ jest.resetAllMocks();
+ cleanup();
+ });
+
+ test('should render ConfirmVoteRegistrationDelegation component with proper props', async () => {
+ let queryByTestId: any;
+
+ const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+ };
+ const certificate: Wallet.Cardano.Certificate = {
+ __typename: Wallet.Cardano.CertificateType.VoteRegistrationDelegation,
+ stakeCredential: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(STAKE_KEY_HASH)
+ },
+ dRep: {
+ type: Wallet.Cardano.CredentialType.KeyHash,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden').toString('hex'))
+ },
+ deposit: BigInt('100000')
+ };
+ const tx = buildMockTx({
+ certificates: [certificate]
+ });
+ const errorMessage = 'errorMessage';
+ const props = { signTxData: { dappInfo, tx }, errorMessage };
+
+ await act(async () => {
+ ({ queryByTestId } = render(, {
+ wrapper: getWrapper()
+ }));
+ });
+
+ expect(queryByTestId('ConfirmVoteRegistrationDelegation')).toBeInTheDocument();
+ expect(mockConfirmVoteRegistrationDelegation).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ metadata: {
+ depositPaid: `${certificate.deposit.toString()} ${cardanoCoinMock.symbol}`,
+ stakeKeyHash: certificate.stakeCredential.hash,
+ alwaysAbstain: isDRepAlwaysAbstainMocked,
+ alwaysNoConfidence: isDRepAlwaysNoConfidenceMocked,
+ drepId: (certificate.dRep as Wallet.Cardano.Credential).hash.toString()
+ },
+ translations: {
+ metadata: t('core.VoteRegistrationDelegation.metadata'),
+ option: t('core.VoteRegistrationDelegation.option'),
+ labels: {
+ drepId: t('core.VoteRegistrationDelegation.drepId'),
+ alwaysAbstain: t('core.VoteRegistrationDelegation.alwaysAbstain'),
+ alwaysNoConfidence: t('core.VoteRegistrationDelegation.alwaysNoConfidence'),
+ depositPaid: t('core.VoteRegistrationDelegation.depositPaid'),
+ stakeKeyHash: t('core.VoteRegistrationDelegation.stakeKeyHash')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx
index 4f58f3f937..2f6eca12f0 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx
@@ -19,24 +19,12 @@ import * as React from 'react';
import { cleanup, render } from '@testing-library/react';
import { DappTransactionContainer } from '../DappTransactionContainer';
import '@testing-library/jest-dom';
-import { I18nextProvider } from 'react-i18next';
-import { StoreProvider } from '@src/stores';
-import {
- AnalyticsProvider,
- AppSettingsProvider,
- BackgroundServiceAPIProvider,
- BackgroundServiceAPIProviderProps,
- DatabaseProvider
-} from '@src/providers';
-import { APP_MODE_BROWSER } from '@src/utils/constants';
-import i18n from '@lib/i18n';
import { BehaviorSubject } from 'rxjs';
import { act } from 'react-dom/test-utils';
-import { PostHogClientProvider } from '@providers/PostHogClientProvider';
-import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
import { buildMockTx } from '@src/utils/mocks/tx';
import { Wallet } from '@lace/cardano';
import { SignTxData } from '../types';
+import { getWrapper } from '../testing.utils';
const { Cardano, Crypto } = Wallet;
@@ -114,30 +102,6 @@ jest.mock('antd', () => {
};
});
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- );
-
describe('Testing DappTransactionContainer component', () => {
beforeEach(() => {
mockUseWalletStore.mockReset();
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx
new file mode 100644
index 0000000000..8dd390fba9
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ProposalProceduresContainer.test.tsx
@@ -0,0 +1,179 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const mockHardForkInitiationActionContainer = jest.fn(() => );
+const mockInfoActionContainer = jest.fn(() => );
+const mockNewConstitutionActionContainer = jest.fn(() => );
+const mockNoConfidenceActionContainer = jest.fn(() => );
+const mockParameterChangeActionContainer = jest.fn(() => );
+const mockTreasuryWithdrawalsActionContainer = jest.fn(() => );
+const mockUpdateCommitteeActionContainer = jest.fn(() => );
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import { ProposalProceduresContainer } from '../ProposalProceduresContainer';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { buildMockTx } from '@src/utils/mocks/tx';
+
+jest.mock('../proposal-procedures/HardForkInitiationActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/HardForkInitiationActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ HardForkInitiationActionContainer: mockHardForkInitiationActionContainer
+ };
+});
+
+jest.mock('../proposal-procedures/InfoActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/InfoActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ InfoActionContainer: mockInfoActionContainer
+ };
+});
+
+jest.mock('../proposal-procedures/NewConstitutionActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/NewConstitutionActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ NewConstitutionActionContainer: mockNewConstitutionActionContainer
+ };
+});
+
+jest.mock('../proposal-procedures/NoConfidenceActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/NoConfidenceActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ NoConfidenceActionContainer: mockNoConfidenceActionContainer
+ };
+});
+
+jest.mock('../proposal-procedures/ParameterChangeActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/ParameterChangeActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ ParameterChangeActionContainer: mockParameterChangeActionContainer
+ };
+});
+
+jest.mock('../proposal-procedures/TreasuryWithdrawalsActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/TreasuryWithdrawalsActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ TreasuryWithdrawalsActionContainer: mockTreasuryWithdrawalsActionContainer
+ };
+});
+
+jest.mock('../proposal-procedures/UpdateCommitteeActionContainer', () => {
+ const original = jest.requireActual('../proposal-procedures/UpdateCommitteeActionContainer');
+ return {
+ __esModule: true,
+ ...original,
+ UpdateCommitteeActionContainer: mockUpdateCommitteeActionContainer
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const tx = buildMockTx();
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const hardForkInitiationAction = {
+ __typename: Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action
+} as Wallet.Cardano.HardForkInitiationAction;
+const infoAction = {
+ __typename: Wallet.Cardano.GovernanceActionType.info_action
+} as Wallet.Cardano.InfoAction;
+const newConstitution = {
+ __typename: Wallet.Cardano.GovernanceActionType.new_constitution
+} as Wallet.Cardano.NewConstitution;
+const noConfidence = {
+ __typename: Wallet.Cardano.GovernanceActionType.no_confidence
+} as Wallet.Cardano.NoConfidence;
+const parameterChangeAction = {
+ __typename: Wallet.Cardano.GovernanceActionType.parameter_change_action
+} as Wallet.Cardano.ParameterChangeAction;
+const treasuryWithdrawalsAction = {
+ __typename: Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action
+} as Wallet.Cardano.TreasuryWithdrawalsAction;
+const updateCommittee = {
+ __typename: Wallet.Cardano.GovernanceActionType.update_committee
+} as Wallet.Cardano.UpdateCommittee;
+
+const proposalProcedures = [
+ { deposit, rewardAccount, anchor, governanceAction: hardForkInitiationAction },
+ { deposit, rewardAccount, anchor, governanceAction: infoAction },
+ { deposit, rewardAccount, anchor, governanceAction: newConstitution },
+ { deposit, rewardAccount, anchor, governanceAction: noConfidence },
+ { deposit, rewardAccount, anchor, governanceAction: parameterChangeAction },
+ { deposit, rewardAccount, anchor, governanceAction: treasuryWithdrawalsAction },
+ { deposit, rewardAccount, anchor, governanceAction: updateCommittee }
+];
+const signTxData = { dappInfo, tx: { ...tx, body: { ...tx.body, proposalProcedures } } };
+const props = { signTxData, errorMessage };
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render proper procedure', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render());
+ });
+
+ expect(queryByTestId('HardForkInitiationActionContainer')).toBeInTheDocument();
+ expect(queryByTestId('InfoActionContainer')).toBeInTheDocument();
+ expect(queryByTestId('NewConstitutionActionContainer')).toBeInTheDocument();
+ expect(queryByTestId('NoConfidenceActionContainer')).toBeInTheDocument();
+ expect(queryByTestId('ParameterChangeActionContainer')).toBeInTheDocument();
+ expect(queryByTestId('TreasuryWithdrawalsActionContainer')).toBeInTheDocument();
+ expect(queryByTestId('UpdateCommitteeActionContainer')).toBeInTheDocument();
+
+ const expectedProps = { dappInfo: signTxData.dappInfo, errorMessage, deposit, rewardAccount, anchor };
+
+ expect(mockHardForkInitiationActionContainer).toHaveBeenLastCalledWith(
+ { ...expectedProps, governanceAction: hardForkInitiationAction },
+ {}
+ );
+ expect(mockInfoActionContainer).toHaveBeenLastCalledWith({ ...expectedProps, governanceAction: infoAction }, {});
+ expect(mockNewConstitutionActionContainer).toHaveBeenLastCalledWith(
+ { ...expectedProps, governanceAction: newConstitution },
+ {}
+ );
+ expect(mockNoConfidenceActionContainer).toHaveBeenLastCalledWith(
+ { ...expectedProps, governanceAction: noConfidence },
+ {}
+ );
+ expect(mockParameterChangeActionContainer).toHaveBeenLastCalledWith(
+ { ...expectedProps, governanceAction: parameterChangeAction },
+ {}
+ );
+ expect(mockTreasuryWithdrawalsActionContainer).toHaveBeenLastCalledWith(
+ { ...expectedProps, governanceAction: treasuryWithdrawalsAction },
+ {}
+ );
+ expect(mockUpdateCommitteeActionContainer).toHaveBeenLastCalledWith(
+ { ...expectedProps, governanceAction: updateCommittee },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx
index d1ddb7fdcc..fd1c35b40e 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/VotingProceduresContainer.test.tsx
@@ -13,22 +13,10 @@ import * as React from 'react';
import { cleanup, render } from '@testing-library/react';
import { VoterType, Votes, VotingProceduresContainer, getVote, getVoterType } from '../VotingProceduresContainer';
import '@testing-library/jest-dom';
-import { I18nextProvider } from 'react-i18next';
-import { StoreProvider } from '@src/stores';
-import {
- AnalyticsProvider,
- AppSettingsProvider,
- BackgroundServiceAPIProvider,
- BackgroundServiceAPIProviderProps,
- DatabaseProvider
-} from '@src/providers';
-import { APP_MODE_BROWSER } from '@src/utils/constants';
-import i18n from '@lib/i18n';
import { act } from 'react-dom/test-utils';
-import { PostHogClientProvider } from '@providers/PostHogClientProvider';
-import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
import { buildMockTx } from '@src/utils/mocks/tx';
import { Wallet } from '@lace/cardano';
+import { getWrapper } from '../testing.utils';
jest.mock('@src/stores', () => ({
...jest.requireActual('@src/stores'),
@@ -65,30 +53,6 @@ jest.mock('react-i18next', () => {
};
});
-const backgroundService = {
- getBackgroundStorage: jest.fn(),
- setBackgroundStorage: jest.fn()
-} as unknown as BackgroundServiceAPIProviderProps['value'];
-
-const getWrapper =
- () =>
- ({ children }: { children: React.ReactNode }) =>
- (
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
- );
-
describe('Testing VotingProceduresContainer component', () => {
beforeEach(() => {
mockUseWalletStore.mockReset();
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx
index d998977a9f..a43ecc32b0 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/utils.test.tsx
@@ -102,37 +102,66 @@ describe('Testing utils', () => {
const txInspectorCurriedFnPayload = { minted: [], burned: [] } as unknown as any;
const createTxInspectorSpy = jest
.spyOn(Core, 'createTxInspector')
- .mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, votingProcedures: true }));
- expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VotingProcedures);
+ .mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, proposalProcedures: true }));
+ expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.ProposalProcedures);
expect(createTxInspectorSpy).toHaveBeenCalledTimes(1);
+ createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, votingProcedures: true }));
+ expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VotingProcedures);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(2);
+
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, minted: { length: 1 } }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.Mint);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(2);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(3);
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, burned: { length: 1 } }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.Burn);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(3);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(4);
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, dRepRegistration: true }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.DRepRegistration);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(4);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(5);
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, dRepRetirement: true }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.DRepRetirement);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(5);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(6);
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, voteDelegation: true }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VoteDelegation);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(6);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(7);
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, dRepUpdate: true }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.DRepUpdate);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(7);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(8);
+
+ createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload, stakeVoteDelegation: true }));
+ expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.StakeVoteDelegation);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(9);
+
+ createTxInspectorSpy.mockReturnValueOnce(() => ({
+ ...txInspectorCurriedFnPayload,
+ voteRegistrationDelegation: true
+ }));
+ expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.VoteRegistrationDelegation);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(10);
+
+ createTxInspectorSpy.mockReturnValueOnce(() => ({
+ ...txInspectorCurriedFnPayload,
+ stakeRegistrationDelegation: true
+ }));
+ expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.StakeRegistrationDelegation);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(11);
+
+ createTxInspectorSpy.mockReturnValueOnce(() => ({
+ ...txInspectorCurriedFnPayload,
+ stakeVoteDelegationRegistration: true
+ }));
+ expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.StakeVoteDelegationRegistration);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(12);
createTxInspectorSpy.mockReturnValueOnce(() => ({ ...txInspectorCurriedFnPayload }));
expect(getTxType(tx)).toEqual(Wallet.Cip30TxType.Send);
- expect(createTxInspectorSpy).toHaveBeenCalledTimes(8);
+ expect(createTxInspectorSpy).toHaveBeenCalledTimes(13);
});
test('testing drepIDasBech32FromHash', () => {
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts
index 602b689441..105b686672 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts
@@ -12,6 +12,7 @@ import { dAppRoutePaths } from '@routes';
import { Wallet } from '@lace/cardano';
import { useRedirection } from '@hooks';
import { CardanoTxOut, WalletInfo } from '@src/types';
+import { config } from '@src/config';
import { TokenInfo, getAssetsInformation } from '@src/utils/get-assets-information';
import { getTransactionAssetsId } from '@src/stores/slices';
import { AddressListType } from '@src/views/browser-view/features/activity';
@@ -288,3 +289,20 @@ export const useGetOwnPubDRepKeyHash = (): UseGetOwnPubDRepKeyHash => {
// TODO consider using Zustand or at least some common abstraction e.g. https://github.com/streamich/react-use/blob/master/src/useAsync.ts
return { loading: ownPubDRepKeyHash === undefined, ownPubDRepKeyHash };
};
+
+export const useCExpolorerBaseUrl = (): string => {
+ const [explorerBaseUrl, setExplorerBaseUrl] = useState('');
+ const { environmentName } = useWalletStore();
+
+ const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config();
+
+ useEffect(() => {
+ const newUrl =
+ environmentName === 'Sanchonet' ? '' : `${CEXPLORER_BASE_URL[environmentName]}/${CEXPLORER_URL_PATHS.Tx}`;
+ if (newUrl !== explorerBaseUrl) {
+ setExplorerBaseUrl(newUrl);
+ }
+ }, [CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS.Tx, environmentName, explorerBaseUrl]);
+
+ return explorerBaseUrl;
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx
new file mode 100644
index 0000000000..30b937804e
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/HardForkInitiationActionContainer.tsx
@@ -0,0 +1,93 @@
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Wallet } from '@lace/cardano';
+import { HardForkInitiationAction } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ governanceAction: Wallet.Cardano.HardForkInitiationAction;
+ deposit: Wallet.Cardano.ProposalProcedure['deposit'];
+ rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const HardForkInitiationActionContainer = ({
+ dappInfo,
+ governanceAction,
+ deposit,
+ rewardAccount,
+ anchor,
+ errorMessage
+}: Props): React.ReactElement => {
+ const { t } = useTranslation();
+ const {
+ walletUI: { cardanoCoin }
+ } = useWalletStore();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ protocolVersion: {
+ major: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.major'),
+ minor: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.minor'),
+ patch: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.patch')
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ }
+ }),
+ [t]
+ );
+
+ const { governanceActionId, protocolVersion } = governanceAction;
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.hardForkInitiation.title'),
+ deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ }
+ },
+ protocolVersion: {
+ major: protocolVersion.major.toString(),
+ minor: protocolVersion.minor.toString(),
+ patch: protocolVersion.patch?.toString()
+ },
+ ...(governanceActionId && {
+ actionId: {
+ index: governanceActionId.actionIndex.toString(),
+ id: governanceActionId.id || ''
+ }
+ })
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx
new file mode 100644
index 0000000000..de46a3b62f
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/InfoActionContainer.tsx
@@ -0,0 +1,50 @@
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Wallet } from '@lace/cardano';
+import { InfoAction } from '@lace/core';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const InfoActionContainer = ({ dappInfo, anchor, errorMessage }: Props): React.ReactElement => {
+ const { t } = useTranslation();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ }
+ }),
+ [t]
+ );
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.infoAction.title')
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ }
+ }
+ };
+
+ return ;
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx
new file mode 100644
index 0000000000..de84864942
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NewConstitutionActionContainer.tsx
@@ -0,0 +1,98 @@
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Wallet } from '@lace/cardano';
+import { NewConstitutionAction } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ governanceAction: Wallet.Cardano.NewConstitution;
+ deposit: Wallet.Cardano.ProposalProcedure['deposit'];
+ rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const NewConstitutionActionContainer = ({
+ dappInfo,
+ governanceAction,
+ deposit,
+ rewardAccount,
+ anchor,
+ errorMessage
+}: Props): React.ReactElement => {
+ const { t } = useTranslation();
+ const {
+ walletUI: { cardanoCoin }
+ } = useWalletStore();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ constitution: {
+ title: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.title'),
+ anchor: {
+ dataHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.dataHash'),
+ url: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.url')
+ },
+ scriptHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.scriptHash')
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ }
+ }),
+ [t]
+ );
+
+ const { governanceActionId, constitution } = governanceAction;
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.newConstitutionAction.title'),
+ deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ }
+ },
+ ...(governanceActionId && {
+ actionId: {
+ index: governanceActionId.actionIndex.toString(),
+ id: governanceActionId.id || ''
+ }
+ }),
+ constitution: {
+ anchor: {
+ dataHash: constitution.anchor.dataHash.toString(),
+ url: constitution.anchor.url.toString()
+ },
+ ...(constitution.scriptHash && { scriptHash: constitution.scriptHash.toString() })
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx
new file mode 100644
index 0000000000..7605b54424
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/NoConfidenceActionContainer.tsx
@@ -0,0 +1,81 @@
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Wallet } from '@lace/cardano';
+import { NoConfidenceAction } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ governanceAction: Wallet.Cardano.NoConfidence;
+ deposit: Wallet.Cardano.ProposalProcedure['deposit'];
+ rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const NoConfidenceActionContainer = ({
+ dappInfo,
+ governanceAction,
+ deposit,
+ rewardAccount,
+ anchor,
+ errorMessage
+}: Props): React.ReactElement => {
+ const { t } = useTranslation();
+ const {
+ walletUI: { cardanoCoin }
+ } = useWalletStore();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ }
+ }),
+ [t]
+ );
+
+ const { governanceActionId } = governanceAction;
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.noConfidenceAction.title'),
+ deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ }
+ },
+ ...(governanceActionId && {
+ actionId: {
+ index: governanceActionId.actionIndex.toString(),
+ id: governanceActionId.id || ''
+ }
+ })
+ };
+
+ return ;
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx
new file mode 100644
index 0000000000..31264a6be2
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/ParameterChangeActionContainer.tsx
@@ -0,0 +1,341 @@
+/* eslint-disable unicorn/no-array-reduce */
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { formatPercentages } from '@lace/common';
+import { Wallet } from '@lace/cardano';
+import { ParameterChangeAction } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ governanceAction: Wallet.Cardano.ParameterChangeAction;
+ deposit: Wallet.Cardano.ProposalProcedure['deposit'];
+ rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const ParameterChangeActionContainer = ({
+ dappInfo,
+ governanceAction,
+ deposit,
+ rewardAccount,
+ anchor,
+ errorMessage
+}: Props): React.ReactElement => {
+ const { t } = useTranslation();
+ const {
+ walletUI: { cardanoCoin }
+ } = useWalletStore();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ },
+ memory: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.memory'),
+ step: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.step'),
+ networkGroup: {
+ title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.title'),
+ maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBBSize'),
+ maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxSize'),
+ maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBHSize'),
+ maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxValSize'),
+ maxTxExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxExUnits'),
+ maxBlockExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBlockExUnits'),
+ maxCollateralInputs: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxCollateralInputs'
+ ),
+ coinsByUTXOByte: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.coinsByUTXOByte'),
+ tooltip: {
+ maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBBSize'),
+ maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxSize'),
+ maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBHSize'),
+ maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxValSize'),
+ maxTxExUnits: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxExUnits'
+ ),
+ maxBlockExUnits: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBlockExUnits'
+ ),
+ maxCollateralInputs: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxCollateralInputs'
+ ),
+ coinsByUTXOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.coinsByUTXOByte'
+ )
+ }
+ },
+ economicGroup: {
+ title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.title'),
+ minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeA'),
+ minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeB'),
+ keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.keyDeposit'),
+ poolDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.poolDeposit'),
+ rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.rho'),
+ tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tau'),
+ minPoolCost: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minPoolCost'),
+ coinsPerUTxOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.coinsPerUTxOByte'
+ ),
+ prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.prices'),
+ tooltip: {
+ minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeA'),
+ minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeB'),
+ keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.keyDeposit'),
+ poolDeposit: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.poolDeposit'
+ ),
+ rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.rho'),
+ tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.tau'),
+ minPoolCost: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minPoolCost'
+ ),
+ coinsPerUTxOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.coinsPerUTxOByte'
+ ),
+ prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.prices')
+ }
+ },
+ technicalGroup: {
+ title: t('core.ProposalProcedure.governanceAction.technicalGroup.title'),
+ a0: t('core.ProposalProcedure.governanceAction.technicalGroup.a0'),
+ eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.eMax'),
+ nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.nOpt'),
+ costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.costModels'),
+ collateralPercentage: t('core.ProposalProcedure.governanceAction.technicalGroup.collateralPercentage'),
+ tooltip: {
+ a0: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.a0'),
+ eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.eMax'),
+ nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.nOpt'),
+ costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.costModels'),
+ collateralPercentage: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.collateralPercentage')
+ }
+ },
+ governanceGroup: {
+ title: t('core.ProposalProcedure.governanceAction.governanceGroup.title'),
+ govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionLifetime'),
+ govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionDeposit'),
+ drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.drepDeposit'),
+ drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.drepActivity'),
+ ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMinSize'),
+ ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMaxTermLength'),
+ dRepVotingThresholds: {
+ title: t('core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.title'),
+ motionNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.motionNoConfidence'
+ ),
+ committeeNormal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNormal'
+ ),
+ committeeNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNoConfidence'
+ ),
+ updateConstitution: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.updateConstitution'
+ ),
+ hardForkInitiation: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.hardForkInitiation'
+ ),
+ ppNetworkGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppNetworkGroup'
+ ),
+ ppEconomicGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppEconomicGroup'
+ ),
+ ppTechnicalGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppTechnicalGroup'
+ ),
+ ppGovernanceGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppGovernanceGroup'
+ ),
+ treasuryWithdrawal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.treasuryWithdrawal'
+ )
+ },
+ tooltip: {
+ govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionLifetime'),
+ govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionDeposit'),
+ drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepDeposit'),
+ drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepActivity'),
+ ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMinSize'),
+ ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMaxTermLength'),
+ dRepVotingThresholds: {
+ title: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.title'),
+ motionNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.motionNoConfidence'
+ ),
+ committeeNormal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNormal'
+ ),
+ committeeNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNoConfidence'
+ ),
+ updateConstitution: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.updateConstitution'
+ ),
+ hardForkInitiation: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.hardForkInitiation'
+ ),
+ ppNetworkGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppNetworkGroup'
+ ),
+ ppEconomicGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppEconomicGroup'
+ ),
+ ppTechnicalGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppTechnicalGroup'
+ ),
+ ppGovernanceGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppGovernanceGroup'
+ ),
+ treasuryWithdrawal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.treasuryWithdrawal'
+ )
+ }
+ }
+ }
+ }),
+ [t]
+ );
+
+ const {
+ protocolParamUpdate: {
+ maxBlockBodySize,
+ maxTxSize,
+ maxBlockHeaderSize,
+ maxValueSize,
+ maxExecutionUnitsPerTransaction,
+ maxExecutionUnitsPerBlock,
+ maxCollateralInputs,
+ stakeKeyDeposit,
+ poolDeposit,
+ minFeeCoefficient,
+ minFeeConstant,
+ treasuryExpansion,
+ monetaryExpansion,
+ minPoolCost,
+ coinsPerUtxoByte,
+ prices,
+ poolInfluence,
+ poolRetirementEpochBound,
+ desiredNumberOfPools,
+ costModels,
+ collateralPercentage,
+ governanceActionDeposit,
+ dRepDeposit,
+ governanceActionValidityPeriod,
+ dRepInactivityPeriod,
+ minCommitteeSize,
+ committeeTermLimit,
+ dRepVotingThresholds: {
+ motionNoConfidence,
+ committeeNormal,
+ commiteeNoConfidence,
+ updateConstitution,
+ hardForkInitiation,
+ ppNetworkGroup,
+ ppEconomicGroup,
+ ppTechnicalGroup,
+ ppGovernanceGroup,
+ treasuryWithdrawal
+ }
+ }
+ } = governanceAction;
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.title'),
+ deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`,
+ rewardAccount
+ },
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ },
+ protocolParamUpdate: {
+ maxTxExUnits: {
+ memory: maxExecutionUnitsPerTransaction.memory.toString(),
+ step: maxExecutionUnitsPerTransaction.steps.toString()
+ },
+ maxBlockExUnits: {
+ memory: maxExecutionUnitsPerBlock.memory.toString(),
+ step: maxExecutionUnitsPerBlock.steps.toString()
+ },
+ networkGroup: {
+ maxBBSize: maxBlockBodySize.toString(),
+ maxTxSize: maxTxSize.toString(),
+ maxBHSize: maxBlockHeaderSize.toString(),
+ maxValSize: maxValueSize.toString(),
+ maxCollateralInputs: maxCollateralInputs.toString()
+ },
+ economicGroup: {
+ minFeeA: minFeeCoefficient.toString(),
+ minFeeB: minFeeConstant.toString(),
+ keyDeposit: stakeKeyDeposit.toString(),
+ poolDeposit: poolDeposit.toString(),
+ rho: monetaryExpansion,
+ tau: treasuryExpansion,
+ minPoolCost: minPoolCost.toString(),
+ coinsPerUTxOByte: coinsPerUtxoByte.toString(),
+ price: {
+ memory: prices.memory.toString(),
+ step: prices.steps.toString()
+ }
+ },
+ technicalGroup: {
+ a0: poolInfluence,
+ eMax: poolRetirementEpochBound.toString(),
+ nOpt: desiredNumberOfPools.toString(),
+ // TODO: review cost model syntax/display
+ costModels: {
+ PlutusV1: Object.entries(costModels.get(Wallet.Cardano.PlutusLanguageVersion.V1)).reduce(
+ (acc, cur) => ({ ...acc, [cur[0]]: cur[1] }),
+ {}
+ ),
+ PlutusV2: Object.entries(costModels.get(Wallet.Cardano.PlutusLanguageVersion.V2)).reduce(
+ (acc, cur) => ({ ...acc, [cur[0]]: cur[1] }),
+ {}
+ )
+ },
+ collateralPercentage: collateralPercentage.toString()
+ },
+ governanceGroup: {
+ govActionLifetime: governanceActionValidityPeriod.toString(),
+ govActionDeposit: governanceActionDeposit.toString(),
+ drepDeposit: dRepDeposit.toString(),
+ drepActivity: dRepInactivityPeriod.toString(),
+ ccMinSize: minCommitteeSize.toString(),
+ ccMaxTermLength: committeeTermLimit.toString(),
+ dRepVotingThresholds: {
+ motionNoConfidence: formatPercentages(motionNoConfidence.numerator / motionNoConfidence.denominator),
+ committeeNormal: formatPercentages(committeeNormal.numerator / committeeNormal.denominator),
+ committeeNoConfidence: formatPercentages(commiteeNoConfidence.numerator / commiteeNoConfidence.denominator),
+ updateToConstitution: formatPercentages(updateConstitution.numerator / updateConstitution.denominator),
+ hardForkInitiation: formatPercentages(hardForkInitiation.numerator / hardForkInitiation.denominator),
+ ppNetworkGroup: formatPercentages(ppNetworkGroup.numerator / ppNetworkGroup.denominator),
+ ppEconomicGroup: formatPercentages(ppEconomicGroup.numerator / ppEconomicGroup.denominator),
+ ppTechnicalGroup: formatPercentages(ppTechnicalGroup.numerator / ppTechnicalGroup.denominator),
+ ppGovGroup: formatPercentages(ppGovernanceGroup.numerator / ppGovernanceGroup.denominator),
+ treasuryWithdrawal: formatPercentages(treasuryWithdrawal.numerator / treasuryWithdrawal.denominator)
+ }
+ }
+ }
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx
new file mode 100644
index 0000000000..6cb2d0d385
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/TreasuryWithdrawalsActionContainer.tsx
@@ -0,0 +1,91 @@
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Wallet } from '@lace/cardano';
+import { TreasuryWithdrawalsAction } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ governanceAction: Wallet.Cardano.TreasuryWithdrawalsAction;
+ deposit: Wallet.Cardano.ProposalProcedure['deposit'];
+ rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const TreasuryWithdrawalsActionContainer = ({
+ dappInfo,
+ governanceAction,
+ deposit,
+ rewardAccount,
+ anchor,
+ errorMessage
+}: Props): React.ReactElement => {
+ const { t } = useTranslation();
+ const {
+ walletUI: { cardanoCoin }
+ } = useWalletStore();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ },
+ withdrawals: {
+ title: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'),
+ rewardAccount: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.rewardAccount'),
+ lovelace: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.lovelace')
+ }
+ }),
+ [t]
+ );
+
+ const { withdrawals } = governanceAction;
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'),
+ deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ }
+ },
+ withdrawals: [...withdrawals].map((withdrawal) => ({
+ rewardAccount: withdrawal.rewardAccount.toString(),
+ lovelace: `${Wallet.util.lovelacesToAdaString(withdrawal.coin.toString())} ${cardanoCoin.symbol}`
+ }))
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx
new file mode 100644
index 0000000000..0a01112d1f
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/UpdateCommitteeActionContainer.tsx
@@ -0,0 +1,107 @@
+import React, { useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Wallet } from '@lace/cardano';
+import { UpdateCommitteeAction } from '@lace/core';
+import { useWalletStore } from '@src/stores';
+import { SignTxData } from '../types';
+import { useCExpolorerBaseUrl } from '../hooks';
+
+interface Props {
+ dappInfo: SignTxData['dappInfo'];
+ governanceAction: Wallet.Cardano.UpdateCommittee;
+ deposit: Wallet.Cardano.ProposalProcedure['deposit'];
+ rewardAccount: Wallet.Cardano.ProposalProcedure['rewardAccount'];
+ anchor: Wallet.Cardano.ProposalProcedure['anchor'];
+ errorMessage?: string;
+}
+
+export const UpdateCommitteeActionContainer = ({
+ dappInfo,
+ governanceAction,
+ deposit,
+ rewardAccount,
+ anchor,
+ errorMessage
+}: Props): React.ReactElement => {
+ const { t } = useTranslation();
+ const {
+ walletUI: { cardanoCoin }
+ } = useWalletStore();
+
+ const explorerBaseUrl = useCExpolorerBaseUrl();
+
+ const translations = useMemo[0]['translations']>(
+ () => ({
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ },
+ membersToBeAdded: {
+ title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.title'),
+ coldCredential: {
+ hash: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.hash'),
+ epoch: t(
+ 'core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.epoch'
+ )
+ }
+ },
+ membersToBeRemoved: {
+ title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.title'),
+ hash: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.hash')
+ }
+ }),
+ [t]
+ );
+
+ const { membersToBeAdded, membersToBeRemoved, governanceActionId } = governanceAction;
+
+ const data: Parameters[0]['data'] = {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.title'),
+ deposit: `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${cardanoCoin.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ ...(explorerBaseUrl && { txHashUrl: `${explorerBaseUrl}/${anchor.dataHash}` })
+ }
+ },
+ ...(governanceActionId && {
+ actionId: {
+ index: governanceActionId.actionIndex.toString(),
+ id: governanceActionId.id || ''
+ }
+ }),
+ membersToBeAdded: [...membersToBeAdded].map(({ coldCredential: { hash }, epoch }) => ({
+ coldCredential: {
+ // TODO: use bech32 in future revision
+ hash: hash.toString()
+ },
+ epoch: epoch.toString()
+ })),
+ membersToBeRemoved: [...membersToBeRemoved].map(({ hash }) => ({
+ // TODO: use bech32 in future revision
+ hash: hash.toString()
+ }))
+ };
+
+ return (
+
+ );
+};
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx
new file mode 100644
index 0000000000..3ff6302ac4
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/HardForkInitiationActionContainer.test.tsx
@@ -0,0 +1,175 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+const mockUseWalletStore = jest.fn(() => ({
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+}));
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockHardForkInitiationAction = jest.fn(() => );
+const mockLovelacesToAdaString = jest.fn((val) => val);
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { HardForkInitiationActionContainer } from '../HardForkInitiationActionContainer';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ HardForkInitiationAction: mockHardForkInitiationAction
+ };
+});
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const hardForkInitiationAction = {
+ protocolVersion: {
+ major: 123,
+ minor: 234,
+ patch: 456
+ },
+ governanceActionId: {
+ actionIndex: 123,
+ id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821')
+ },
+ __typename: Wallet.Cardano.GovernanceActionType.hard_fork_initiation_action
+} as Wallet.Cardano.HardForkInitiationAction;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render HardForkInitiationAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('HardForkInitiationAction')).toBeInTheDocument();
+ expect(mockHardForkInitiationAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.hardForkInitiation.title'),
+ deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ }
+ },
+ protocolVersion: {
+ major: hardForkInitiationAction.protocolVersion.major.toString(),
+ minor: hardForkInitiationAction.protocolVersion.minor.toString(),
+ patch: hardForkInitiationAction.protocolVersion.patch?.toString()
+ },
+ actionId: {
+ index: hardForkInitiationAction.governanceActionId.actionIndex.toString(),
+ id: hardForkInitiationAction.governanceActionId.id || ''
+ }
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ protocolVersion: {
+ major: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.major'),
+ minor: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.minor'),
+ patch: t('core.ProposalProcedure.governanceAction.hardForkInitiation.protocolVersion.patch')
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx
new file mode 100644
index 0000000000..e4b11d214c
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/InfoActionContainer.test.tsx
@@ -0,0 +1,115 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockInfoAction = jest.fn(() => );
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { InfoActionContainer } from '../InfoActionContainer';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ InfoAction: mockInfoAction
+ };
+});
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const infoAction = {
+ __typename: Wallet.Cardano.GovernanceActionType.info_action
+} as Wallet.Cardano.InfoAction;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render InfoAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('InfoAction')).toBeInTheDocument();
+ expect(mockInfoAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.infoAction.title')
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ }
+ }
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx
new file mode 100644
index 0000000000..468233c9b3
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NewConstitutionActionContainer.test.tsx
@@ -0,0 +1,179 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+const mockUseWalletStore = jest.fn(() => ({
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+}));
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockNewConstitutionAction = jest.fn(() => );
+const mockLovelacesToAdaString = jest.fn((val) => val);
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { NewConstitutionActionContainer } from '../NewConstitutionActionContainer';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ NewConstitutionAction: mockNewConstitutionAction
+ };
+});
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const newConstitution = {
+ __typename: Wallet.Cardano.GovernanceActionType.new_constitution,
+ constitution: {
+ anchor,
+ scriptHash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('newConstitutionscriptHashnew').toString('hex'))
+ },
+ governanceActionId: {
+ actionIndex: 123,
+ id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821')
+ }
+} as Wallet.Cardano.NewConstitution;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render NewConstitutionAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('NewConstitutionAction')).toBeInTheDocument();
+ expect(mockNewConstitutionAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.newConstitutionAction.title'),
+ deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ }
+ },
+ actionId: {
+ index: newConstitution.governanceActionId.actionIndex.toString(),
+ id: newConstitution.governanceActionId.id || ''
+ },
+ constitution: {
+ anchor: {
+ dataHash: newConstitution.constitution.anchor.dataHash.toString(),
+ url: newConstitution.constitution.anchor.url.toString()
+ },
+ scriptHash: newConstitution.constitution.scriptHash.toString()
+ }
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ constitution: {
+ title: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.title'),
+ anchor: {
+ dataHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.dataHash'),
+ url: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.anchor.url')
+ },
+ scriptHash: t('core.ProposalProcedure.governanceAction.newConstitutionAction.constitution.scriptHash')
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx
new file mode 100644
index 0000000000..5d0d1ee411
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/NoConfidenceActionContainer.test.tsx
@@ -0,0 +1,160 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+const mockUseWalletStore = jest.fn(() => ({
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+}));
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockNoConfidenceAction = jest.fn(() => );
+const mockLovelacesToAdaString = jest.fn((val) => val);
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { NoConfidenceActionContainer } from '../NoConfidenceActionContainer';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ NoConfidenceAction: mockNoConfidenceAction
+ };
+});
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const noConfidence = {
+ __typename: Wallet.Cardano.GovernanceActionType.no_confidence,
+ governanceActionId: {
+ actionIndex: 123,
+ id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821')
+ }
+} as Wallet.Cardano.NoConfidence;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render NoConfidenceAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('NoConfidenceAction')).toBeInTheDocument();
+ expect(mockNoConfidenceAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.noConfidenceAction.title'),
+ deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ }
+ },
+ actionId: {
+ index: noConfidence.governanceActionId.actionIndex.toString(),
+ id: noConfidence.governanceActionId.id || ''
+ }
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx
new file mode 100644
index 0000000000..b0f46f2293
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/ParameterChangeActionContainer.test.tsx
@@ -0,0 +1,504 @@
+/* eslint-disable unicorn/no-array-reduce */
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+const mockUseWalletStore = jest.fn(() => ({
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+}));
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockParameterChangeAction = jest.fn(() => );
+const mockLovelacesToAdaString = jest.fn((val) => val);
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { ParameterChangeActionContainer } from '../ParameterChangeActionContainer';
+import { formatPercentages } from '@lace/common';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ ParameterChangeAction: mockParameterChangeAction
+ };
+});
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const parameterChangeAction = {
+ protocolParamUpdate: {
+ maxBlockBodySize: 1,
+ maxTxSize: 2,
+ maxBlockHeaderSize: 3,
+ maxValueSize: 4,
+ maxExecutionUnitsPerTransaction: {
+ memory: 5,
+ steps: 6
+ },
+ maxExecutionUnitsPerBlock: {
+ memory: 7,
+ steps: 8
+ },
+ maxCollateralInputs: 9,
+ stakeKeyDeposit: 10,
+ poolDeposit: 11,
+ minFeeCoefficient: 12,
+ minFeeConstant: 13,
+ treasuryExpansion: '14',
+ monetaryExpansion: '15',
+ minPoolCost: 16,
+ coinsPerUtxoByte: 17,
+ prices: {
+ memory: 18,
+ steps: 19
+ },
+ poolInfluence: '20',
+ poolRetirementEpochBound: 21,
+ desiredNumberOfPools: 22,
+ costModels: new Map([
+ [0, [23, 24]],
+ [1, [25, 26]]
+ ]),
+ collateralPercentage: 27,
+ governanceActionDeposit: 28,
+ dRepDeposit: 29,
+ governanceActionValidityPeriod: 30,
+ dRepInactivityPeriod: 31,
+ minCommitteeSize: 32,
+ committeeTermLimit: 33,
+ dRepVotingThresholds: {
+ motionNoConfidence: {
+ numerator: 34,
+ denominator: 35
+ },
+ committeeNormal: {
+ numerator: 36,
+ denominator: 37
+ },
+ commiteeNoConfidence: {
+ numerator: 38,
+ denominator: 39
+ },
+ updateConstitution: {
+ numerator: 40,
+ denominator: 41
+ },
+ hardForkInitiation: {
+ numerator: 42,
+ denominator: 43
+ },
+ ppNetworkGroup: {
+ numerator: 44,
+ denominator: 45
+ },
+ ppEconomicGroup: {
+ numerator: 46,
+ denominator: 47
+ },
+ ppTechnicalGroup: {
+ numerator: 48,
+ denominator: 49
+ },
+ ppGovernanceGroup: {
+ numerator: 50,
+ denominator: 51
+ },
+ treasuryWithdrawal: {
+ numerator: 55,
+ denominator: 56
+ }
+ }
+ },
+ governanceActionId: {
+ actionIndex: 123,
+ id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821')
+ },
+ __typename: Wallet.Cardano.GovernanceActionType.parameter_change_action
+} as Wallet.Cardano.ParameterChangeAction;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render ParameterChangeAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('ParameterChangeAction')).toBeInTheDocument();
+ expect(mockParameterChangeAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.title'),
+ deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`,
+ rewardAccount
+ },
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ },
+ protocolParamUpdate: {
+ maxTxExUnits: {
+ memory: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerTransaction.memory.toString(),
+ step: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerTransaction.steps.toString()
+ },
+ maxBlockExUnits: {
+ memory: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerBlock.memory.toString(),
+ step: parameterChangeAction.protocolParamUpdate.maxExecutionUnitsPerBlock.steps.toString()
+ },
+ networkGroup: {
+ maxBBSize: parameterChangeAction.protocolParamUpdate.maxBlockBodySize.toString(),
+ maxTxSize: parameterChangeAction.protocolParamUpdate.maxTxSize.toString(),
+ maxBHSize: parameterChangeAction.protocolParamUpdate.maxBlockHeaderSize.toString(),
+ maxValSize: parameterChangeAction.protocolParamUpdate.maxValueSize.toString(),
+ maxCollateralInputs: parameterChangeAction.protocolParamUpdate.maxCollateralInputs.toString()
+ },
+ economicGroup: {
+ minFeeA: parameterChangeAction.protocolParamUpdate.minFeeCoefficient.toString(),
+ minFeeB: parameterChangeAction.protocolParamUpdate.minFeeConstant.toString(),
+ keyDeposit: parameterChangeAction.protocolParamUpdate.stakeKeyDeposit.toString(),
+ poolDeposit: parameterChangeAction.protocolParamUpdate.poolDeposit.toString(),
+ rho: parameterChangeAction.protocolParamUpdate.monetaryExpansion,
+ tau: parameterChangeAction.protocolParamUpdate.treasuryExpansion,
+ minPoolCost: parameterChangeAction.protocolParamUpdate.minPoolCost.toString(),
+ coinsPerUTxOByte: parameterChangeAction.protocolParamUpdate.coinsPerUtxoByte.toString(),
+ price: {
+ memory: parameterChangeAction.protocolParamUpdate.prices.memory.toString(),
+ step: parameterChangeAction.protocolParamUpdate.prices.steps.toString()
+ }
+ },
+ technicalGroup: {
+ a0: parameterChangeAction.protocolParamUpdate.poolInfluence,
+ eMax: parameterChangeAction.protocolParamUpdate.poolRetirementEpochBound.toString(),
+ nOpt: parameterChangeAction.protocolParamUpdate.desiredNumberOfPools.toString(),
+ costModels: {
+ PlutusV1: Object.entries(
+ parameterChangeAction.protocolParamUpdate.costModels.get(Wallet.Cardano.PlutusLanguageVersion.V1)
+ ).reduce((acc, cur) => ({ ...acc, [cur[0]]: cur[1] }), {}),
+ PlutusV2: Object.entries(
+ parameterChangeAction.protocolParamUpdate.costModels.get(Wallet.Cardano.PlutusLanguageVersion.V2)
+ ).reduce((acc, cur) => ({ ...acc, [cur[0]]: cur[1] }), {})
+ },
+ collateralPercentage: parameterChangeAction.protocolParamUpdate.collateralPercentage.toString()
+ },
+ governanceGroup: {
+ govActionLifetime: parameterChangeAction.protocolParamUpdate.governanceActionValidityPeriod.toString(),
+ govActionDeposit: parameterChangeAction.protocolParamUpdate.governanceActionDeposit.toString(),
+ drepDeposit: parameterChangeAction.protocolParamUpdate.dRepDeposit.toString(),
+ drepActivity: parameterChangeAction.protocolParamUpdate.dRepInactivityPeriod.toString(),
+ ccMinSize: parameterChangeAction.protocolParamUpdate.minCommitteeSize.toString(),
+ ccMaxTermLength: parameterChangeAction.protocolParamUpdate.committeeTermLimit.toString(),
+ dRepVotingThresholds: {
+ motionNoConfidence: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.motionNoConfidence.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.motionNoConfidence.denominator
+ ),
+ committeeNormal: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.committeeNormal.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.committeeNormal.denominator
+ ),
+ committeeNoConfidence: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.commiteeNoConfidence.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.commiteeNoConfidence.denominator
+ ),
+ updateToConstitution: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.updateConstitution.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.updateConstitution.denominator
+ ),
+ hardForkInitiation: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.hardForkInitiation.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.hardForkInitiation.denominator
+ ),
+ ppNetworkGroup: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppNetworkGroup.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppNetworkGroup.denominator
+ ),
+ ppEconomicGroup: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppEconomicGroup.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppEconomicGroup.denominator
+ ),
+ ppTechnicalGroup: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppTechnicalGroup.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppTechnicalGroup.denominator
+ ),
+ ppGovGroup: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppGovernanceGroup.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.ppGovernanceGroup.denominator
+ ),
+ treasuryWithdrawal: formatPercentages(
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.treasuryWithdrawal.numerator /
+ parameterChangeAction.protocolParamUpdate.dRepVotingThresholds.treasuryWithdrawal.denominator
+ )
+ }
+ }
+ }
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ },
+ memory: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.memory'),
+ step: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.step'),
+ networkGroup: {
+ title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.title'),
+ maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBBSize'),
+ maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxSize'),
+ maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBHSize'),
+ maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxValSize'),
+ maxTxExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxExUnits'),
+ maxBlockExUnits: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBlockExUnits'
+ ),
+ maxCollateralInputs: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxCollateralInputs'
+ ),
+ coinsByUTXOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.coinsByUTXOByte'
+ ),
+ tooltip: {
+ maxBBSize: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBBSize'
+ ),
+ maxTxSize: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxSize'
+ ),
+ maxBHSize: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBHSize'
+ ),
+ maxValSize: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxValSize'
+ ),
+ maxTxExUnits: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxTxExUnits'
+ ),
+ maxBlockExUnits: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxBlockExUnits'
+ ),
+ maxCollateralInputs: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.maxCollateralInputs'
+ ),
+ coinsByUTXOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.tooltip.coinsByUTXOByte'
+ )
+ }
+ },
+ economicGroup: {
+ title: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.title'),
+ minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeA'),
+ minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeB'),
+ keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.keyDeposit'),
+ poolDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.poolDeposit'),
+ rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.rho'),
+ tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tau'),
+ minPoolCost: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minPoolCost'),
+ coinsPerUTxOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.coinsPerUTxOByte'
+ ),
+ prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.prices'),
+ tooltip: {
+ minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeA'),
+ minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minFeeB'),
+ keyDeposit: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.keyDeposit'
+ ),
+ poolDeposit: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.poolDeposit'
+ ),
+ rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.rho'),
+ tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.tau'),
+ minPoolCost: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.minPoolCost'
+ ),
+ coinsPerUTxOByte: t(
+ 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.coinsPerUTxOByte'
+ ),
+ prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tooltip.prices')
+ }
+ },
+ technicalGroup: {
+ title: t('core.ProposalProcedure.governanceAction.technicalGroup.title'),
+ a0: t('core.ProposalProcedure.governanceAction.technicalGroup.a0'),
+ eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.eMax'),
+ nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.nOpt'),
+ costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.costModels'),
+ collateralPercentage: t('core.ProposalProcedure.governanceAction.technicalGroup.collateralPercentage'),
+ tooltip: {
+ a0: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.a0'),
+ eMax: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.eMax'),
+ nOpt: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.nOpt'),
+ costModels: t('core.ProposalProcedure.governanceAction.technicalGroup.tooltip.costModels'),
+ collateralPercentage: t(
+ 'core.ProposalProcedure.governanceAction.technicalGroup.tooltip.collateralPercentage'
+ )
+ }
+ },
+ governanceGroup: {
+ title: t('core.ProposalProcedure.governanceAction.governanceGroup.title'),
+ govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionLifetime'),
+ govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.govActionDeposit'),
+ drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.drepDeposit'),
+ drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.drepActivity'),
+ ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMinSize'),
+ ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.ccMaxTermLength'),
+ dRepVotingThresholds: {
+ title: t('core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.title'),
+ motionNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.motionNoConfidence'
+ ),
+ committeeNormal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNormal'
+ ),
+ committeeNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.committeeNoConfidence'
+ ),
+ updateConstitution: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.updateConstitution'
+ ),
+ hardForkInitiation: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.hardForkInitiation'
+ ),
+ ppNetworkGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppNetworkGroup'
+ ),
+ ppEconomicGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppEconomicGroup'
+ ),
+ ppTechnicalGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppTechnicalGroup'
+ ),
+ ppGovernanceGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.ppGovernanceGroup'
+ ),
+ treasuryWithdrawal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.dRepVotingThresholds.treasuryWithdrawal'
+ )
+ },
+ tooltip: {
+ govActionLifetime: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionLifetime'),
+ govActionDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.govActionDeposit'),
+ drepDeposit: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepDeposit'),
+ drepActivity: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.drepActivity'),
+ ccMinSize: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMinSize'),
+ ccMaxTermLength: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.ccMaxTermLength'),
+ dRepVotingThresholds: {
+ title: t('core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.title'),
+ motionNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.motionNoConfidence'
+ ),
+ committeeNormal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNormal'
+ ),
+ committeeNoConfidence: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.committeeNoConfidence'
+ ),
+ updateConstitution: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.updateConstitution'
+ ),
+ hardForkInitiation: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.hardForkInitiation'
+ ),
+ ppNetworkGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppNetworkGroup'
+ ),
+ ppEconomicGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppEconomicGroup'
+ ),
+ ppTechnicalGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppTechnicalGroup'
+ ),
+ ppGovernanceGroup: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.ppGovernanceGroup'
+ ),
+ treasuryWithdrawal: t(
+ 'core.ProposalProcedure.governanceAction.governanceGroup.tooltip.dRepVotingThresholds.treasuryWithdrawal'
+ )
+ }
+ }
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx
new file mode 100644
index 0000000000..701b0c2be3
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/TreasuryWithdrawalsActionContainer.test.tsx
@@ -0,0 +1,165 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+const mockUseWalletStore = jest.fn(() => ({
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+}));
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockTreasuryWithdrawalsAction = jest.fn(() => );
+const mockLovelacesToAdaString = jest.fn((val) => val);
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { TreasuryWithdrawalsActionContainer } from '../TreasuryWithdrawalsActionContainer';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ TreasuryWithdrawalsAction: mockTreasuryWithdrawalsAction
+ };
+});
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const treasuryWithdrawalsAction = {
+ withdrawals: new Set([
+ { rewardAccount, coin: BigInt('10000000') },
+ { rewardAccount, coin: BigInt('10000001') }
+ ]),
+ __typename: Wallet.Cardano.GovernanceActionType.treasury_withdrawals_action
+} as Wallet.Cardano.TreasuryWithdrawalsAction;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render TreasuryWithdrawalsAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('TreasuryWithdrawalsAction')).toBeInTheDocument();
+ expect(mockTreasuryWithdrawalsAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'),
+ deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ }
+ },
+ withdrawals: [...treasuryWithdrawalsAction.withdrawals].map((withdrawal) => ({
+ rewardAccount: withdrawal.rewardAccount.toString(),
+ lovelace: `${withdrawal.coin.toString()} ${cardanoCoinMock.symbol}`
+ }))
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ },
+ withdrawals: {
+ title: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.title'),
+ rewardAccount: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.rewardAccount'),
+ lovelace: t('core.ProposalProcedure.governanceAction.treasuryWithdrawals.withdrawals.lovelace')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx
new file mode 100644
index 0000000000..5c99335f63
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/proposal-procedures/__tests__/UpdateCommitteeActionContainer.test.tsx
@@ -0,0 +1,208 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable no-magic-numbers */
+/* eslint-disable import/imports-first */
+const cardanoCoinMock = {
+ symbol: 'cardanoCoinMockSymbol'
+};
+const mockUseWalletStore = jest.fn(() => ({
+ walletUI: { cardanoCoin: cardanoCoinMock },
+ walletInfo: {}
+}));
+const t = jest.fn().mockImplementation((res) => res);
+const mockUseTranslation = jest.fn(() => ({ t }));
+const mockUpdateCommitteeAction = jest.fn(() => );
+const mockLovelacesToAdaString = jest.fn((val) => val);
+const mockedCExpolorerBaseUrl = 'mockedCExpolorerBaseUrl';
+const mockUseCExpolorerBaseUrl = jest.fn(() => mockedCExpolorerBaseUrl);
+import { Wallet } from '@lace/cardano';
+import * as React from 'react';
+import { cleanup, render } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { act } from 'react-dom/test-utils';
+import { UpdateCommitteeActionContainer } from '../UpdateCommitteeActionContainer';
+import { getWrapper } from '../../testing.utils';
+
+jest.mock('react-i18next', () => {
+ const original = jest.requireActual('react-i18next');
+ return {
+ __esModule: true,
+ ...original,
+ useTranslation: mockUseTranslation
+ };
+});
+
+jest.mock('@lace/core', () => {
+ const original = jest.requireActual('@lace/core');
+ return {
+ __esModule: true,
+ ...original,
+ UpdateCommitteeAction: mockUpdateCommitteeAction
+ };
+});
+
+jest.mock('@src/stores', () => ({
+ ...jest.requireActual('@src/stores'),
+ useWalletStore: mockUseWalletStore
+}));
+
+jest.mock('../../hooks', () => {
+ const original = jest.requireActual('../../hooks');
+ return {
+ __esModule: true,
+ ...original,
+ useCExpolorerBaseUrl: mockUseCExpolorerBaseUrl
+ };
+});
+
+jest.mock('@lace/cardano', () => {
+ const actual = jest.requireActual('@lace/cardano');
+ return {
+ __esModule: true,
+ ...actual,
+ Wallet: {
+ ...actual.Wallet,
+ util: {
+ ...actual.Wallet.util,
+ lovelacesToAdaString: mockLovelacesToAdaString
+ }
+ }
+ };
+});
+
+const dappInfo = {
+ name: 'dappName',
+ logo: 'dappLogo',
+ url: 'dappUrl'
+};
+const errorMessage = 'errorMessage';
+const deposit = BigInt('10000');
+const rewardAccount = Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj');
+const anchor = {
+ url: 'anchorUrl',
+ dataHash: Wallet.Crypto.Hash32ByteBase16(Buffer.from('anchorDataHashanchorDataHashanch').toString('hex'))
+};
+
+const updateCommittee = {
+ membersToBeAdded: new Set([
+ {
+ coldCredential: {
+ type: 0,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti1').toString('hex'))
+ },
+ epoch: 1
+ },
+ {
+ coldCredential: {
+ type: 1,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti2').toString('hex'))
+ },
+ epoch: 2
+ }
+ ]),
+ membersToBeRemoved: new Set([
+ {
+ type: 0,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti2').toString('hex'))
+ },
+ {
+ type: 1,
+ hash: Wallet.Crypto.Hash28ByteBase16(Buffer.from('updateCommitteecoldCredenti3').toString('hex'))
+ }
+ ]),
+ governanceActionId: {
+ actionIndex: 123,
+ id: Wallet.Cardano.TransactionId('724a0a88b9470a714fc5bf84daf5851fa259a9b89e1a5453f6f5cd6595ad9821')
+ },
+ __typename: Wallet.Cardano.GovernanceActionType.update_committee
+} as Wallet.Cardano.UpdateCommittee;
+
+describe('Testing ProposalProceduresContainer component', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+
+ test('should render UpdateCommitteeAction component with proper props', async () => {
+ let queryByTestId: any;
+
+ await act(async () => {
+ ({ queryByTestId } = render(
+ ,
+ {
+ wrapper: getWrapper()
+ }
+ ));
+ });
+
+ expect(queryByTestId('UpdateCommitteeAction')).toBeInTheDocument();
+ expect(mockUpdateCommitteeAction).toHaveBeenLastCalledWith(
+ {
+ dappInfo,
+ data: {
+ txDetails: {
+ txType: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.title'),
+ deposit: `${deposit.toString()} ${cardanoCoinMock.symbol}`,
+ rewardAccount
+ },
+ procedure: {
+ anchor: {
+ url: anchor.url,
+ hash: anchor.dataHash,
+ txHashUrl: `${mockedCExpolorerBaseUrl}/${anchor.dataHash}`
+ }
+ },
+ actionId: {
+ index: updateCommittee.governanceActionId.actionIndex.toString(),
+ id: updateCommittee.governanceActionId.id || ''
+ },
+ membersToBeAdded: [...updateCommittee.membersToBeAdded].map(({ coldCredential: { hash }, epoch }) => ({
+ coldCredential: {
+ hash: hash.toString()
+ },
+ epoch: epoch.toString()
+ })),
+ membersToBeRemoved: [...updateCommittee.membersToBeRemoved].map(({ hash }) => ({ hash: hash.toString() }))
+ },
+ translations: {
+ txDetails: {
+ title: t('core.ProposalProcedure.txDetails.title'),
+ txType: t('core.ProposalProcedure.txDetails.txType'),
+ deposit: t('core.ProposalProcedure.txDetails.deposit'),
+ rewardAccount: t('core.ProposalProcedure.txDetails.rewardAccount')
+ },
+ procedure: {
+ title: t('core.ProposalProcedure.procedure.title'),
+ anchor: {
+ url: t('core.ProposalProcedure.procedure.anchor.url'),
+ hash: t('core.ProposalProcedure.procedure.anchor.hash')
+ }
+ },
+ actionId: {
+ title: t('core.ProposalProcedure.governanceAction.actionId.title'),
+ index: t('core.ProposalProcedure.governanceAction.actionId.index'),
+ txId: t('core.ProposalProcedure.governanceAction.actionId.txId')
+ },
+ membersToBeAdded: {
+ title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.title'),
+ coldCredential: {
+ hash: t(
+ 'core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.hash'
+ ),
+ epoch: t(
+ 'core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeAdded.coldCredential.epoch'
+ )
+ }
+ },
+ membersToBeRemoved: {
+ title: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.title'),
+ hash: t('core.ProposalProcedure.governanceAction.updateCommitteeAction.membersToBeRemoved.hash')
+ }
+ },
+ errorMessage
+ },
+ {}
+ );
+ });
+});
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx
new file mode 100644
index 0000000000..4d4cff1643
--- /dev/null
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/testing.utils.tsx
@@ -0,0 +1,38 @@
+import { I18nextProvider } from 'react-i18next';
+import { StoreProvider } from '@src/stores';
+import {
+ AnalyticsProvider,
+ AppSettingsProvider,
+ BackgroundServiceAPIProvider,
+ BackgroundServiceAPIProviderProps,
+ DatabaseProvider
+} from '@src/providers';
+import { APP_MODE_BROWSER } from '@src/utils/constants';
+import i18n from '@lib/i18n';
+import { PostHogClientProvider } from '@providers/PostHogClientProvider';
+import { postHogClientMocks } from '@src/utils/mocks/test-helpers';
+import React from 'react';
+
+const backgroundService = {
+ getBackgroundStorage: jest.fn(),
+ setBackgroundStorage: jest.fn()
+} as unknown as BackgroundServiceAPIProviderProps['value'];
+
+export const getWrapper =
+ () =>
+ ({ children }: { children: React.ReactNode }): React.ReactElement =>
+ (
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+ );
diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts
index a92dc323c5..664bfe32d7 100644
--- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts
+++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/utils.ts
@@ -67,11 +67,15 @@ export const votingProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano
tx?.body?.votingProcedures;
// eslint-disable-next-line complexity
+export const proposalProceduresInspector = (tx: Wallet.Cardano.Tx): Wallet.Cardano.ProposalProcedure[] | undefined =>
+ tx?.body?.proposalProcedures;
+
export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => {
const inspector = createTxInspector({
minted: assetsMintedInspector,
burned: assetsBurnedInspector,
votingProcedures: votingProceduresInspector,
+ proposalProcedures: proposalProceduresInspector,
dRepRegistration: certificateInspectorFactory(CertificateType.RegisterDelegateRepresentative),
dRepRetirement: certificateInspectorFactory(CertificateType.UnregisterDelegateRepresentative),
dRepUpdate: certificateInspectorFactory(CertificateType.UpdateDelegateRepresentative),
@@ -93,11 +97,16 @@ export const getTxType = (tx: Wallet.Cardano.Tx): Wallet.Cip30TxType => {
stakeVoteDelegation,
voteRegistrationDelegation,
stakeRegistrationDelegation,
- stakeVoteDelegationRegistration
+ stakeVoteDelegationRegistration,
+ proposalProcedures
} = inspector(tx as Wallet.Cardano.HydratedTx);
const isMintTransaction = minted.length > 0;
const isBurnTransaction = burned.length > 0;
+ if (proposalProcedures) {
+ return Wallet.Cip30TxType.ProposalProcedures;
+ }
+
if (votingProcedures) {
return Wallet.Cip30TxType.VotingProcedures;
}
diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json
index 23463cdad6..26e7307368 100644
--- a/apps/browser-extension-wallet/src/lib/translations/en.json
+++ b/apps/browser-extension-wallet/src/lib/translations/en.json
@@ -1130,6 +1130,186 @@
"tryingToUseAssetNotInWallet": "This DApp is trying to use token not held in your wallet.",
"noCollateral": "Wallet should not be able to sign dapp txs without collateral."
},
+ "ProposalProcedure": {
+ "dRepId": "DRep ID",
+ "txDetails": {
+ "deposit": "Deposit",
+ "rewardAccount": "Reward account",
+ "title": "Transaction Details",
+ "txType": "Transaction Type"
+ },
+ "procedure": {
+ "anchor": {
+ "hash": "Anchor Hash",
+ "url": "Anchor URL"
+ },
+ "title": "Procedure",
+ "dRepId": "DRep ID"
+ },
+ "governanceAction": {
+ "actionId": {
+ "title": "Action ID",
+ "index": "Index",
+ "txId": "TX ID"
+ },
+ "hardForkInitiation": {
+ "title": "Hard Fork Initiation",
+ "protocolVersion": {
+ "major": "Protocol Version Major",
+ "minor": "Protocol Version Minor",
+ "patch": "Protocol Version Patch"
+ }
+ },
+ "newConstitutionAction": {
+ "title": "New Constitution Action",
+ "constitution": {
+ "title": "Constitution Details",
+ "anchor": {
+ "dataHash": "Anchor Data Hash",
+ "url": "Constitution Anchor URL"
+ },
+ "scriptHash": "Constitution Script Hash"
+ }
+ },
+ "infoAction": {
+ "title": "Info Action"
+ },
+ "noConfidenceAction": {
+ "title": "No Confidence"
+ },
+ "protocolParamUpdate": {
+ "title": "Protocol Parameter Update",
+ "memory": "Memory",
+ "step": "Step",
+ "networkGroup": {
+ "title": "Network group",
+ "maxBBSize": "Max BB Size",
+ "maxTxSize": "Max Tx Size",
+ "maxBHSize": "Max BH Size",
+ "maxValSize": "Max Val Size",
+ "maxTxExUnits": "Max TX Ex Units",
+ "maxBlockExUnits": "Max Blk Ex Units",
+ "maxCollateralInputs": "Max Coll Inputs",
+ "tooltip": {
+ "maxBBSize": "Max block body size",
+ "maxTxSize": "Max transaction size",
+ "maxBHSize": "Max block header size",
+ "maxValSize": "Max size of a serialized asset value",
+ "maxTxExUnits": "Max script execution units in a single transaction",
+ "maxBlockExUnits": "Max script execution units in a single block",
+ "maxCollateralInputs": "Max number of collateral inputs"
+ }
+ },
+ "economicGroup": {
+ "title": "Economic group",
+ "minFeeA": "Min Fee A",
+ "minFeeB": "Min Fee B",
+ "keyDeposit": "Key Deposit",
+ "poolDeposit": "Pool Deposit",
+ "rho": "Rho",
+ "tau": "Tau",
+ "minPoolCost": "Min Pool Cost",
+ "coinsPerUTxOByte": "Coins/UTxO Byte",
+ "prices": "Price",
+ "tooltip": {
+ "minFeeA": "Min fee coefficient",
+ "minFeeB": "Min fee constant",
+ "keyDeposit": "Delegation key Lovelace deposit",
+ "poolDeposit": "Pool registration Lovelace deposit",
+ "rho": "Monetary expansion",
+ "tau": "Treasury expansion",
+ "minPoolCost": "Min fixed rewards cut for pools",
+ "coinsPerUTxOByte": "Min Lovelace deposit per byte of serialized UTxO",
+ "prices": "Prices of Plutus execution units"
+ }
+ },
+ "technicalGroup": {
+ "title": "Technical group",
+ "a0": "A0",
+ "eMax": "EMax",
+ "nOpt": "NOpt",
+ "costModels": "Cost Models",
+ "collateralPercentage": "Coll Percentage",
+ "tooltip": {
+ "a0": "Pool pledge influence",
+ "eMax": "Pool retirement maximum epoch",
+ "nOpt": "Desired number of pools",
+ "costModels": "Plutus execution cost models",
+ "collateralPercentage": "Proportion of collateral needed for scripts"
+ }
+ },
+ "governanceGroup": {
+ "title": "Governance group",
+ "govActionLifetime": "Gov Act Lifetime",
+ "govActionDeposit": "Gov Act Deposit",
+ "drepDeposit": "DRep Deposit",
+ "drepActivity": "DRep Activity",
+ "ccMinSize": "CC Min Size",
+ "ccMaxTermLength": "CC Max Term Length",
+ "dRepVotingThresholds": {
+ "title": "Governance voting thresholds",
+ "motionNoConfidence": "Motion No Conf",
+ "committeeNormal": "Comm Normal",
+ "committeeNoConfidence": "Comm No Conf",
+ "updateConstitution": "Update Const",
+ "hardForkInitiation": "Hard Fork Init",
+ "ppNetworkGroup": "PP Network Grp",
+ "ppEconomicGroup": "PP Economic Grp",
+ "ppTechnicalGroup": "PP Technical Grp",
+ "ppGovernanceGroup": "PP Governance Grp",
+ "treasuryWithdrawal": "Treasury Withdraw"
+ },
+ "tooltip": {
+ "govActionLifetime": "governance action maximum lifetime in epochs",
+ "govActionDeposit": "governance action deposit",
+ "drepDeposit": "DRep deposit amount",
+ "drepActivity": "DRep activity period in epochs",
+ "ccMinSize": "Min constitutional committee size",
+ "ccMaxTermLength": "Max term length (in epochs) for the constitutional committee members",
+ "dRepVotingThresholds": {
+ "title": "Governance voting thresholds",
+ "motionNoConfidence": "1. Motion of no-confidence",
+ "committeeNormal": "2a. New committee/threshold (normal state)",
+ "committeeNoConfidence": "2b. New committee/threshold (state of no-confidence)",
+ "updateConstitution": "3. Update to the Constitution or proposal policy",
+ "hardForkInitiation": "4. Hard-fork initiation",
+ "ppNetworkGroup": "5a. Protocol parameter changes, network group",
+ "ppEconomicGroup": "5b. Protocol parameter changes, economic group",
+ "ppTechnicalGroup": "5c. Protocol parameter changes, technical group",
+ "ppGovernanceGroup": "5d. Protocol parameter changes, governance group",
+ "treasuryWithdrawal": "6. Treasury withdrawal"
+ }
+ }
+ }
+ },
+ "treasuryWithdrawals": {
+ "title": "Treasury Withdrawals",
+ "withdrawals": {
+ "lovelace": "Withdrawal Amount",
+ "rewardAccount": "Withdrawal Reward Account"
+ }
+ },
+ "updateCommitteeAction": {
+ "title": "Update Committee Action",
+ "membersToBeAdded": {
+ "title": "Members To Be Added",
+ "coldCredential": {
+ "hash": "Cold Credential Hash",
+ "epoch": "Epoch"
+ }
+ },
+ "membersToBeRemoved": {
+ "title": "Members To Be Removed",
+ "hash": "Hash"
+ },
+ "newQuorumThreshold": {
+ "title": "New Quorum Threshold",
+ "denominator": "Denominator",
+ "numerator": "Numerator"
+ }
+ }
+ }
+ },
"VotingProcedures": {
"title": "Confirm Vote",
"voterType": "Voter type",
diff --git a/packages/cardano/src/wallet/types.ts b/packages/cardano/src/wallet/types.ts
index 7a4f78c0ad..9c2091494f 100644
--- a/packages/cardano/src/wallet/types.ts
+++ b/packages/cardano/src/wallet/types.ts
@@ -40,7 +40,8 @@ export enum Cip30TxType {
VoteRegistrationDelegation = 'VoteRegistrationDelegation',
StakeRegistrationDelegation = 'StakeRegistrationDelegation',
StakeVoteDelegationRegistration = 'StakeVoteDelegationRegistration',
- StakeVoteDelegation = 'StakeVoteDelegation'
+ StakeVoteDelegation = 'StakeVoteDelegation',
+ ProposalProcedures = 'ProposalProcedures'
}
export type Cip30SignTxOutput = {
diff --git a/packages/core/.storybook/preview.js b/packages/core/.storybook/preview.js
index 7ffa549eec..4a32e4e72b 100644
--- a/packages/core/.storybook/preview.js
+++ b/packages/core/.storybook/preview.js
@@ -4,6 +4,16 @@ import 'normalize.css';
import './theme.scss';
import { ThemeColorScheme, ThemeProvider } from '@lace/ui';
+export const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
export const preview = {
parameters: {
actions: { argTypesRegex: '^on[A-Z].*' },
@@ -12,6 +22,10 @@ export const preview = {
color: /(background|color)$/i,
date: /Date$/
}
+ },
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'Popup'
}
}
};
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 70019dfd2c..d8cc6891e4 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -37,3 +37,4 @@ export * from '@ui/components/ConfirmStakeRegistrationDelegation';
export * from '@ui/components/ConfirmStakeVoteRegistrationDelegation';
export * from '@ui/components/ConfirmVoteRegistrationDelegation';
export * from '@ui/components/VotingProcedures';
+export * from '@ui/components/ProposalProcedures';
diff --git a/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx
similarity index 100%
rename from packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.ts
rename to packages/core/src/ui/components/ConfirmStakeRegistrationDelegation/ConfirmStakeRegistrationDelegation.stories.tsx
diff --git a/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx
similarity index 100%
rename from packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.ts
rename to packages/core/src/ui/components/ConfirmStakeVoteDelegation/ConfirmStakeVoteDelegation.stories.tsx
diff --git a/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx
similarity index 100%
rename from packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.ts
rename to packages/core/src/ui/components/ConfirmStakeVoteRegistrationDelegation/ConfirmStakeVoteRegistrationDelegation.stories.tsx
diff --git a/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts b/packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx
similarity index 100%
rename from packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.ts
rename to packages/core/src/ui/components/ConfirmVoteRegistrationDelegation/ConfirmVoteRegistrationDelegation.stories.tsx
diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx
index 56f29d0531..07525eac9c 100644
--- a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx
+++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx
@@ -2,6 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { DappTransaction } from './DappTransaction';
import { ComponentProps } from 'react';
+import { Wallet } from '@lace/cardano';
const meta: Meta = {
title: 'DappTransaction',
@@ -20,13 +21,6 @@ const data: ComponentProps = {
name: 'Mint',
url: 'https://preprod.mint.handle.me'
},
- translations: {
- recipient: 'Recipient',
- amount: 'Amount',
- adaFollowingNumericValue: 'ADA',
- fee: 'Fee',
- transaction: 'Transaction'
- },
transaction: {
fee: '0.17',
outputs: [
@@ -36,7 +30,7 @@ const data: ComponentProps = {
'addr_test1qrl0s3nqfljv8dfckn7c4wkzu5rl6wn4hakkddcz2mczt3szlqss933x0aag07qcgspcaglmay6ufl4y4lalmlpe02mqhl0fx2'
}
],
- type: 'Mint'
+ type: Wallet.Cip30TxType.Mint
}
};
diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx
new file mode 100644
index 0000000000..b50685d593
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.stories.tsx
@@ -0,0 +1,98 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { HardForkInitiationAction } from './HardForkInitiationAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/HardForkInitiationAction',
+ component: HardForkInitiationAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'Hard Fork Initiation',
+ deposit: '2000',
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'
+ },
+ procedure: {
+ anchor: {
+ hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io'
+ }
+ },
+ actionId: {
+ index: '0',
+ id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0'
+ },
+ protocolVersion: {
+ major: '5',
+ minor: '1',
+ patch: '1'
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type',
+ deposit: 'Deposit',
+ rewardAccount: 'Reward account'
+ },
+ procedure: {
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ title: 'Procedure'
+ },
+ actionId: {
+ title: 'Action ID',
+ index: 'Index',
+ txId: 'TX ID'
+ },
+ protocolVersion: {
+ major: 'Protocol Version Major',
+ minor: 'Protocol Version Minor',
+ patch: 'Protocol Version Patch'
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx
new file mode 100644
index 0000000000..6a722f1018
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationAction.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, Metadata, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import * as Types from './HardForkInitiationActionTypes';
+import { TransactionDetails } from '../components/TransactionDetails';
+import { Procedure } from '../components/Procedure';
+import { ActionId } from '../components/ActionId';
+
+export interface HardForkInitiationActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const HardForkInitiationAction = ({
+ dappInfo,
+ errorMessage,
+ data: { procedure, txDetails, actionId, protocolVersion },
+ translations
+}: HardForkInitiationActionProps): JSX.Element => (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+ {/* tx details section */}
+
+ |
+
+ |
+ {/* procedure section */}
+
+
+
+ |
+
+
+ |
+ {protocolVersion.patch && (
+
+
+ |
+ )}
+ {/* action id section*/}
+ {actionId && (
+ <>
+
+
+ |
+
+ >
+ )}
+
+
+);
diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts
new file mode 100644
index 0000000000..bd8ae61b7d
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/HardForkInitiationActionTypes.ts
@@ -0,0 +1,25 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as ActionIdTypes from '../components/ActionIdTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+
+export interface Data {
+ txDetails: TxDetailsTypes.TxDetails;
+ procedure: ProcedureTypes.Procedure;
+ actionId?: ActionIdTypes.Data;
+ protocolVersion: {
+ major: string;
+ minor: string;
+ patch?: string;
+ };
+}
+
+export interface Translations {
+ txDetails: TxDetailsTypes.Translations;
+ procedure: ProcedureTypes.Translations;
+ actionId?: ActionIdTypes.Translations;
+ protocolVersion: {
+ major: string;
+ minor: string;
+ patch: string;
+ };
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts
new file mode 100644
index 0000000000..94f3dd74c4
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/HardForkInitiationAction/index.ts
@@ -0,0 +1 @@
+export { HardForkInitiationAction } from './HardForkInitiationAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx
new file mode 100644
index 0000000000..3f97136d22
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.stories.tsx
@@ -0,0 +1,75 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { InfoAction } from './InfoAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/InfoAction',
+ component: InfoAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'Info action'
+ },
+ procedure: {
+ anchor: {
+ hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io'
+ }
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type'
+ },
+ procedure: {
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ title: 'Procedure'
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx
new file mode 100644
index 0000000000..b84260bd9a
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoAction.tsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import * as Types from './InfoActionTypes';
+import { TransactionDetails } from '../components/TransactionDetails';
+import { Procedure } from '../components/Procedure';
+
+export interface InfoActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const InfoAction = ({
+ dappInfo,
+ errorMessage,
+ data: { procedure, txDetails },
+ translations
+}: InfoActionProps): JSX.Element => (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+
+
+
+ |
+ {/* procedure section */}
+
+
+
+);
diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts
new file mode 100644
index 0000000000..2f8853991e
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/InfoActionTypes.ts
@@ -0,0 +1,11 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+export interface Data {
+ txDetails: TxDetailsTypes.TxDetails;
+ procedure: ProcedureTypes.Procedure;
+}
+
+export interface Translations {
+ txDetails: TxDetailsTypes.Translations;
+ procedure: ProcedureTypes.Translations;
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts
new file mode 100644
index 0000000000..f6fae454f1
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/InfoAction/index.ts
@@ -0,0 +1 @@
+export { InfoAction } from './InfoAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx
new file mode 100644
index 0000000000..d6fc007a4c
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.stories.tsx
@@ -0,0 +1,103 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { NewConstitutionAction } from './NewConstitutionAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/NewConstitutionAction',
+ component: NewConstitutionAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'New Constitution',
+ deposit: '2000',
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'
+ },
+ procedure: {
+ anchor: {
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io/'
+ }
+ },
+ actionId: {
+ index: '0',
+ id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0'
+ },
+ constitution: {
+ anchor: {
+ dataHash: '0000000000000000000000000000000000000000000000000000000000000000',
+ url: 'https://www.someurl.io'
+ },
+ scriptHash: 'cb0ec2692497b458e46812c8a5bfa2931d1a2d965a99893828ec810f'
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type',
+ deposit: 'Deposit',
+ rewardAccount: 'Reward account'
+ },
+ procedure: {
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ title: 'Procedure'
+ },
+ actionId: {
+ title: 'Action ID',
+ index: 'Index',
+ txId: 'TX ID'
+ },
+ constitution: {
+ title: 'Constitution Details',
+ anchor: {
+ dataHash: 'Anchor Data Hash',
+ url: 'Anchor URL'
+ },
+ scriptHash: 'Script Hash'
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx
new file mode 100644
index 0000000000..7fb710ab03
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionAction.tsx
@@ -0,0 +1,63 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, Metadata, MetadataLink, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import * as Types from './NewConstitutionActionTypes';
+import { TransactionDetails } from '../components/TransactionDetails';
+import { Procedure } from '../components/Procedure';
+import { ActionId } from '../components/ActionId';
+
+export interface NewConstitutionActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const NewConstitutionAction = ({
+ dappInfo,
+ errorMessage,
+ data: { txDetails, procedure, constitution, actionId },
+ translations
+}: NewConstitutionActionProps): JSX.Element => (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+ {/* txDetails section */}
+
+
+
+ |
+ {/* procedure section */}
+
+
+
+ |
+ {constitution.scriptHash && (
+
+
+ |
+ )}
+ {/* action id section*/}
+ {actionId && (
+ <>
+
+
+ |
+
+ >
+ )}
+
+
+);
diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts
new file mode 100644
index 0000000000..4d53001a2d
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/NewConstitutionActionTypes.ts
@@ -0,0 +1,30 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as ActionIdTypes from '../components/ActionIdTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+
+export interface Data {
+ procedure: ProcedureTypes.Procedure;
+ actionId?: ActionIdTypes.Data;
+ txDetails: TxDetailsTypes.TxDetails;
+ constitution: {
+ anchor: {
+ dataHash: string;
+ url: string;
+ };
+ scriptHash: string;
+ };
+}
+
+export interface Translations {
+ procedure: ProcedureTypes.Translations;
+ actionId?: ActionIdTypes.Translations;
+ txDetails: TxDetailsTypes.Translations;
+ constitution: {
+ title: string;
+ anchor: {
+ dataHash: string;
+ url: string;
+ };
+ scriptHash: string;
+ };
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts
new file mode 100644
index 0000000000..2e50ba16ea
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NewConstitutionAction/index.ts
@@ -0,0 +1 @@
+export { NewConstitutionAction } from './NewConstitutionAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx
new file mode 100644
index 0000000000..81092e9bfe
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.stories.tsx
@@ -0,0 +1,88 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { NoConfidenceAction } from './NoConfidenceAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/NoConfidenceAction',
+ component: NoConfidenceAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'No Confidence',
+ deposit: '2000',
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'
+ },
+ procedure: {
+ anchor: {
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io/'
+ }
+ },
+ actionId: {
+ index: '0',
+ id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0'
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type',
+ deposit: 'Deposit',
+ rewardAccount: 'Reward account'
+ },
+ procedure: {
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ title: 'Procedure'
+ },
+ actionId: {
+ title: 'Action ID',
+ index: 'Index',
+ txId: 'TX ID'
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx
new file mode 100644
index 0000000000..73034a6fe9
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceAction.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import * as Types from './NoConfidenceActionTypes';
+import { TransactionDetails } from '../components/TransactionDetails';
+import { Procedure } from '../components/Procedure';
+import { ActionId } from '../components/ActionId';
+
+export interface NoConfidenceActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const NoConfidenceAction = ({
+ dappInfo,
+ errorMessage,
+ data: { procedure, txDetails, actionId },
+ translations
+}: NoConfidenceActionProps): JSX.Element => (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+ {/* tx details section */}
+
+
+
+ |
+ {/* procedure section */}
+
+ {/* action id section*/}
+ {actionId && (
+ <>
+
+
+ |
+
+ >
+ )}
+
+
+);
diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts
new file mode 100644
index 0000000000..0cdfcea7d0
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/NoConfidenceActionTypes.ts
@@ -0,0 +1,15 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as ActionIdTypes from '../components/ActionIdTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+
+export interface Data {
+ procedure: ProcedureTypes.Procedure;
+ actionId?: ActionIdTypes.Data;
+ txDetails: TxDetailsTypes.TxDetails;
+}
+
+export interface Translations {
+ procedure: ProcedureTypes.Translations;
+ actionId?: ActionIdTypes.Translations;
+ txDetails: TxDetailsTypes.Translations;
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts
new file mode 100644
index 0000000000..2c9c805b7b
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/NoConfidenceAction/index.ts
@@ -0,0 +1 @@
+export { NoConfidenceAction } from './NoConfidenceAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx
new file mode 100644
index 0000000000..0e92b3b4d3
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/EconomicGroup.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { Metadata, Text, sx, Cell } from '@lace/ui';
+import { Card } from '../components/Card';
+import * as Types from './ParameterChangeActionTypes';
+
+interface Props {
+ economicGroup: Types.EconomicGroup;
+ translations: Types.Translations['economicGroup'];
+}
+
+export const EconomicGroup = ({ economicGroup, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx
new file mode 100644
index 0000000000..e0aa03098a
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/GovernanceGroup.tsx
@@ -0,0 +1,144 @@
+import React from 'react';
+import { Metadata, Text, sx, Divider, Cell } from '@lace/ui';
+import * as Types from './ParameterChangeActionTypes';
+
+interface Props {
+ governanceGroup: Types.GovernanceGroup;
+ translations: Types.Translations['governanceGroup'];
+}
+
+export const GovernanceGroup = ({ governanceGroup, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ {translations.dRepVotingThresholds.title}
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx
new file mode 100644
index 0000000000..88605cbc76
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/NetworkGroup.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import { Metadata, Text, sx, Cell } from '@lace/ui';
+import * as Types from './ParameterChangeActionTypes';
+
+interface Props {
+ networkGroup: Types.NetworkGroup;
+ translations: Types.Translations['networkGroup'];
+}
+
+export const NetworkGroup = ({ networkGroup, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx
new file mode 100644
index 0000000000..2e4fababde
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.stories.tsx
@@ -0,0 +1,243 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { ParameterChangeAction } from './ParameterChangeAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/ParameterChangeAction',
+ component: ParameterChangeAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'Protocol Parameter Update',
+ deposit: '2000',
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'
+ },
+ anchor: {
+ hash: '0000000000000000000000000000000000000000000000000000000000000000',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io/'
+ },
+ protocolParamUpdate: {
+ maxBlockExUnits: {
+ memory: '50000000',
+ step: '4000000000'
+ },
+ maxTxExUnits: {
+ memory: '10000000',
+ step: '10000000000'
+ },
+ networkGroup: {
+ maxBBSize: '65536',
+ maxBHSize: '1100',
+ maxTxSize: '16384',
+ maxCollateralInputs: '3',
+ maxValSize: '5000'
+ },
+ economicGroup: {
+ minFeeA: '44',
+ minFeeB: '155381',
+ keyDeposit: '2000000',
+ poolDeposit: '500000000',
+ minPoolCost: '340000000',
+ coinsPerUTxOByte: '34482',
+ price: {
+ memory: '0.0577',
+ step: '0.0000721'
+ },
+ rho: '0.003',
+ tau: '0.2'
+ },
+ technicalGroup: {
+ a0: '0.3',
+ nOpt: '150',
+ collateralPercentage: '150',
+ costModels: {
+ PlutusV1: {
+ 'addInteger-cpu-arguments-intercept': '197_209',
+ 'addInteger-cpu-arguments-slope': '0'
+ },
+ PlutusV2: {
+ 'addInteger-cpu-arguments-intercept': '197_209',
+ 'addInteger-cpu-arguments-slope': '0'
+ }
+ },
+ eMax: '18'
+ },
+ governanceGroup: {
+ govActionLifetime: '14',
+ govActionDeposit: '0',
+ ccMaxTermLength: '60',
+ ccMinSize: '0',
+ drepActivity: '0',
+ drepDeposit: '0',
+ dRepVotingThresholds: {
+ motionNoConfidence: '0.51',
+ committeeNormal: '0.51',
+ committeeNoConfidence: '0.51',
+ updateToConstitution: '0.51',
+ hardForkInitiation: '0.51',
+ ppNetworkGroup: '0.51',
+ ppEconomicGroup: '0.51',
+ ppTechnicalGroup: '0.51',
+ ppGovGroup: '0.51',
+ treasuryWithdrawal: '0.51'
+ }
+ }
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type',
+ deposit: 'Deposit',
+ rewardAccount: 'Reward account'
+ },
+ memory: 'Memory',
+ step: 'Step',
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ networkGroup: {
+ title: 'Network group',
+ maxBBSize: 'Max BB Size',
+ maxTxSize: 'Max Tx Size',
+ maxBHSize: 'Max BH Size',
+ maxValSize: 'Max Val Size',
+ maxTxExUnits: 'Max TX Ex Units',
+ maxBlockExUnits: 'Max Blk Ex Units',
+ maxCollateralInputs: 'Max Coll Inputs',
+ tooltip: {
+ maxBBSize: 'Max block body size',
+ maxTxSize: 'Max transaction size',
+ maxBHSize: 'Max block header size',
+ maxValSize: 'Max size of a serialized asset value',
+ maxTxExUnits: 'Max script execution units in a single transaction',
+ maxBlockExUnits: 'Max script execution units in a single block',
+ maxCollateralInputs: 'Max number of collateral inputs'
+ }
+ },
+ economicGroup: {
+ title: 'Economic group',
+ minFeeA: 'Min Fee A',
+ minFeeB: 'Min Fee B',
+ keyDeposit: 'Key Deposit',
+ poolDeposit: 'Pool Deposit',
+ rho: 'Rho',
+ tau: 'Tau',
+ minPoolCost: 'Min Pool Cost',
+ coinsPerUTxOByte: 'Coins/UTxO Byte',
+ prices: 'Price',
+ tooltip: {
+ minFeeA: 'Min fee coefficient',
+ minFeeB: 'Min fee constant',
+ keyDeposit: 'Delegation key Lovelace deposit',
+ poolDeposit: 'Pool registration Lovelace deposit',
+ rho: 'Monetary expansion',
+ tau: 'Treasury expansion',
+ minPoolCost: 'Min fixed rewards cut for pools',
+ coinsPerUTxOByte: 'Min Lovelace deposit per byte of serialized UTxO',
+ prices: 'Prices of Plutus execution units'
+ }
+ },
+ technicalGroup: {
+ title: 'Technical group',
+ a0: 'A0',
+ eMax: 'EMax',
+ nOpt: 'NOpt',
+ costModels: 'Cost Models',
+ collateralPercentage: 'Coll Percentage',
+ tooltip: {
+ a0: 'Pool pledge influence',
+ eMax: 'Pool retirement maximum epoch',
+ nOpt: 'Desired number of pools',
+ costModels: 'Plutus execution cost models',
+ collateralPercentage: 'Proportion of collateral needed for scripts'
+ }
+ },
+ governanceGroup: {
+ title: 'Governance group',
+ govActionLifetime: 'Gov Act Lifetime',
+ govActionDeposit: 'Gov Act Deposit',
+ drepDeposit: 'DRep Deposit',
+ drepActivity: 'DRep Activity',
+ ccMinSize: 'CC Min Size',
+ ccMaxTermLength: 'CC Max Term Length',
+ dRepVotingThresholds: {
+ title: 'Governance voting thresholds',
+ motionNoConfidence: 'Motion No Conf',
+ committeeNormal: 'Comm Normal',
+ committeeNoConfidence: 'Comm No Conf',
+ updateConstitution: 'Update Const',
+ hardForkInitiation: 'Hard Fork Init',
+ ppNetworkGroup: 'PP Network Grp',
+ ppEconomicGroup: 'PP Economic Grp',
+ ppTechnicalGroup: 'PP Technical Grp',
+ ppGovernanceGroup: 'PP Governance Grp',
+ treasuryWithdrawal: 'Treasury Withdraw'
+ },
+ tooltip: {
+ govActionLifetime: 'governance action maximum lifetime in epochs',
+ govActionDeposit: 'governance action deposit',
+ drepDeposit: 'DRep deposit amount',
+ drepActivity: 'DRep activity period in epochs',
+ ccMinSize: 'Min constitutional committee size',
+ ccMaxTermLength: 'Max term length (in epochs) for the constitutional committee members',
+ dRepVotingThresholds: {
+ title: 'DRep voting thresholds',
+ motionNoConfidence: '1. Motion of no-confidence',
+ committeeNormal: '2a. New committee/threshold (normal state)',
+ committeeNoConfidence: '2b. New committee/threshold (state of no-confidence)',
+ updateConstitution: '3. Update to the Constitution or proposal policy',
+ hardForkInitiation: '4. Hard-fork initiation',
+ ppNetworkGroup: '5a. Protocol parameter changes, network group',
+ ppEconomicGroup: '5b. Protocol parameter changes, economic group',
+ ppTechnicalGroup: '5c. Protocol parameter changes, technical group',
+ ppGovernanceGroup: '5d. Protocol parameter changes, governance group',
+ treasuryWithdrawal: '6. Treasury withdrawal'
+ }
+ }
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx
new file mode 100644
index 0000000000..871101f707
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeAction.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, Metadata, MetadataLink, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import { TransactionDetails } from '../components/TransactionDetails';
+import * as Types from './ParameterChangeActionTypes';
+import { EconomicGroup } from './EconomicGroup';
+import { NetworkGroup } from './NetworkGroup';
+import { TechnicalGroup } from './TechnicalGroup';
+import { GovernanceGroup } from './GovernanceGroup';
+import { Card } from '../components/Card';
+
+interface ParameterChangeActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const ParameterChangeAction = ({
+ dappInfo,
+ errorMessage,
+ data: { txDetails, protocolParamUpdate, anchor },
+ translations
+}: ParameterChangeActionProps): JSX.Element => {
+ const { economicGroup, governanceGroup, networkGroup, technicalGroup, maxTxExUnits, maxBlockExUnits } =
+ protocolParamUpdate;
+
+ return (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+ {/* tx details section */}
+
+ <>
+
+
+ |
+
+ {anchor.txHashUrl ? (
+
+ ) : (
+
+ )}
+ |
+ >
+
+
+
+
+
+
+
+ |
+
+
+
+ |
+
+
+
+
+
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts
new file mode 100644
index 0000000000..90efec01da
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/ParameterChangeActionTypes.ts
@@ -0,0 +1,185 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+
+export interface Data {
+ protocolParamUpdate: ProtocolParamUpdate;
+ txDetails: TxDetailsTypes.TxDetails;
+ anchor: ProcedureTypes.Procedure['anchor'];
+}
+
+export interface NetworkGroup {
+ maxBBSize: string;
+ maxTxSize: string;
+ maxBHSize: string;
+ maxValSize: string;
+ maxCollateralInputs: string;
+}
+
+export interface EconomicGroup {
+ minFeeA: string;
+ minFeeB: string;
+ keyDeposit: string;
+ poolDeposit: string;
+ rho: string;
+ tau: string;
+ minPoolCost: string;
+ coinsPerUTxOByte: string;
+ price: {
+ memory: string;
+ step: string;
+ };
+}
+
+export interface TechnicalGroup {
+ a0: string;
+ eMax: string;
+ nOpt: string;
+ costModels: {
+ PlutusV1: Record;
+ PlutusV2: Record;
+ };
+ collateralPercentage: string;
+}
+
+export interface GovernanceGroup {
+ govActionLifetime: string;
+ govActionDeposit: string;
+ drepDeposit: string;
+ drepActivity: string;
+ ccMinSize: string;
+ ccMaxTermLength: string;
+ dRepVotingThresholds: {
+ motionNoConfidence: string;
+ committeeNormal: string;
+ committeeNoConfidence: string;
+ updateToConstitution: string;
+ hardForkInitiation: string;
+ ppNetworkGroup: string;
+ ppEconomicGroup: string;
+ ppTechnicalGroup: string;
+ ppGovGroup: string;
+ treasuryWithdrawal: string;
+ };
+}
+
+interface ProtocolParamUpdate {
+ maxTxExUnits: {
+ memory: string;
+ step: string;
+ };
+ maxBlockExUnits: {
+ memory: string;
+ step: string;
+ };
+ networkGroup: NetworkGroup;
+ economicGroup: EconomicGroup;
+ technicalGroup: TechnicalGroup;
+ governanceGroup: GovernanceGroup;
+}
+
+export interface Translations {
+ txDetails: TxDetailsTypes.Translations;
+ anchor: ProcedureTypes.Translations['anchor'];
+ memory: string;
+ step: string;
+ networkGroup: {
+ title: string;
+ maxBBSize: string;
+ maxTxSize: string;
+ maxBHSize: string;
+ maxValSize: string;
+ maxTxExUnits: string;
+ maxBlockExUnits: string;
+ maxCollateralInputs: string;
+ tooltip: {
+ maxBBSize: string;
+ maxTxSize: string;
+ maxBHSize: string;
+ maxValSize: string;
+ maxTxExUnits: string;
+ maxBlockExUnits: string;
+ maxCollateralInputs: string;
+ };
+ };
+ economicGroup: {
+ title: string;
+ minFeeA: string;
+ minFeeB: string;
+ keyDeposit: string;
+ poolDeposit: string;
+ rho: string;
+ tau: string;
+ minPoolCost: string;
+ coinsPerUTxOByte: string;
+ prices: string;
+ tooltip: {
+ minFeeA: string;
+ minFeeB: string;
+ keyDeposit: string;
+ poolDeposit: string;
+ rho: string;
+ tau: string;
+ minPoolCost: string;
+ coinsPerUTxOByte: string;
+ prices: string;
+ };
+ };
+ technicalGroup: {
+ title: string;
+ a0: string;
+ eMax: string;
+ nOpt: string;
+ costModels: string;
+ collateralPercentage: string;
+ tooltip: {
+ a0: string;
+ eMax: string;
+ nOpt: string;
+ costModels: string;
+ collateralPercentage: string;
+ };
+ };
+ governanceGroup: {
+ title: string;
+ govActionLifetime: string;
+ govActionDeposit: string;
+ drepDeposit: string;
+ drepActivity: string;
+ ccMinSize: string;
+ ccMaxTermLength: string;
+ dRepVotingThresholds: {
+ title: string;
+ motionNoConfidence: string;
+ committeeNormal: string;
+ committeeNoConfidence: string;
+ updateConstitution: string;
+ hardForkInitiation: string;
+ ppNetworkGroup: string;
+ ppEconomicGroup: string;
+ ppTechnicalGroup: string;
+ ppGovernanceGroup: string;
+ treasuryWithdrawal: string;
+ };
+ tooltip: {
+ govActionLifetime: string;
+ govActionDeposit: string;
+ drepDeposit: string;
+ drepActivity: string;
+ ccMinSize: string;
+ ccMaxTermLength: string;
+ dRepVotingThresholds: {
+ title: string;
+ motionNoConfidence: string;
+ committeeNormal: string;
+ committeeNoConfidence: string;
+ updateConstitution: string;
+ hardForkInitiation: string;
+ ppNetworkGroup: string;
+ ppEconomicGroup: string;
+ ppTechnicalGroup: string;
+ ppGovernanceGroup: string;
+ treasuryWithdrawal: string;
+ };
+ };
+ };
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx
new file mode 100644
index 0000000000..fd3776d211
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/TechnicalGroup.tsx
@@ -0,0 +1,58 @@
+import React from 'react';
+import { Metadata, Text, sx, Cell } from '@lace/ui';
+import * as Types from './ParameterChangeActionTypes';
+
+interface Props {
+ technicalGroup: Types.TechnicalGroup;
+ translations: Types.Translations['technicalGroup'];
+}
+
+export const TechnicalGroup = ({ technicalGroup, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ // TODO: review cost model syntax/display
+ // const costModels = Object.entries(technicalGroup.costModels).map(([key, value]) => ({
+ // title: key,
+ // fields: Object.entries(value).map(([cKey, cValue]) => ({
+ // label: cKey,
+ // value: cValue
+ // }))
+ // }));
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+
+
+ |
+ {/* TODO: review cost model syntax/display */}
+ {/*
+
+ {translations.costModels}
+
+ |
+
+ {costModels.map(({ title, fields }, idx) => (
+ 0 ? '$24' : '$0'} mb={costModels.length === idx - 1 ? '$18' : '$0'} key={title}>
+
+
+ ))}
+ | */}
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts
new file mode 100644
index 0000000000..63caf5ba15
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/ParameterChangeAction/index.ts
@@ -0,0 +1 @@
+export { ParameterChangeAction } from './ParameterChangeAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx
new file mode 100644
index 0000000000..951be959cd
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.stories.tsx
@@ -0,0 +1,99 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { TreasuryWithdrawalsAction } from './TreasuryWithdrawalsAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/TreasuryWithdrawalsAction',
+ component: TreasuryWithdrawalsAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'Treasury Withdrawals',
+ deposit: '2000',
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'
+ },
+ procedure: {
+ anchor: {
+ hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io'
+ }
+ },
+ withdrawals: [
+ {
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr',
+ lovelace: '1030939916423'
+ }
+ ],
+ actionId: {
+ index: '0',
+ id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0'
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type',
+ deposit: 'Deposit',
+ rewardAccount: 'Reward account'
+ },
+ procedure: {
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ title: 'Procedure'
+ },
+ actionId: {
+ title: 'Action ID',
+ index: 'Index',
+ txId: 'TX ID'
+ },
+ withdrawals: {
+ title: 'Withdrawal Details',
+ lovelace: 'Lovelace Withdrawn',
+ rewardAccount: 'Reward account'
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx
new file mode 100644
index 0000000000..38a8d2196d
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsAction.tsx
@@ -0,0 +1,71 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, sx, Text, Metadata, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import * as Types from './TreasuryWithdrawalsActionTypes';
+import { TransactionDetails } from '../components/TransactionDetails';
+import { ActionId } from '../components/ActionId';
+import { Procedure } from '../components/Procedure';
+
+interface TreasuryWithdrawalsActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const TreasuryWithdrawalsAction = ({
+ dappInfo,
+ errorMessage,
+ data: { txDetails, procedure, withdrawals, actionId },
+ translations
+}: TreasuryWithdrawalsActionProps): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+ {/* tx details section */}
+
+
+
+ |
+ {/* procedure section */}
+
+
+
+ {translations.withdrawals.title}
+
+ |
+ {withdrawals.map((withdrawal) => (
+
+
+
+ |
+
+
+ |
+
+ ))}
+ {actionId && (
+ <>
+
+
+ |
+
+ >
+ )}
+
+
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts
new file mode 100644
index 0000000000..7de7a0fc0d
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/TreasuryWithdrawalsActionTypes.ts
@@ -0,0 +1,24 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as ActionIdTypes from '../components/ActionIdTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+
+export interface Data {
+ actionId?: ActionIdTypes.Data;
+ txDetails: TxDetailsTypes.TxDetails;
+ procedure: ProcedureTypes.Procedure;
+ withdrawals: Array<{
+ rewardAccount: string;
+ lovelace: string;
+ }>;
+}
+
+export interface Translations {
+ txDetails: TxDetailsTypes.Translations;
+ actionId?: ActionIdTypes.Translations;
+ procedure: ProcedureTypes.Translations;
+ withdrawals: {
+ title: string;
+ rewardAccount: string;
+ lovelace: string;
+ };
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts
new file mode 100644
index 0000000000..2b32267088
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/TreasuryWithdrawalsAction/index.ts
@@ -0,0 +1 @@
+export { TreasuryWithdrawalsAction } from './TreasuryWithdrawalsAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx
new file mode 100644
index 0000000000..9750fa1551
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.stories.tsx
@@ -0,0 +1,130 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { UpdateCommitteeAction } from './UpdateCommitteeActionAction';
+import { ComponentProps } from 'react';
+
+const customViewports = {
+ popup: {
+ name: 'Popup',
+ styles: {
+ width: '360px',
+ height: '600'
+ }
+ }
+};
+
+const meta: Meta = {
+ title: 'ProposalProcedure/UpdateCommitteeAction',
+ component: UpdateCommitteeAction,
+ parameters: {
+ layout: 'centered',
+ viewport: {
+ viewports: customViewports,
+ defaultViewport: 'popup'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+const data: ComponentProps = {
+ dappInfo: {
+ logo: 'https://cdn.mint.handle.me/favicon.png',
+ name: 'Mint',
+ url: 'https://preprod.mint.handle.me'
+ },
+ data: {
+ txDetails: {
+ txType: 'Hard Fork Initiation',
+ deposit: '2000',
+ rewardAccount: 'stake1u89sasnfyjtmgk8ydqfv3fdl52f36x3djedfnzfc9rkgzrcss5vgr'
+ },
+ procedure: {
+ anchor: {
+ hash: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0',
+ url: 'https://www.someurl.io',
+ txHashUrl: 'https://www.someurl.io'
+ }
+ },
+ actionId: {
+ index: '0',
+ id: '26bfdcc75a7f4d0cd8c71f0189bc5ca5ad2f4a3db6240c82b5a0edac7f9203e0'
+ },
+ membersToBeAdded: [
+ {
+ coldCredential: {
+ hash: '30000000000000000000000000000000000000000000000000000000'
+ },
+ epoch: '1'
+ },
+ {
+ coldCredential: {
+ hash: '40000000000000000000000000000000000000000000000000000000'
+ },
+ epoch: '2'
+ }
+ ],
+ membersToBeRemoved: [
+ {
+ hash: '00000000000000000000000000000000000000000000000000000000'
+ },
+ {
+ hash: '20000000000000000000000000000000000000000000000000000000'
+ }
+ ],
+ newQuorumThreshold: {
+ denominator: '5',
+ numerator: '1'
+ }
+ },
+ translations: {
+ txDetails: {
+ title: 'Transaction Details',
+ txType: 'Transaction Type',
+ deposit: 'Deposit',
+ rewardAccount: 'Reward account'
+ },
+ procedure: {
+ anchor: {
+ hash: 'Anchor Hash',
+ url: 'Anchor URL'
+ },
+ title: 'Procedure'
+ },
+ actionId: {
+ title: 'Action ID',
+ index: 'Index',
+ txId: 'TX ID'
+ },
+ membersToBeAdded: {
+ title: 'Members To Be Added',
+ coldCredential: {
+ hash: 'Cold Credential Hash',
+ epoch: 'Epoch'
+ }
+ },
+ membersToBeRemoved: {
+ title: 'Members To Be Removed',
+ hash: 'Hash'
+ },
+ newQuorumThreshold: {
+ title: 'New Quorum Threshold',
+ denominator: 'Denominator',
+ numerator: 'Numerator'
+ }
+ }
+};
+
+export const Overview: Story = {
+ args: {
+ ...data
+ }
+};
+
+export const WithError: Story = {
+ args: {
+ ...data,
+ errorMessage: 'Something went wrong'
+ }
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx
new file mode 100644
index 0000000000..97237316d5
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionAction.tsx
@@ -0,0 +1,97 @@
+import React from 'react';
+import { Box, Grid, Flex, Divider, sx, Text, Metadata, Cell } from '@lace/ui';
+import { DappInfo, DappInfoProps } from '../../DappInfo';
+import { ErrorPane } from '@lace/common';
+import * as Types from './UpdateCommitteeActionTypes';
+import { Procedure } from '../components/Procedure';
+import { TransactionDetails } from '../components/TransactionDetails';
+import { ActionId } from '../components/ActionId';
+
+interface UpdateCommitteeActionProps {
+ dappInfo: Omit;
+ errorMessage?: string;
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const UpdateCommitteeAction = ({
+ dappInfo,
+ errorMessage,
+ data: { procedure, txDetails, membersToBeAdded, membersToBeRemoved, actionId },
+ translations
+}: UpdateCommitteeActionProps): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+
+
+
+
+ {errorMessage && (
+
+
+
+ )}
+
+ {/* tx details section */}
+
+
+
+ |
+ {/* procedure section */}
+
+
+
+ |
+ {membersToBeAdded.length > 0 && (
+ <>
+
+
+ {translations.membersToBeAdded.title}
+
+ |
+ {membersToBeAdded.map(({ coldCredential, epoch }) => (
+
+
+
+ |
+
+
+ |
+
+ ))}
+ >
+ )}
+ {membersToBeRemoved.length > 0 && (
+ <>
+
+
+ {translations.membersToBeRemoved.title}
+
+ |
+ {membersToBeRemoved.map(({ hash }) => (
+
+
+
+ |
+
+
+ |
+
+ ))}
+ >
+ )}
+ {actionId && (
+ <>
+
+
+ |
+
+ >
+ )}
+
+
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts
new file mode 100644
index 0000000000..ce8b90745d
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/UpdateCommitteeActionTypes.ts
@@ -0,0 +1,50 @@
+import * as ProcedureTypes from '../components/ProcedureTypes';
+import * as ActionIdTypes from '../components/ActionIdTypes';
+import * as TxDetailsTypes from '../components/TransactionDetailsTypes';
+
+interface MembersToBeAdded {
+ coldCredential: {
+ hash: string;
+ };
+ epoch: string;
+}
+
+interface MembersToBeRemoved {
+ hash: string;
+}
+
+interface NewQuorumThreshold {
+ denominator: string;
+ numerator: string;
+}
+
+export interface Data {
+ actionId?: ActionIdTypes.Data;
+ txDetails: TxDetailsTypes.TxDetails;
+ procedure: ProcedureTypes.Procedure;
+ membersToBeAdded: MembersToBeAdded[];
+ membersToBeRemoved: MembersToBeRemoved[];
+ newQuorumThreshold?: NewQuorumThreshold;
+}
+
+export interface Translations {
+ procedure: ProcedureTypes.Translations;
+ actionId?: ActionIdTypes.Translations;
+ txDetails: TxDetailsTypes.Translations;
+ membersToBeAdded: {
+ title: string;
+ coldCredential: {
+ hash: string;
+ epoch: string;
+ };
+ };
+ membersToBeRemoved: {
+ title: string;
+ hash: string;
+ };
+ newQuorumThreshold?: {
+ title: string;
+ denominator: string;
+ numerator: string;
+ };
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts
new file mode 100644
index 0000000000..b2b57cd254
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/UpdateCommitteeAction/index.ts
@@ -0,0 +1 @@
+export { UpdateCommitteeAction } from './UpdateCommitteeActionAction';
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx b/packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx
new file mode 100644
index 0000000000..27112d1a52
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/ActionId.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { Cell, sx, Metadata, Text } from '@lace/ui';
+import * as Types from './ActionIdTypes';
+
+interface Props {
+ data: Types.Data;
+ translations: Types.Translations;
+}
+
+export const ActionId = ({ data, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+
+
+ |
+
+
+ |
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts b/packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts
new file mode 100644
index 0000000000..3dbb0d8824
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/ActionIdTypes.ts
@@ -0,0 +1,10 @@
+export interface Data {
+ index: string;
+ id: string;
+}
+
+export interface Translations {
+ title?: string;
+ index: string;
+ txId: string;
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss b/packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss
new file mode 100644
index 0000000000..bf8fa31cab
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/Card.module.scss
@@ -0,0 +1,3 @@
+.text {
+ word-break: break-all;
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx b/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx
new file mode 100644
index 0000000000..44bed28c23
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/Card.tsx
@@ -0,0 +1,94 @@
+import React from 'react';
+import { Grid, Flex, Card as UICard, Box, sx, Text, TextLink, Tooltip, Cell } from '@lace/ui';
+import styles from './Card.module.scss';
+
+interface Item {
+ label: string;
+ value: string;
+ tooltip?: string;
+ url?: string;
+}
+
+interface Props {
+ title?: string;
+ tooltip?: string;
+ data: Item[];
+}
+
+export const Card = ({ title, tooltip, data }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ const renderRow = (props: Item) => (
+ <>
+
+
+ {props.tooltip ? (
+
+
+ {props.label}
+
+
+ ) : (
+
+ {props.label}
+
+ )}
+
+ |
+
+
+ {props.url ? (
+
+
+
+ ) : (
+
+ {props.value}
+
+ )}
+
+ |
+ >
+ );
+
+ const renderTitle = () => {
+ if (!title) return <>>;
+
+ if (tooltip) {
+ return (
+
+
+
+ {title}
+
+
+
+ );
+ }
+
+ return (
+
+
+ {title}
+
+
+ );
+ };
+
+ return (
+
+
+
+
+ {renderTitle()}
+
+ {data.map((props) => renderRow(props))}
+
+
+
+
+ |
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx b/packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx
new file mode 100644
index 0000000000..682fcc21ff
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/Procedure.tsx
@@ -0,0 +1,32 @@
+import React from 'react';
+import { Metadata, MetadataLink, Text, sx, Cell } from '@lace/ui';
+import * as Types from './ProcedureTypes';
+
+interface Props {
+ data: Types.Procedure;
+ translations: Types.Translations;
+}
+
+export const Procedure = ({ data, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+ <>
+
+
+ |
+
+
+ |
+ >
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts b/packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts
new file mode 100644
index 0000000000..a1ec2c00a7
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/ProcedureTypes.ts
@@ -0,0 +1,15 @@
+export interface Procedure {
+ anchor: {
+ url: string;
+ hash: string;
+ txHashUrl: string;
+ };
+}
+
+export interface Translations {
+ title: string;
+ anchor: {
+ url: string;
+ hash: string;
+ };
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx
new file mode 100644
index 0000000000..8a21c6b005
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetails.tsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import { Metadata, Text, sx, Cell } from '@lace/ui';
+import * as Types from './TransactionDetailsTypes';
+
+interface Props {
+ data: Types.TxDetails;
+ translations: Types.Translations;
+}
+
+export const TransactionDetails = ({ data, translations }: Props): JSX.Element => {
+ const textCss = sx({
+ color: '$text_primary'
+ });
+
+ return (
+ <>
+
+
+ {translations.title}
+
+ |
+
+
+ |
+ {data.rewardAccount && (
+
+
+ |
+ )}
+ {data.deposit && (
+
+
+ |
+ )}
+ >
+ );
+};
diff --git a/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts
new file mode 100644
index 0000000000..744b832002
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/components/TransactionDetailsTypes.ts
@@ -0,0 +1,12 @@
+export interface TxDetails {
+ txType: string;
+ deposit?: string;
+ rewardAccount?: string;
+}
+
+export interface Translations {
+ title: string;
+ txType: string;
+ deposit?: string;
+ rewardAccount?: string;
+}
diff --git a/packages/core/src/ui/components/ProposalProcedures/index.ts b/packages/core/src/ui/components/ProposalProcedures/index.ts
new file mode 100644
index 0000000000..d8d0bc4008
--- /dev/null
+++ b/packages/core/src/ui/components/ProposalProcedures/index.ts
@@ -0,0 +1,7 @@
+export * from './HardForkInitiationAction';
+export * from './InfoAction';
+export * from './NewConstitutionAction';
+export * from './NoConfidenceAction';
+export * from './ParameterChangeAction';
+export * from './TreasuryWithdrawalsAction';
+export * from './UpdateCommitteeAction';
diff --git a/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts b/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts
new file mode 100644
index 0000000000..c48a22fb36
--- /dev/null
+++ b/packages/staking/src/features/DelegationCard/DelegationTooltip.css.ts
@@ -0,0 +1,11 @@
+import { style } from '@vanilla-extract/css';
+import { theme } from '../theme';
+
+export const tooltip = style({
+ background: theme.colors.$tooltipBgColor,
+ borderRadius: theme.radius.$small,
+ boxShadow: theme.elevation.$tooltip,
+ margin: theme.spacing.$10,
+ maxWidth: theme.spacing.$214,
+ padding: theme.spacing.$16,
+});
diff --git a/packages/ui/src/design-system/metadata/metadata-link.component.tsx b/packages/ui/src/design-system/metadata/metadata-link.component.tsx
index 5eb52078cf..3d766be856 100644
--- a/packages/ui/src/design-system/metadata/metadata-link.component.tsx
+++ b/packages/ui/src/design-system/metadata/metadata-link.component.tsx
@@ -3,6 +3,7 @@ import React from 'react';
import { Flex } from '../flex';
import { Grid, Cell } from '../grid';
import { TextLink } from '../text-link';
+import { Tooltip } from '../tooltip';
import * as Typography from '../typography';
import * as cx from './metadata.css';
@@ -13,20 +14,32 @@ type Props = OmitClassName<'div'> & {
label: string;
text: string;
url: string;
+ tooltip?: string;
};
export const MetadataLink = ({
label,
text,
url,
+ tooltip,
...props
}: Readonly): JSX.Element => {
return (
-
- {label}
-
+ {tooltip == undefined ? (
+
+ {label}
+
+ ) : (
+
+
+
+ {label}
+
+
+
+ )}
|
diff --git a/packages/ui/src/design-system/metadata/metadata.component.tsx b/packages/ui/src/design-system/metadata/metadata.component.tsx
index f9d9fd0aa8..6f15a58c6b 100644
--- a/packages/ui/src/design-system/metadata/metadata.component.tsx
+++ b/packages/ui/src/design-system/metadata/metadata.component.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { Flex } from '../flex';
import { Grid, Cell } from '../grid';
+import { Tooltip } from '../tooltip';
import * as Typography from '../typography';
import * as cx from './metadata.css';
@@ -11,19 +12,31 @@ import type { OmitClassName } from '../../types';
type Props = OmitClassName<'div'> & {
label: string;
text: string;
+ tooltip?: string;
};
export const Metadata = ({
label,
text,
+ tooltip,
...props
}: Readonly): JSX.Element => {
return (
|
-
- {label}
-
+ {tooltip == undefined ? (
+
+ {label}
+
+ ) : (
+
+
+
+ {label}
+
+
+
+ )}
|
diff --git a/packages/ui/src/design-system/metadata/metadata.stories.tsx b/packages/ui/src/design-system/metadata/metadata.stories.tsx
index a504ca41a2..d91634f4f3 100644
--- a/packages/ui/src/design-system/metadata/metadata.stories.tsx
+++ b/packages/ui/src/design-system/metadata/metadata.stories.tsx
@@ -42,6 +42,7 @@ const MainComponents = (): JSX.Element => (
|
|
diff --git a/packages/ui/src/design-system/tooltip/tooltip-content.css.ts b/packages/ui/src/design-system/tooltip/tooltip-content.css.ts
index 29d7cd6999..803e3c84e5 100644
--- a/packages/ui/src/design-system/tooltip/tooltip-content.css.ts
+++ b/packages/ui/src/design-system/tooltip/tooltip-content.css.ts
@@ -15,5 +15,6 @@ export const tooltipContent = style([
}),
{
position: 'relative',
+ wordBreak: 'break-word',
},
]);
| |