Skip to content

Commit

Permalink
[dapp-kit] execute from dApp rather than wallet when using useSignAnd…
Browse files Browse the repository at this point in the history
…ExecuteTransactionBlock (#14200)

## Description 

Describe the changes or additions included in this PR.

## Test Plan 

How did you test the new or updated feature?

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [ ] protocol change
- [ ] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
  • Loading branch information
hayes-mysten authored Oct 11, 2023
1 parent 9a89ca6 commit 361818a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/dull-dryers-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@mysten/dapp-kit': minor
---

execute transaction from dApp rather than wallet in useSignAndExecuteTransactionBlock
45 changes: 37 additions & 8 deletions sdk/dapp-kit/src/hooks/wallet/useSignAndExecuteTransactionBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
WalletNotConnectedError,
} from '../../errors/walletErrors.js';
import type { PartialBy } from '../../types/utilityTypes.js';
import { useSuiClient } from '../useSuiClient.js';
import { useCurrentAccount } from './useCurrentAccount.js';
import { useCurrentWallet } from './useCurrentWallet.js';

Expand All @@ -39,43 +40,71 @@ type UseSignAndExecuteTransactionBlockMutationOptions = Omit<
unknown
>,
'mutationFn'
>;
> & {
executeFromWallet?: boolean;
};

/**
* Mutation hook for prompting the user to sign and execute a transaction block.
*/
export function useSignAndExecuteTransactionBlock({
mutationKey,
executeFromWallet,
...mutationOptions
}: UseSignAndExecuteTransactionBlockMutationOptions = {}) {
const currentWallet = useCurrentWallet();
const currentAccount = useCurrentAccount();
const client = useSuiClient();

return useMutation({
mutationKey: walletMutationKeys.signAndExecuteTransactionBlock(mutationKey),
mutationFn: async (signAndExecuteTransactionBlockArgs) => {
mutationFn: async ({ requestType, options, ...signTransactionBlockArgs }) => {
if (!currentWallet) {
throw new WalletNotConnectedError('No wallet is connected.');
}

const signerAccount = signAndExecuteTransactionBlockArgs.account ?? currentAccount;
const signerAccount = signTransactionBlockArgs.account ?? currentAccount;
if (!signerAccount) {
throw new WalletNoAccountSelectedError(
'No wallet account is selected to sign and execute the transaction block with.',
);
}

const walletFeature = currentWallet.features['sui:signAndExecuteTransactionBlock'];
if (executeFromWallet) {
const walletFeature = currentWallet.features['sui:signAndExecuteTransactionBlock'];
if (!walletFeature) {
throw new WalletFeatureNotSupportedError(
"This wallet doesn't support the `signAndExecuteTransactionBlock` feature.",
);
}

return walletFeature.signAndExecuteTransactionBlock({
...signTransactionBlockArgs,
account: signerAccount,
chain: signTransactionBlockArgs.chain ?? signerAccount.chains[0],
requestType,
options,
});
}

const walletFeature = currentWallet.features['sui:signTransactionBlock'];
if (!walletFeature) {
throw new WalletFeatureNotSupportedError(
"This wallet doesn't support the `signAndExecuteTransactionBlock` feature.",
"This wallet doesn't support the `signTransactionBlock` feature.",
);
}

return await walletFeature.signAndExecuteTransactionBlock({
...signAndExecuteTransactionBlockArgs,
const { signature, transactionBlockBytes } = await walletFeature.signTransactionBlock({
...signTransactionBlockArgs,
account: signerAccount,
chain: signAndExecuteTransactionBlockArgs.chain ?? signerAccount.chains[0],
chain: signTransactionBlockArgs.chain ?? signerAccount.chains[0],
});

return client.executeTransactionBlock({
transactionBlock: transactionBlockBytes,
signature,
requestType,
options,
});
},
...mutationOptions,
Expand Down
23 changes: 16 additions & 7 deletions sdk/dapp-kit/test/hooks/useSignAndExecuteTransactionBlock.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { getFullnodeUrl, SuiClient } from '@mysten/sui.js/client';
import { TransactionBlock } from '@mysten/sui.js/transactions';
import { act, renderHook, waitFor } from '@testing-library/react';
import type { Mock } from 'vitest';
Expand Down Expand Up @@ -59,7 +60,12 @@ describe('useSignAndExecuteTransactionBlock', () => {
features: suiFeatures,
});

const wrapper = createWalletProviderContextWrapper();
const suiClient = new SuiClient({ url: getFullnodeUrl('localnet') });
const executeTransactionBlock = vi.spyOn(suiClient, 'executeTransactionBlock');

executeTransactionBlock.mockReturnValueOnce(Promise.resolve({ digest: '123' }));

const wrapper = createWalletProviderContextWrapper({}, suiClient);
const { result } = renderHook(
() => ({
connectWallet: useConnectWallet(),
Expand All @@ -72,13 +78,12 @@ describe('useSignAndExecuteTransactionBlock', () => {

await waitFor(() => expect(result.current.connectWallet.isSuccess).toBe(true));

const useSignAndExecuteTransactionBlockFeature =
mockWallet.features['sui:signAndExecuteTransactionBlock'];
const useSignAndExecuteTransactionBlockMock = useSignAndExecuteTransactionBlockFeature!
.signAndExecuteTransactionBlock as Mock;
const signTransactionBlockFeature = mockWallet.features['sui:signTransactionBlock'];
const signTransactionBlockMock = signTransactionBlockFeature!.signTransactionBlock as Mock;

useSignAndExecuteTransactionBlockMock.mockReturnValueOnce({
digest: '123',
signTransactionBlockMock.mockReturnValueOnce({
transactionBlockBytes: 'abc',
signature: '123',
});

result.current.useSignAndExecuteTransactionBlock.mutate({
Expand All @@ -92,6 +97,10 @@ describe('useSignAndExecuteTransactionBlock', () => {
expect(result.current.useSignAndExecuteTransactionBlock.data).toStrictEqual({
digest: '123',
});
expect(suiClient.executeTransactionBlock).toHaveBeenCalledWith({
transactionBlock: 'abc',
signature: '123',
});

act(() => unregister());
});
Expand Down
5 changes: 3 additions & 2 deletions sdk/dapp-kit/test/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import type { SuiClient } from '@mysten/sui.js/client';
import { getFullnodeUrl, SuiClient } from '@mysten/sui.js/client';
import type { IdentifierRecord, ReadonlyWalletAccount } from '@mysten/wallet-standard';
import { getWallets } from '@mysten/wallet-standard';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
Expand All @@ -20,11 +20,12 @@ export function createSuiClientContextWrapper(client: SuiClient) {

export function createWalletProviderContextWrapper(
providerProps: Omit<ComponentProps<typeof WalletProvider>, 'children'> = {},
suiClient: SuiClient = new SuiClient({ url: getFullnodeUrl('localnet') }),
) {
const queryClient = new QueryClient();
return function WalletProviderContextWrapper({ children }: { children: React.ReactNode }) {
return (
<SuiClientProvider>
<SuiClientProvider networks={{ test: suiClient }}>
<QueryClientProvider client={queryClient}>
<WalletProvider {...providerProps}>{children}</WalletProvider>;
</QueryClientProvider>
Expand Down

3 comments on commit 361818a

@vercel
Copy link

@vercel vercel bot commented on 361818a Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 361818a Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 361818a Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

mysten-ui – ./apps/ui

mysten-ui-mysten-labs.vercel.app
mysten-ui-git-main-mysten-labs.vercel.app
mysten-ui.vercel.app

Please sign in to comment.