diff --git a/docs/tutorials/overview.md b/docs/tutorials/overview.md
index 522a9dd95f0..bcd15782bd3 100644
--- a/docs/tutorials/overview.md
+++ b/docs/tutorials/overview.md
@@ -13,6 +13,7 @@ sidebar_position: 1
* [Explorer & smart contracts](./explorer-sc)
* [Manage rules with Prolog](./prolog-1)
* [Leverage the ontology](./ontology-1)
+* [Build a Web App](./webapp-1)
## Nono's tutorials
diff --git a/docs/tutorials/webapp-1.md b/docs/tutorials/webapp-1.md
new file mode 100644
index 00000000000..9c7e2ba2e06
--- /dev/null
+++ b/docs/tutorials/webapp-1.md
@@ -0,0 +1,1863 @@
+---
+sidebar_position: 7
+---
+
+# Build a Web App
+
+
+
+This tutorial guides you on building a web application that interacts seamlessly with the OKP4 protocol, a Cosmos-based blockchain. Whether you're a seasoned developer or just starting out, this tutorial will walk you through every step of the process using the [Vite](https://vitejs.dev/) framework and [graz](https://graz.sh/) hooks. By the end, you'll have the pieces of code you need to develop a front end ready to harness the power of the OKP4 blockchain **🚀**.
+
+:::note
+
+This is a step-by-step tutorial, but you can also directly check out [the Web UI boilerplates repository](https://github.com/okp4/web-ui-boilerplates).
+
+:::
+
+**Prerequisites:**
+
+- Basic understanding of blockchain and a [browser wallet set up with some $KNOW tokens](https://docs.okp4.network/tutorials/keplr-1)
+- Familiarity with JavaScript and modern web development, [Node.js](https://nodejs.org/) installed on your machine. This guide uses [Node.JS v18.18 (LTS)](https://nodejs.org/fr/blog/release/v18.18.0). You'll see commands with `npm`, but you’re encouraged to use `yarn` or `pnpm` as well.
+
+## Importance of user interfaces interacting with the OKP4 protocol
+
+The OKP4 protocol is a blockchain built on the Cosmos SDK that enables providers to share resources like web services or datasets with anyone without the need to trust a third party. A provider describes resources ([RDF triples](https://docs.okp4.network/tutorials/ontology-1)) and rules ([Prolog program to define access conditions, revenue sharing, and more](https://docs.okp4.network/tutorials/prolog-1)), and consumers send requests to the blockchain which guarantees the respect of sharing conditions.
+
+In essence, user interfaces are the key to democratizing the power of the OKP4 blockchain, making it usable and beneficial for everyone. For most users, [interacting with the protocol via terminal commands](https://docs.okp4.network/tutorials/cli-1) or delving into the intricacies of defining rules with the Prolog language is a daunting challenge. Similarly, the manipulation and understanding of RDF triples for semantic data can be complex without the right tools.
+
+This is where the significance of user-friendly interfaces comes into play. A well-designed user interface abstracts the complexities of the underlying protocol, offering a more intuitive and ergonomic experience. It bridges the gap between advanced blockchain technology and everyday users, ensuring that the benefits of the OKP4 protocol are accessible to a broader audience. Without these interfaces, the potential of the OKP4 protocol would remain largely untapped, confined to the realm of tech-savvy individuals.
+
+## Setting up the development environment
+
+### Vite project
+
+Let’s initiate a project with the [Vite framework](https://vitejs.dev/), which offers a lightning-fast cold server start and blazingly fast hot updates:
+
+```bash
+npm create vite@4.4.1 # you're free to use the latest version, but to ensure compatibility this guide provides a specific version
+```
+
+Choose the **React** framework and the **TypeScript + SWC** variant.
+
+Once you've initialized your project with Vite, the next step is to install the necessary dependencies :
+
+```bash
+npm i
+```
+
+With the dependencies in place, it's time to fire up our development server. This will allow you to see real-time changes. Start the development server with:
+
+```bash
+npm run dev
+```
+
+Now, with the server running, you can navigate to the provided local URL in your browser and witness the live development environment. The updates will reflect instantly as you change the code, giving you a dynamic and efficient development experience.
+
+### Node.JS polyfills
+
+As we’ll deal with libraries and dependencies that were originally designed for a Node.js environment, browsers don't natively support these modules. To bridge this gap and ensure that the Vite project runs smoothly in the browser, providing "polyfills" for these Node.js modules is necessary.
+
+Thus, install **`node-stdlib-browser`** and the **`vite-plugin-node-stdlib-browser`** plugin:
+
+```bash
+# you're free to use the latest versions, but to ensure compatibility this guide provides specific versions
+npm i -D node-stdlib-browser@1.2.0 vite-plugin-node-stdlib-browser@0.2.1
+```
+
+Then, modify the Vite config file (`vite.config.ts`):
+
+```ts
+import { defineConfig } from "vite";
+import nodePolyfills from "vite-plugin-node-stdlib-browser";
+import react from "@vitejs/plugin-react-swc";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [nodePolyfills(), react()],
+});
+```
+
+### Cosmos SDK utilities and type definitions
+
+Now, install some packages to equip the project with a suite of tools and libraries essential for interacting with the blockchain, handling encoding/decoding, ensuring transactional integrity, and working with specific data structures defined by the OKP4 protocol.
+
+```bash
+# you're free to use the latest versions, but to ensure compatibility this guide provides specific versions
+npm i graz@0.0.50 @cosmjs/encoding@0.31.1 @cosmjs/proto-signing@0.31.1 cosmjs-types@0.8.0 @okp4/cognitarium-schema@3.0.0 @okp4/law-stone-schema@3.0.0
+```
+
+- [**graz**](https://www.npmjs.com/package/graz): a collection of React hooks containing everything you need to start working with the [Cosmos ecosystem](https://cosmos.network/)
+- [**@cosmjs/encoding**](https://www.npmjs.com/package/@cosmjs/encoding): to transform data into a format that can be easily transported or stored.
+- [**@cosmjs/proto-signing**](https://www.npmjs.com/package/@cosmjs/proto-signing): to ensure the data's authenticity and integrity
+- [**cosmjs-types**](https://www.npmjs.com/package/cosmjs-types): TypeScript type definitions related to Protocol Buffers used by Cosmos SDK
+- [**@okp4/cognitarium-schema**](https://www.npmjs.com/package/@okp4/cognitarium-schema) and [**@okp4/law-stone-schema**](https://www.npmjs.com/package/@okp4/law-stone-schema): schema definitions related to the OKP4 protocol. Schemas define the structure of transaction data, ensuring consistency and validity
+
+## Connect the web app to the OKP4 testnet
+
+Integrating a provider within the React UI is essential to establish the connection with a browser wallet like Keplr. Here is how to wrap the app with **`` (**`main.tsx` file)
+
+```tsx
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App.tsx";
+import "./index.css";
+import { GrazProvider } from "graz";
+
+ReactDOM.createRoot(document.getElementById("root")!).render(
+
+
+
+
+
+);
+```
+
+Then, configure `graz` in the `App.tsx` file providing the OKP4 testnet information, and use the `useActiveChain` hook to check the connection to `okp4-nemeton-1`:
+
+```tsx
+import "./App.css";
+import { configureGraz, useActiveChain } from "graz";
+
+configureGraz({
+ defaultChain: {
+ chainId: "okp4-nemeton-1",
+ currencies: [
+ {
+ coinDenom: "know",
+ coinMinimalDenom: "uknow",
+ coinDecimals: 6,
+ coinGeckoId: "OKP4 nemeton",
+ coinImageUrl:
+ "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/okp4testnet/images/okp4.png",
+ },
+ ],
+ rpc: "https://api.testnet.okp4.network:443/rpc",
+ rest: "https://api.testnet.okp4.network:443/",
+ },
+});
+
+function App() {
+ const activeChain = useActiveChain();
+
+ return (
+
+ Connected to {activeChain?.chainId}
+
+ );
+}
+
+export default App;
+```
+
+For clarity, it’s better to provide full information for a configuration with the OKP4 testnet in a `constants.ts` file:
+
+```ts
+export const OKP4TestnetChain = {
+ chainId: "okp4-nemeton-1",
+ currencies: [
+ {
+ coinDenom: "know",
+ coinMinimalDenom: "uknow",
+ coinDecimals: 6,
+ coinImageUrl:
+ "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/okp4testnet/images/okp4.png",
+ },
+ ],
+ path: "okp4testnet",
+ rest: "https://api.testnet.okp4.network:443/",
+ rpc: "https://api.testnet.okp4.network/rpc",
+ bech32Config: {
+ bech32PrefixAccAddr: "okp4",
+ bech32PrefixAccPub: "okp4pub",
+ bech32PrefixValAddr: "okp4valoper",
+ bech32PrefixValPub: "okp4valoperpub",
+ bech32PrefixConsAddr: "okp4valcons",
+ bech32PrefixConsPub: "okp4valconspub",
+ },
+ chainName: "okp4testnet",
+ feeCurrencies: [
+ {
+ coinDenom: "know",
+ coinMinimalDenom: "uknow",
+ coinDecimals: 6,
+ coinImageUrl:
+ "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/okp4testnet/images/okp4.png",
+ },
+ ],
+ stakeCurrency: {
+ coinDenom: "know",
+ coinMinimalDenom: "uknow",
+ coinDecimals: 6,
+ coinImageUrl:
+ "https://raw.githubusercontent.com/cosmos/chain-registry/master/testnets/okp4testnet/images/okp4.png",
+ },
+ bip44: {
+ coinType: 118,
+ },
+};
+```
+
+## Connect your wallet and see your account information
+
+Users must link their OKP4 account to the app to interact with the blockchain. Users can securely manage and authorise transactions directly from the browser by connecting their Keplr wallet (or browser extension alternative like [Leap](https://www.leapwallet.io/)). All state-changing actions on the blockchain are executed using the user's private key stored in Keplr, ensuring security through cryptographic signatures.
+
+### Basic connection
+
+Let’s create a `Connection` component with a `Connection.tsx` file created in a `components` folder:
+
+```tsx
+import {
+ useAccount,
+ useDisconnect,
+ useSuggestChainAndConnect,
+ WalletType,
+} from "graz";
+import { ChainInfo, Key } from "graz/dist/keplr";
+
+export function Connection({ chainInfo }: { chainInfo: ChainInfo }) {
+ const { suggestAndConnect } = useSuggestChainAndConnect();
+ const { disconnect } = useDisconnect();
+ const {
+ data: account,
+ isConnected,
+ }: { data: Key | null; isConnected: boolean } = useAccount();
+
+ const gas = {
+ price: "0.012",
+ denom: "uknow",
+ };
+
+ return (
+
+ {isConnected ? (
+ <>
+
Linked wallet address: {account?.bech32Address}
+
+ >
+ ) : (
+ <>
+
+ >
+ )}
+
+ );
+}
+```
+
+`useSuggestChainAndConnect` hook provides a `suggestAndConnect` function to add the OKP4 chain in the browser wallet networks list and to connect to the user’s OKP4 account. `useAccount` hook is useful to know if the app is connected to an account and also to get the user’s wallet address.
+
+Export the `Connection` component with an `index.ts` file in the `components` folder:
+
+```tsx
+import { Connection } from "./Connection";
+export { Connection };
+```
+
+Now add the `Connection` component in the main `App.tsx` file:
+
+```tsx
+import { configureGraz } from "graz";
+import {
+ Connection
+} from "./components";
+import "./App.css";
+import {
+ OKP4TestnetChain,
+} from "./constants";
+
+configureGraz({
+ defaultChain: OKP4TestnetChain,
+});
+
+function App() {
+ return (
+ <>
+
OKP4 Vite Starter
+
+
+
+ >
+ );
+}
+
+export default App;
+```
+
+
+
+### Connection with Metamask
+
+[Metamask is the most popular crypto wallet](https://www.techopedia.com/cryptocurrency/metamask-wallet-review) but does not support Cosmos-based accounts. Fortunately, [Metamask can be extended with plug-ins](https://snaps.metamask.io/), and the Leap wallet team built one to enable all Cosmos-based chains with Metamask.
+
+`graz` enables the app to be compatible with several wallet extensions, including Metamask. As the `getAvailableWallets` hook returns the list of wallets on the user’s device, add the support of all compatible wallets with the following code in the `Connection.tsx` file:
+
+```tsx
+import {
+ getAvailableWallets,
+ useAccount,
+ useDisconnect,
+ useSuggestChainAndConnect,
+ WalletType,
+} from "graz";
+import { ChainInfo, Key } from "graz/dist/keplr";
+
+export function Connection({ chainInfo }: { chainInfo: ChainInfo }) {
+ const wallets = getAvailableWallets();
+ const { suggestAndConnect } = useSuggestChainAndConnect();
+ const { disconnect } = useDisconnect();
+ const {
+ data: account,
+ isConnected,
+ }: { data: Key | null; isConnected: boolean } = useAccount();
+
+ const gas = {
+ price: "0.012",
+ denom: "uknow",
+ };
+
+ return (
+
+ );
+}
+```
+
+You can now connect an OKP4 account to the app with Metamask:
+
+
+
+## Get user balance
+
+`useBalance` hook returns the number of tokens the user holds in the wallet. Here is an example of a `Balance` component (`Balance.tsx` in the `components` folder):
+
+```tsx
+import { useAccount, useBalance } from "graz";
+import { Coin } from "graz/dist/cosmjs";
+
+export function Balance() {
+ const { isConnected }: { isConnected: boolean } = useAccount();
+
+ const {
+ data: balance,
+ isLoading,
+ }: { data: Coin | undefined; isLoading: boolean } = useBalance("uknow");
+
+ return (
+
+ );
+}
+```
+
+#### Select some RDF triples in a triple store: query a `Select` message to a `cognitarium` instance
+
+Here is `QueryCognitarium`, a specific component to [query a `Select` message to a `cognitarium` instance](https://docs.okp4.network/contracts/okp4-cognitarium#querymsgselect) (`QueryCognitarium.tsx` file in the `components` folder) that uses the generic `Query` component:
+
+```tsx
+import {
+ PurpleTriplePattern,
+ PurpleVarOrNode,
+ PurpleVarOrNodeOrLiteral,
+ QueryPrefix,
+ QueryWhere,
+ Select,
+ SelectClass,
+ SelectItem,
+ SelectQuery,
+ SelectResponse,
+ Value,
+} from "@okp4/cognitarium-schema";
+import { Query } from "./Query";
+
+function onQueryResult(data?: Record) {
+ if (!data) return <>>;
+
+ const selectResult: SelectResponse = data as unknown as SelectResponse;
+
+ return (
+
+ )}
+ >
+ );
+}
+```
+
+This example filters to display the contracts the connected account has instantiated.
+
+## Analyze transactions
+
+### Decode transaction data
+
+This part shows how to decode data sent and validated on the OKP4 protocol. It explains:
+
+- how to decode a transaction message based on its type URL (`typeUrl`)
+- how to decode messages payload from smart contract (CosmWasm) interactions
+- how to retrieve plain Prolog program previously stored
+
+The necessary `decode` function depends on the type URL. Here is a helper function (created in the `utils.ts` file) to decode transactions from any protobuf message:
+
+```tsx
+import { Any } from "cosmjs-types/google/protobuf/any";
+import {
+ MsgExecuteContract,
+ MsgInstantiateContract,
+} from "cosmjs-types/cosmwasm/wasm/v1/tx";
+import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
+import { MsgVote } from "cosmjs-types/cosmos/gov/v1beta1/tx";
+
+/**
+ * Decodes a transaction message based on its type URL.
+ *
+ * @param message - The transaction message, which includes a type URL and a value.
+ * @returns An object containing the type URL and the decoded value of the message.
+ */
+export function decodeTxMessage(message: Any) {
+ const { typeUrl, value } = message;
+
+ // For contract instantiation messages from the CosmWasm module
+ if (typeUrl === "/cosmwasm.wasm.v1.MsgInstantiateContract")
+ return {
+ typeUrl,
+ value: MsgInstantiateContract.decode(value),
+ };
+ // For contract execution messages from the CosmWasm module
+ else if (typeUrl === "/cosmwasm.wasm.v1.MsgExecuteContract")
+ return {
+ typeUrl,
+ value: MsgExecuteContract.decode(value),
+ };
+ // For sending tokens using the Cosmos bank module
+ else if (typeUrl === "/cosmos.bank.v1beta1.MsgSend")
+ return {
+ typeUrl,
+ value: MsgSend.decode(value),
+ };
+ // For voting on proposals using the Cosmos governance module
+ else if (typeUrl === "/cosmos.gov.v1beta1.MsgVote")
+ return {
+ typeUrl,
+ value: MsgVote.decode(value),
+ };
+ else {
+ console.warn("unhandled typeUrl ", typeUrl);
+ const decoder = new TextDecoder();
+ return {
+ typeUrl,
+ value: decoder.decode(value), // Use a generic text decoder for the value
+ };
+ }
+}
+```
+
+This `decodeTxWasmMessageValue` helper decodes wasm messages (`msg` payload from CosmWasm smart contract instantiations and executions):
+
+```tsx
+import { fromBase64, fromUtf8 } from "@cosmjs/encoding";
+
+/**
+ * Further decodes the `msg` field of a given CosmWasm transaction message value
+ * that's of type `MsgInstantiateContract` or `MsgExecuteContract`.
+ *
+ * @param decodedValue - The preliminarily decoded message value.
+ * @returns An object containing the original decoded value and the further decoded `msg` field.
+ */
+export function decodeTxWasmMessageValue(
+ decodedValue: MsgInstantiateContract | MsgExecuteContract
+) {
+ const msg = JSON.parse(fromUtf8(decodedValue.msg));
+ return {
+ ...decodedValue,
+ msg,
+ };
+}
+```
+
+Thus, update the `decodeTxMessage` helper to return a fully decoded response (for `typeUrl` related to CosmWasm messages only !):
+
+```tsx
+export function decodeTxMessage(message: Any) {
+ const { typeUrl, value } = message;
+
+ // For contract instantiation messages from the CosmWasm module
+ if (typeUrl === "/cosmwasm.wasm.v1.MsgInstantiateContract")
+ return {
+ typeUrl,
+ value: decodeTxWasmMessageValue(
+ MsgInstantiateContract.decode(value)
+ ),
+ };
+ // For contract execution messages from the CosmWasm module
+ else if (typeUrl === "/cosmwasm.wasm.v1.MsgExecuteContract")
+ return {
+ typeUrl,
+ value: decodeTxWasmMessageValue(MsgExecuteContract.decode(value)),
+ };
+ ...
+}
+```
+
+Do you remember you provided data in the `msg` payload in base64 encoded? So it would be best if you had a function to decode back. This helper is mainly useful to get Prolog program (as RDF triples may be requested with [Query messages](https://docs.okp4.network/contracts/okp4-cognitarium#querymsg)).
+
+```tsx
+/**
+ * Decodes a Prolog program that is provided as a Base64-encoded UTF-8 string.
+ *
+ * @param program - A Base64-encoded UTF-8 string representing a Prolog program.
+ * @returns The decoded Prolog program as a string.
+ */
+export function decodePrologProgram(program: string): string {
+ // 1: ensures that the program string is a valid Base64 encoded string
+ // by appending the necessary "=" characters to make its length a multiple of 4
+ const missingChars = program.length % 4;
+ if (missingChars > 0) {
+ for (let index = 0; index < missingChars; index++) {
+ program += "=";
+ }
+ }
+
+ // 2: decode the program string from Base64 format
+ const utf8Program = fromBase64(program);
+
+ // 3: decode from UTF-8 encoded byte array into a string
+ return fromUtf8(utf8Program);
+}
+```
+
+Thus, update the `decodeTxWasmMessageValue` helper to populate the returned object with the decoded prolog program :
+
+```tsx
+/**
+ * Further decodes the `msg` field of a given CosmWasm transaction message value
+ * that's of type `MsgInstantiateContract` or `MsgExecuteContract`.
+ *
+ * @param decodedValue - The preliminarily decoded message value.
+ * @returns An object containing the original decoded value and the further decoded `msg` field.
+ */
+export function decodeTxWasmMessageValue(
+ decodedValue: MsgInstantiateContract | MsgExecuteContract
+) {
+ // 1: convert the 'msg' field from a UTF-8 encoded byte array into a JSON object
+ const msg = JSON.parse(fromUtf8(decodedValue.msg));
+
+ // 2: if the parsed 'msg' object has a 'program' field, decode the Prolog program
+ if (msg?.program) msg.programDecoded = decodePrologProgram(msg.program);
+
+ // 3: return the original 'decodedValue' object and the further decoded 'msg' object.
+ return {
+ ...decodedValue,
+ msg,
+ };
+}
+```
+
+### List and filter transactions
+
+You’ll find a complete `Transactions` component below. But let’s understand how we can write such code.
+
+You can use any available client (among [CosmWasmClient](https://cosmwasm.github.io/CosmWasmJS/clients/reading/CosmWasmClient.html), [StargateClient](https://www.npmjs.com/package/@cosmjs/stargate) or [TendermintClient](https://www.npmjs.com/package/@cosmjs/tendermint-rpc)) to get OKP4 network transactions. Here is how to search transactions with a query filtering by message sender with the `txSearch` method from a `Tendermint37Client` client provided by the `useTendermintClient` hook:
+
+```tsx
+import { useAccount, useTendermintClient } from "graz";
+import {
+ Tendermint37Client,
+ TxData,
+ TxResponse,
+ TxSearchParams,
+ TxSearchResponse,
+} from "graz/dist/tendermint";
+
+const { data: tendermintClient }: { data: Tendermint37Client | undefined } =
+ useTendermintClient("tm37");
+
+const query: string = "message.sender='okp41cu9wzlcyyxpek20jaqfwzu3llzjgx34cwnv2v5'";
+// you can also filter by message action, i.e. "message.action='/cosmwasm.wasm.v1.MsgInstantiateContract'";
+// and/or use operators like: "message.sender='okp41cu9wzlcyyxpek20jaqfwzu3llzjgx34cwnv2v5' AND instantiate.code_id=5",
+
+const searchParams: TxSearchParams = {
+ query,
+ page: 1,
+ per_page: 100,
+ order_by: "desc",
+};
+
+const txSearchRes: TxSearchResponse = await tendermintClient?.txSearch(
+ searchParams
+);
+```
+
+Note that if you want to get all transactions (`TxResponse[]` type), you need to process by batches of 100 elements:
+
+```tsx
+const transactionsFromRequest: TxResponse[] = [];
+let page: number = 1;
+let stop: boolean = false;
+
+do {
+ try {
+ // ...
+ const { txs }: { txs: readonly TxResponse[] }= txSearchRes;
+ transactionsFromRequest.push(...txs);
+ page++;
+ } catch (e) {
+ stop = true;
+ }
+} while (!stop);
+```
+
+To fully decode a transaction, first, use the `decodeTxRaw` helper from `@cosmjs/proto-signing` and then populate with decoded messages thanks to the `decodeTxMessage` we previously added in `utils.ts`:
+
+```tsx
+import { DecodedTxRaw, decodeTxRaw } from "@cosmjs/proto-signing";
+import { decodeTxMessage } from "../utils";
+
+type DecodedTransaction = {
+ decodedTx: DecodedTxRaw;
+ decodedMsgs: Array;
+ hash: string;
+ result: TxData;
+};
+
+const {
+ tx,
+ hash,
+ height,
+ result,
+}: {
+ tx: Uint8Array;
+ hash: Uint8Array;
+ height: number;
+ result: TxData;
+} = txResponse;
+
+const decodedTx: DecodedTxRaw = decodeTxRaw(tx);
+
+const transaction: DecodedTransaction = {
+ decodedTx,
+ decodedMsgs: decodedTx.body.messages.map(decodeTxMessage),
+ hash,
+ result,
+};
+```
+
+You may have to format the hash in uppercase if you want to link the web app user to [a Ping Pub explorer](https://testnet.ping.pub/OKP4%20testnet):
+
+```tsx
+import { toHex } from "@cosmjs/encoding";
+
+const transaction: DecodedTransaction = {
+ // ...
+ hash: toHex(hash).toUpperCase(),
+};
+```
+
+You may also need to know when a transaction has been validated. You can have this information with a `block` method from a `Tendermint37Client` client:
+
+```tsx
+import {
+ Block,
+ ReadonlyDateWithNanoseconds,
+} from "graz/dist/tendermint";
+
+const { block }: { block: Block } = await tendermintClient.block(height);
+const {
+ header: { time },
+}: {
+ header: {
+ time: ReadonlyDateWithNanoseconds;
+ };
+} = block;
+```
+
+To sum up, here is a `Transactions` component (`Transactions.tsx` file in the `components` folder) combining all you need to search transactions from a filtering query and get decoded transactions with validation time:
+
+```tsx
+import { useEffect, useState } from "react";
+import { useAccount, useTendermintClient } from "graz";
+import { Key } from "graz/dist/keplr";
+import {
+ Block,
+ ReadonlyDateWithNanoseconds,
+ Tendermint37Client,
+ TxData,
+ TxResponse,
+ TxSearchParams,
+ TxSearchResponse,
+} from "graz/dist/tendermint";
+import { toHex } from "@cosmjs/encoding";
+import { DecodedTxRaw, decodeTxRaw } from "@cosmjs/proto-signing";
+import { decodeTxMessage } from "../utils";
+
+type DecodedTransaction = {
+ decodedTx: DecodedTxRaw;
+ decodedMsgs: Array;
+ hash: string;
+ result: TxData;
+ time: ReadonlyDateWithNanoseconds;
+};
+
+export function Transactions() {
+ const { data: account }: { data: Key | null } = useAccount();
+
+ const { data: tendermintClient }: { data: Tendermint37Client | undefined } =
+ useTendermintClient("tm37");
+
+ const [transactions, setTransactions] = useState>(
+ []
+ );
+
+ const query: string = account?.bech32Address
+ ? `message.sender='${account.bech32Address}'`
+ : "";
+ // you can also filter by message action, i.e. "message.action='/cosmwasm.wasm.v1.MsgInstantiateContract'";
+ // and/or use operators like: "message.sender='okp41cu9wzlcyyxpek20jaqfwzu3llzjgx34cwnv2v5' AND instantiate.code_id=5",
+
+ useEffect(() => {
+ async function fetchTxs() {
+ if (tendermintClient && query.length) {
+ // 1: get all transactions, per batch of 100 elements
+ const transactionsFromRequest: TxResponse[] = [];
+ let page: number = 1;
+ let stop: boolean = false;
+ do {
+ try {
+ const searchParams: TxSearchParams = {
+ query,
+ page,
+ per_page: 100,
+ order_by: "desc",
+ };
+ const txSearchRes: TxSearchResponse =
+ await tendermintClient?.txSearch(searchParams);
+ const { txs }: { txs: readonly TxResponse[] } =
+ txSearchRes;
+ transactionsFromRequest.push(...txs);
+ page++;
+ } catch (e) {
+ stop = true;
+ }
+ } while (!stop);
+
+ // 2: decode and populate transaction infos
+ const decodedTransactions: DecodedTransaction[] =
+ await Promise.all(
+ transactionsFromRequest.map(
+ async (txResponse: TxResponse) => {
+ const {
+ tx,
+ hash,
+ height,
+ result,
+ }: {
+ tx: Uint8Array;
+ hash: Uint8Array;
+ height: number;
+ result: TxData;
+ } = txResponse;
+
+ // Get time from block height
+ const { block }: { block: Block } =
+ await tendermintClient.block(height);
+ const {
+ header: { time },
+ }: {
+ header: {
+ time: ReadonlyDateWithNanoseconds;
+ };
+ } = block;
+
+ const decodedTx: DecodedTxRaw = decodeTxRaw(tx);
+
+ return {
+ decodedTx,
+ decodedMsgs:
+ decodedTx.body.messages.map(
+ decodeTxMessage
+ ),
+ hash: toHex(hash).toUpperCase(),
+ result,
+ time,
+ };
+ }
+ )
+ );
+ console.log({ query, decodedTransactions });
+
+ setTransactions(decodedTransactions);
+ }
+ }
+ fetchTxs();
+ }, [tendermintClient, query]);
+
+ return (
+