diff --git a/.env.example b/.env.example index 2d68326..33d6ed1 100644 --- a/.env.example +++ b/.env.example @@ -32,7 +32,7 @@ NEXT_PUBLIC_MULTIVERSX_CHAIN = devnet NEXT_PUBLIC_DAPP_HOST = http://localhost:3000 # The Elven Tools NFT minter smart contract -NEXT_PUBLIC_NFT_SMART_CONTRACT = erd1qqqqqqqqqqqqqpgq5za2pty2tlfqhj20z9qmrrpjmyt6advcgtkscm7xep +NEXT_PUBLIC_NFT_SMART_CONTRACT = erd1qqqqqqqqqqqqqpgqztp5vpqrxe2tha224jwsa3sv2800a88zgtksar2kc8 # The name for the mint endpoint (change it if you have change it in the smart contract) NEXT_PUBLIC_MINT_FUNCTION_NAME = 'mint' diff --git a/CHANGELOG.md b/CHANGELOG.md index 92760a6..400e143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ### [4.2.0](https://github.com/ElvenTools/elven-tools-dapp/releases/tag/v4.2.0) (2023-01-28) - rebuilt in-app navigation using Next Link instead ActionButton that should be used only for triggering functions, and not navigation -- `useScQuery` can now query more complex data using abi file +- `useScQuery` can now query more complex data using abi file and result parser from MultiversX sdk-core - better errors handling through the network provider - update npm dependencies diff --git a/README.md b/README.md index c16c75a..a5e6a4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ### Elven Tools Dapp - Docs: [elven.tools/docs/minter-dapp-introduction.html](https://www.elven.tools/docs/minter-dapp-introduction.html) +- Dapp's React hooks and components [elven.tools/docs/dapp-react-hooks-and-components.html](https://www.elven.tools/docs/dapp-react-hooks-and-components.html) - Demo: [dapp-demo.elven.tools](https://dapp-demo.elven.tools) - Elven Tools intro (including the Dapp): [youtu.be/Jou5jn8PFz8](https://youtu.be/Jou5jn8PFz8) @@ -32,7 +33,7 @@ const { login, isLoggedIn, error, walletConnectUri, getHWAccounts } = useLogin() login(LoginMethodsEnum.ledger) ``` -Custom mint transactions for the Elven Tools Smart Contract. There is also a more generic `useScTransaction`` hook. +Custom mint transactions for the Elven Tools Smart Contract. There is also a more generic `useScTransaction` hook. ```jsx const { mint, pending, transaction, error } = useMintTransaction(); @@ -42,7 +43,7 @@ const { mint, pending, transaction, error } = useMintTransaction(); mint(amount) ``` -Query the Elven Tools Smart Contract. There is also a more generic `useScQuery`` hook. +Query the Elven Tools Smart Contract. There is also a more generic `useScQuery` hook. ```jsx const { @@ -60,6 +61,38 @@ const { fetch() ``` +You can also query more complex data types. Then you will need to provide the ABI JSON file. + +```jsx +import { TypedOutcomeBundle } from '@multiversx/sdk-core'; +import abiJSON from '../config/abi.json'; + +const { data } = useScQuery({ + type: SCQueryType.COMPLEX, + payload: { + scAddress: 'erd1qqq...', + funcName: 'yourScFunction', + args: [], // args in hex format, use erdjs for conversion, see above + }, + autoInit: true, + abiJSON, +}); +``` + +The `data` here will be a `TypedOutcomeBundle`. Which is: + +```typescript +interface TypedOutcomeBundle { + returnCode: ReturnCode; + returnMessage: string; + values: TypedValue[]; + firstValue?: TypedValue; + secondValue?: TypedValue; + thirdValue?: TypedValue; + lastValue?: TypedValue; +} +``` + For more docs on how to use it check the link above, and for more examples see: [elven.tools/docs/dapp-react-hooks-and-components.html](https://elven.tools/docs/dapp-react-hooks-and-components.html) ### Tracking the progress @@ -87,9 +120,9 @@ Check detailed docs on it here: [How to start with the Dapp](https://www.elven.t - it works on Nextjs - it uses the newest version of [sdk-core](https://github.com/multiversx/mx-sdk-js-core) without the [sdk-dapp](https://github.com/multiversx/mx-sdk-dapp) library. -it uses backend-side rewrites to hide the API endpoint. The only exposed one is `/api` +- optionally it uses backend-side rewrites to hide the API endpoint, then the only exposed one is `/api` - it uses .env file - there is an example in the repo -- it uses chakra-ui +- it uses [chakra-ui](https://chakra-ui.com/) More docs on it: [Minter Dapp introduction](https://www.elven.tools/docs/minter-dapp-introduction.html) diff --git a/components/ActionButton.tsx b/components/ActionButton.tsx index 57ad5e1..6129f0d 100644 --- a/components/ActionButton.tsx +++ b/components/ActionButton.tsx @@ -1,3 +1,5 @@ +// The component for triggering functions. For in-app navigation use Next Link component + import { Box, BoxProps } from '@chakra-ui/react'; import { FC, PropsWithChildren, useCallback } from 'react'; diff --git a/config/dappUi.ts b/config/dappUi.ts index f57787b..db46b53 100644 --- a/config/dappUi.ts +++ b/config/dappUi.ts @@ -43,7 +43,7 @@ export const roadmap = [ { title: 'Q1 2023', points: [ - 'UI automated tests', + 'MultiversX rebranding and dependecies replacement', 'Nextjs configuration improvements', 'Better UI and more functionality', ], diff --git a/hooks/interaction/elvenScHooks/useElvenScQuery.tsx b/hooks/interaction/elvenScHooks/useElvenScQuery.tsx index f9d394b..45f0cbf 100644 --- a/hooks/interaction/elvenScHooks/useElvenScQuery.tsx +++ b/hooks/interaction/elvenScHooks/useElvenScQuery.tsx @@ -11,13 +11,19 @@ interface ScConfigDataArgs { type: SCQueryType; args?: string[]; autoInit?: boolean; + abiJSON?: { + name: string; + endpoints: unknown[]; + types: unknown; + }; } -export function useElvenScQuery({ +export function useElvenScQuery({ funcName, type, args = [], autoInit = true, + abiJSON, }: ScConfigDataArgs) { const { data, isLoading, fetch } = useScQuery({ type, @@ -27,6 +33,7 @@ export function useElvenScQuery({ args, }, autoInit, + abiJSON, }); return { diff --git a/hooks/interaction/useScQuery.tsx b/hooks/interaction/useScQuery.tsx index 20ec54e..4e36927 100644 --- a/hooks/interaction/useScQuery.tsx +++ b/hooks/interaction/useScQuery.tsx @@ -2,6 +2,14 @@ // There is also useElvenScQuery which is specific to the Elven Tools smart contract and uses this hook under the hood import useSWR, { Fetcher } from 'swr'; +import { + ResultsParser, + SmartContractAbi, + SmartContract, + Address, + AbiRegistry, +} from '@multiversx/sdk-core'; +import { ContractQueryResponse } from '@multiversx/sdk-network-providers'; import useSwrMutation from 'swr/mutation'; import { apiCall } from '../../utils/apiCall'; @@ -9,6 +17,7 @@ export enum SCQueryType { NUMBER = 'number', STRING = 'string', BOOLEAN = 'boolean', + COMPLEX = 'complex', } interface SCQueryData { @@ -16,6 +25,11 @@ interface SCQueryData { payload?: Record; options?: Record; autoInit?: boolean; + abiJSON?: { + name: string; + endpoints: unknown[]; + types: unknown; + }; } interface FetcherArgs { @@ -34,11 +48,12 @@ export const fetcher: Fetcher = async ({ payload, }) => await apiCall.post(url, payload || {}); -export function useScQuery({ +export function useScQuery({ type, payload, options, autoInit = true, + abiJSON, }: SCQueryData) { let url = ''; @@ -52,6 +67,10 @@ export function useScQuery({ case SCQueryType.BOOLEAN: url = '/vm-values/int'; break; + // You need to provide ABI JSON for proper results parsing + case SCQueryType.COMPLEX: + url = '/vm-values/query'; + break; } const { data, error, mutate, isValidating, isLoading } = useSWR( @@ -75,10 +94,42 @@ export function useScQuery({ revalidate: true, }); - const parseData = (data: string | number | undefined) => { + const parseData = (data: string | number | undefined | unknown) => { + if (type === SCQueryType.COMPLEX && !abiJSON) { + throw new Error( + 'Please provide the ABI JSON contents if you want to use the COMPLEX queries in useScQuery! Check README.md for more info.' + ); + } + + if ( + type === SCQueryType.COMPLEX && + abiJSON && + (data as Record)?.returnData && + payload?.scAddress && + payload?.funcName + ) { + const parser = new ResultsParser(); + const abiRegistry = AbiRegistry.create(abiJSON); + const abi = new SmartContractAbi(abiRegistry, [abiJSON.name]); + const contract = new SmartContract({ + address: new Address(payload.scAddress as string), + abi: abi, + }); + const endpointDefinition = contract.getEndpoint( + payload.funcName as string + ); + const smResponse = ContractQueryResponse.fromHttpResponse(data); + const parsedResponse = parser.parseQueryResponse( + smResponse, + endpointDefinition + ); + return parsedResponse; + } + if (type === SCQueryType.BOOLEAN) { return Boolean(Number(data)); } + if (type === SCQueryType.NUMBER) { return Number(data); }