Skip to content

Commit

Permalink
Merge pull request #1230 from IntersectMBO/feat/1213-create-governanc…
Browse files Browse the repository at this point in the history
…e-actions-shared-state-to-be-passed-to-pillars

feat: create governance actions shared state
  • Loading branch information
MSzalowski authored Jun 11, 2024
2 parents 87e1d6c + b46ff01 commit b57fc34
Show file tree
Hide file tree
Showing 7 changed files with 5,523 additions and 779 deletions.
95 changes: 95 additions & 0 deletions docs/GOVERNANCE_ACTION_SUBMISSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# How to submit a Governance Action using GovTool

## Prerequisites

- Follow the steps of setting up the [GovTool Frontend](../govtool/frontend/README.md).
- Provide any backend that provides the Epoch params (for the wallet connection), can be the current [GovTool Backend](../govtool/backend/README.md).
- Have a wallet with the 50k of ADA to pay for the transaction and fee.

## Development guide

### Prerequisites

For creating the Governance Action, you need to consume 2 utility methods provided by `GovernanceActionProvided` (documented later within this document), and 3 exported from `CardanoProvider` wallet actions (2 for the 2 types of supported by GovTool Governance Actions and 1 for Signing and Submitting the transaction)

### Step 1: Create the Governance Action metadata object

Create the Governance Action object with the fields specified by [CIP-108](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0108), which are:

- title
- abstract
- motivation
- rationale
- references

(For the detailed documentation of the fields and their types, please refer to the [CIP-108](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0108))

### Step 2: Create the Governance Action JSON-LD

Using the `GovernanceActionProvider` provider, use the `createGovernanceActionJsonLd` method to create the JSON-LD object for the Governance Action.

Example:

```typescript
// When used within a GovernanceActionProvider
const { createGovernanceActionJsonLD } = useCreateGovernanceAction();

const jsonLd = createGovernanceActionJsonLD(governanceAction);
```

Type of the `jsonLd` object is `NodeObject` provided from the `jsonld` package by digitalbazaar ([ref](https://github.com/digitalbazaar/jsonld.js)).

### Step 3: Create the Governance Action Hash of the JSON-LD

Using the `GovernanceActionProvider` provider, use the `createGovernanceActionHash` method to create the hash of the JSON-LD object.

Example:

```typescript
// When used within a GovernanceActionProvider
const { createHash } = useCreateGovernanceAction();

const hash = createHash(jsonLd);
```

Type of the `hash` object is `string` (blake2b-256).

### Step 4: Validate the Governance Action hash and metadata

Validate the Governance Action hash and metadata using any backend that provides the Governance Action validation of the metadata against the provided hash.

### Step 5: Sign and Submit the Governance Action

Using the `CardanoProvider` provider, use the `buildSignSubmitConwayCertTx` method to sign and submit the Governance Action, and either the `buildNewInfoGovernanceAction` or `buildTreasuryGovernanceAction` method to build the transaction based on the Governance action type.

Example:

```typescript
// When used within a CardanoProvider
const { buildSignSubmitConwayCertTx, buildNewInfoGovernanceAction } =
useCardano();

// hash of the generated Governance Action metadata, url of the metadata
const govActionBuilder = await buildNewInfoGovernanceAction({ hash, url });

// sign and submit the transaction
await buildSignSubmitConwayCertTx(govActionBuilder);

// or if you want to use the Treasury Governance Action
const { buildTreasuryGovernanceAction } = useCardano();

// hash of the generated Governance Action metadata, url of the metadata, amount of the transaction, receiving address is the stake key address
const govActionBuilder = await buildTreasuryGovernanceAction({
hash,
url,
amount,
receivingAddress,
});

// sign and submit the transaction
await buildSignSubmitConwayCertTx(govActionBuilder);
```

### Step 6: Verify the Governance Action

`buildSignSubmitConwayCertTx` logs the transaction CBOR making it able to be tracked on the transactions tools such as cexplorer.
11 changes: 6 additions & 5 deletions govtool/frontend/src/components/organisms/PDFWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Box } from "@mui/material";
import PDF from "@intersect.mbo/pdf-ui/cjs";
import "@intersect.mbo/pdf-ui/style";
import { useCardano } from "@/context";
import { TopNav } from "./TopNav";
import { useCardano, useGovernanceActions } from "@/context";

export const PDFWrapper = () => {
const walletAPI = useCardano();
const isWalletConnected = walletAPI.isEnabled;
const { createGovernanceActionJsonLD, createHash } = useGovernanceActions();

return (
<Box
Expand All @@ -15,8 +14,10 @@ export const PDFWrapper = () => {
py: 3,
}}
>
{!isWalletConnected && <TopNav />}
<PDF walletAPI={{ ...walletAPI }} pathname={window.location.pathname} />
<PDF
walletAPI={{ ...walletAPI, createGovernanceActionJsonLD, createHash }}
pathname={window.location.pathname}
/>
</Box>
);
};
21 changes: 12 additions & 9 deletions govtool/frontend/src/context/contextProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@ import { ModalProvider, useModal } from "./modal";
import { SnackbarProvider, useSnackbar } from "./snackbar";
import { DataActionsBarProvider } from "./dataActionsBar";
import { FeatureFlagProvider } from "./featureFlag";
import { GovernanceActionProvider } from "./governanceAction";

