diff --git a/packages/kit/README.md b/packages/kit/README.md index 4c3f031..b0b9dcc 100644 --- a/packages/kit/README.md +++ b/packages/kit/README.md @@ -84,6 +84,7 @@ function App() { ## Hooks +- [useTransact](https://github.com/suiware/kit/blob/main/packages/kit/docs/useTransact.md) - [useBalance](https://github.com/suiware/kit/blob/main/packages/kit/docs/useBalance.md) - [useFaucet](https://github.com/suiware/kit/blob/main/packages/kit/docs/useFaucet.md) - [useNetworkType](https://github.com/suiware/kit/blob/main/packages/kit/docs/useNetworkType.md) diff --git a/packages/kit/docs/useTransact.md b/packages/kit/docs/useTransact.md new file mode 100644 index 0000000..adedc74 --- /dev/null +++ b/packages/kit/docs/useTransact.md @@ -0,0 +1,129 @@ +# useTransact + +`useTransact` is a React hook that facilitates performing transactions on the Sui network, handling wallet interactions and transaction lifecycle events. + +## Usage + +```tsx +import { useTransact } from '@suiware/kit' +import { Transaction } from '@mysten/sui/transactions' + +function MyComponent() { + const { transact } = useTransact({ + onBeforeStart: () => console.log('Transaction starting...'), + onSuccess: (data) => console.log('Transaction succeeded:', data), + onError: (e) => console.error('Transaction failed:', e) + }) + + const handleTransaction = () => { + const tx = new Transaction() + // Configure transaction... + transact(tx) + } + + return ( + + ) +} +``` + +## Parameters + +The hook accepts an options object with the following properties: + +| Parameter | Type | Description | +| --- | --- | --- | +| onBeforeStart | () => void | (Optional) Callback executed when user triggers a transaction | +| onSuccess | (data: SuiSignAndExecuteTransactionOutput) => void | (Optional) Callback executed after successful transaction completion | +| onError | (e: Error) => void | (Optional) Callback executed if transaction fails | + +## Return Value + +The hook returns an object with: + +| Property | Type | Description | +| --- | --- | --- | +| transact | (tx: Transaction) => void | Function to execute a transaction on the Sui network | + +## Features + +- Handles wallet interaction for transaction signing + +- Automatic transaction confirmation tracking + +- Success/error notification management + +- Integration with Sui Explorer for transaction viewing + +- Complete transaction lifecycle management + +## Usage Examples + +### Basic Transaction + +```tsx +const { transact } = useTransact() + +const handleTransaction = () => { + const tx = new Transaction() + tx.moveCall({ + target: `${packageId}::module::function`, + arguments: [/* ... */], + }) + transact(tx) +} +``` + +### With Event Handlers + +```tsx +const { transact } = useTransact({ + onBeforeStart: () => { + setLoading(true) + }, + onSuccess: (data) => { + setLoading(false) + refreshData() + }, + onError: (e) => { + setLoading(false) + showError(e.message) + } +}) +``` + +### Full Example with Move Call + +```tsx +import { useTransact } from '@suiware/kit' +import { Transaction } from '@mysten/sui/transactions' + +function GreetingComponent() { + const { transact } = useTransact({ + onSuccess: () => { + console.log('Greeting updated!') + } + }) + + const updateGreeting = (packageId: string, objectId: string, name: string) => { + const tx = new Transaction() + tx.moveCall({ + arguments: [ + tx.object(objectId), + tx.pure.string(name), + tx.object('0x8') + ], + target: `${packageId}::greeting::set_greeting`, + }) + transact(tx) + } + + return ( + + ) +} +``` diff --git a/packages/kit/package.json b/packages/kit/package.json index be87cb5..8b09e55 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -1,6 +1,6 @@ { "name": "@suiware/kit", - "version": "0.4.5", + "version": "0.5.0", "sideEffects": false, "type": "module", "license": "MIT", @@ -107,6 +107,16 @@ "types": "./dist/components/AmountInput.d.cts", "default": "./dist/components/AmountInput.cjs" } + }, + "./useTransact": { + "import": { + "types": "./dist/hooks/useTransact.d.mts", + "default": "./dist/hooks/useTransact.mjs" + }, + "require": { + "types": "./dist/hooks/useTransact.d.cts", + "default": "./dist/hooks/useTransact.cjs" + } } }, "files": [ diff --git a/packages/kit/src/components/AddressInput.tsx b/packages/kit/src/components/AddressInput.tsx index 1b622a4..7385e15 100644 --- a/packages/kit/src/components/AddressInput.tsx +++ b/packages/kit/src/components/AddressInput.tsx @@ -4,7 +4,6 @@ import debounce from 'lodash.debounce' import { ChangeEvent, FC, useCallback, useEffect, useState } from 'react' import { resolveSuinsName } from '~~/helpers/suins' - const DEBOUNCE_DELAY = 500 export interface IAddressInput { diff --git a/packages/kit/src/hooks/useTransact.ts b/packages/kit/src/hooks/useTransact.ts new file mode 100644 index 0000000..8a3a4ad --- /dev/null +++ b/packages/kit/src/hooks/useTransact.ts @@ -0,0 +1,112 @@ +import { useSignAndExecuteTransaction, useSuiClient } from '@mysten/dapp-kit' +import { Transaction } from '@mysten/sui/transactions' +import type { SuiSignAndExecuteTransactionOutput } from '@mysten/wallet-standard' + +export interface IUseTransactParams { + /** + * (Optional) Is executed when user triggers a transaction. + */ + onBeforeStart?: () => void + /** + * (Optional) React on success, e.g. refetch dependent queries. + * + * @param {SuiSignAndExecuteTransactionOutput} data The transaction output. + */ + onSuccess?: (data: SuiSignAndExecuteTransactionOutput) => void + /** + * (Optional) React on error. + * + * @param {Error} e The error. + */ + onError?: (e: Error) => void +} + +export interface IUseTransactResponse { + /** + * Perform a transaction on the Sui network. + * + * @param {Transaction} tx The transaction to perform. + */ + transact: (tx: Transaction) => void +} + +/** + * The useTransact() hook lets you perform a transaction on the Sui network. + * + * When user triggers a transaction, we display a notification to confirm that transaction in their wallet. + * Once user confirmed or rejected the transaction, we convert that notification to a success message or error message. + * The success message will have a link to a Sui Explorer depending on the active network. + * In case of error, we additionally print full error message in the browser console. + * + * Usage: + * ```ts + * import type { SuiSignAndExecuteTransactionOutput } from '@mysten/wallet-standard' + * import { Transaction } from '@mysten/sui/transactions' + * const { transact: greet } = useTransact({ + * onBeforeStart: () => {}, + * onSuccess: (data: SuiSignAndExecuteTransactionOutput) => {}, + * onError: (e: Error) => {} + * }) + * + * const prepareTransaction = (packageId: string, objectId: string, name: string) => { + * const tx = new Transaction() + * tx.moveCall({ + * arguments: [tx.object(objectId), tx.pure.string(name), tx.object('0x8')], + * target: `${packageId}::greeting::set_greeting`, + * }) + * return tx + * } + * + * greet(prepareTransaction(packageId, objectId, name)) + * ``` + * + * @param {IUseTransactParams} The hook params. + * @returns {IUseTransactResponse} An object with the transact function. + */ +const useTransact = ({ + onBeforeStart, + onSuccess, + onError, +}: IUseTransactParams = {}): IUseTransactResponse => { + const client = useSuiClient() + const { mutate: signAndExecute } = useSignAndExecuteTransaction() + + const transact = (tx: Transaction) => { + if (onBeforeStart != null) { + onBeforeStart() + } + + signAndExecute( + { + transaction: tx, + }, + { + onError: (e: Error) => { + if (onError != null) { + onError(e) + } + }, + onSuccess: (data: SuiSignAndExecuteTransactionOutput) => { + client + .waitForTransaction({ + digest: data.digest, + }) + .then(() => { + if (onSuccess != null) { + onSuccess(data) + } + }) + .catch((e) => { + if (onError != null) { + onError(e) + } + }) + }, + } + ) + } + + return { transact } +} + +export default useTransact diff --git a/packages/kit/tsup.config.ts b/packages/kit/tsup.config.ts index 891423f..0e940a2 100644 --- a/packages/kit/tsup.config.ts +++ b/packages/kit/tsup.config.ts @@ -12,6 +12,7 @@ export default defineConfig((options) => ({ "src/providers/SuiProvider.tsx", "src/components/AddressInput.tsx", "src/components/AmountInput.tsx", + "src/hooks/useTransact.tsx", ], format: ["cjs", "esm"], dts: true,