interface Props {
children: React.ReactNode;
}

const ContextProviders = ({ children }: Props) => (
<FeatureFlagProvider>
<ModalProvider>
<SnackbarProvider>
<DataActionsBarProvider>
<CardanoProvider>{children}</CardanoProvider>
</DataActionsBarProvider>
</SnackbarProvider>
</ModalProvider>
</FeatureFlagProvider>
<GovernanceActionProvider>
<FeatureFlagProvider>
<ModalProvider>
<SnackbarProvider>
<DataActionsBarProvider>
<CardanoProvider>{children}</CardanoProvider>
</DataActionsBarProvider>
</SnackbarProvider>
</ModalProvider>
</FeatureFlagProvider>
</GovernanceActionProvider>
);

export { ContextProviders, useCardano, useModal, useSnackbar };
101 changes: 101 additions & 0 deletions govtool/frontend/src/context/governanceAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
PropsWithChildren,
useMemo,
createContext,
useContext,
useCallback,
} from "react";
import { NodeObject } from "jsonld";
import { blake2bHex } from "blakejs";

import { CIP_108, GOVERNANCE_ACTION_CONTEXT } from "@/consts";
import { canonizeJSON, generateJsonld, generateMetadataBody } from "@/utils";

type GovActionMetadata = {
title: string;
abstract: string;
motivation: string;
rationale: string;
references: { uri: string; label: string }[];
};

type GovernanceActionContextType = {
createGovernanceActionJsonLD: (
govActionMetadata: GovActionMetadata,
) => Promise<NodeObject | undefined>;
createHash: (jsonLD: NodeObject) => Promise<string | undefined>;
} | null;

const GovernanceActionContext =
createContext<GovernanceActionContextType>(null);

/**
* Provides governance action context to its children components.
*
* @param children - The child components to render.
*/
const GovernanceActionProvider = ({ children }: PropsWithChildren) => {
const createGovernanceActionJsonLD = useCallback(
async (govActionMetadata: GovActionMetadata) => {
try {
const metadataBody = generateMetadataBody({
data: govActionMetadata,
acceptedKeys: ["title", "abstract", "motivation", "rationale"],
standardReference: CIP_108,
});

const jsonLD = await generateJsonld(
metadataBody,
GOVERNANCE_ACTION_CONTEXT,
);
return jsonLD;
} catch (error) {
console.error(error);
}
},
[],
);

const createHash = useCallback(async (jsonLD: NodeObject) => {
try {
const canonizedJson = await canonizeJSON(jsonLD);
const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32);
return canonizedJsonHash;
} catch (error) {
console.error(error);
}
}, []);

const value = useMemo(
() => ({
createGovernanceActionJsonLD,
createHash,
}),
[createGovernanceActionJsonLD, createHash],
);

return (
<GovernanceActionContext.Provider value={value}>
{children}
</GovernanceActionContext.Provider>
);
};

/**
* Custom hook that provides access to the governance actions context.
* Throws an error if used outside of a GovernanceActionsProvider.
* @returns The governance actions context.
*/
const useGovernanceActions = () => {
const context = useContext(GovernanceActionContext);

if (!context) {
throw new Error(
"useGovernanceActions must be used within a GovernanceActionsProvider",
);
}

return context;
};

export { GovernanceActionProvider, useGovernanceActions };
1 change: 1 addition & 0 deletions govtool/frontend/src/context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from "./snackbar";
export * from "./usersnapContext";
export * from "./wallet";
export * from "./featureFlag";
export * from "./governanceAction";
5 changes: 3 additions & 2 deletions govtool/frontend/src/utils/generateMetadataBody.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ export const generateMetadataBody = ({
.map(([key, value]) => [standardReference + key, value]);

const references = data?.links
? (data.links as Array<{ link: string }>)
? // uri should not be optional. It is just not yet supported on govtool
(data.links as Array<{ uri?: string; link: string }>)
.filter((link) => link.link)
.map((link) => ({
"@type": "Other",
[`${CIP_100}reference-label`]: "Label",
[`${CIP_100}reference-label`]: link.uri || "Label",
[`${CIP_100}reference-uri`]: link.link,
}))
: undefined;
Expand Down
Loading

0 comments on commit b57fc34

Please sign in to comment.