Skip to content

Commit 38ae2d3

Browse files
[SDK] expose getUserOpHash utility function (#6424)
1 parent d753201 commit 38ae2d3

File tree

6 files changed

+77
-19
lines changed

6 files changed

+77
-19
lines changed

.changeset/proud-rockets-play.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Expose getUserOpHash utility function

packages/thirdweb/src/exports/wallets/smart.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export {
55
createUnsignedUserOp,
66
signUserOp,
77
createAndSignUserOp,
8+
getUserOpHash,
89
} from "../../wallets/smart/lib/userop.js";
910

1011
export {

packages/thirdweb/src/wallets/in-app/core/actions/sign-message.enclave.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import type { ClientScopedStorage } from "../authentication/client-scoped-storag
66

77
export async function signMessage({
88
client,
9-
payload: { message, isRaw },
9+
payload: { message, isRaw, originalMessage, chainId },
1010
storage,
1111
}: {
1212
client: ThirdwebClient;
1313
payload: {
1414
message: string;
1515
isRaw: boolean;
16+
originalMessage?: string;
17+
chainId?: number;
1618
};
1719
storage: ClientScopedStorage;
1820
}) {
@@ -37,6 +39,8 @@ export async function signMessage({
3739
messagePayload: {
3840
message,
3941
isRaw,
42+
originalMessage,
43+
chainId,
4044
},
4145
}),
4246
},

packages/thirdweb/src/wallets/in-app/core/wallet/enclave-wallet.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,17 +217,19 @@ export class EnclaveWallet implements IWebWallet {
217217

218218
return { transactionHash };
219219
},
220-
async signMessage({ message }) {
220+
async signMessage({ message, originalMessage, chainId }) {
221221
const messagePayload = (() => {
222222
if (typeof message === "string") {
223-
return { message, isRaw: false };
223+
return { message, isRaw: false, originalMessage, chainId };
224224
}
225225
return {
226226
message:
227227
typeof message.raw === "string"
228228
? message.raw
229229
: bytesToHex(message.raw),
230230
isRaw: true,
231+
originalMessage,
232+
chainId,
231233
};
232234
})();
233235

packages/thirdweb/src/wallets/interfaces/wallet.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,15 @@ export type Account = {
182182
* const signature = await account.signMessage({ message: 'hello!' });
183183
* ```
184184
*/
185-
signMessage: ({ message }: { message: SignableMessage }) => Promise<Hex>;
185+
signMessage: ({
186+
message,
187+
originalMessage,
188+
chainId,
189+
}: {
190+
message: SignableMessage;
191+
originalMessage?: string;
192+
chainId?: number;
193+
}) => Promise<Hex>;
186194
/**
187195
* Sign the given typed data and return the signature
188196
* @example

packages/thirdweb/src/wallets/smart/lib/userop.ts

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { maxUint96 } from "ox/Solidity";
2-
import { concat, keccak256, toHex } from "viem";
2+
import { concat } from "viem";
33
import type { Chain } from "../../../chains/types.js";
44
import type { ThirdwebClient } from "../../../client/client.js";
55
import {
@@ -16,9 +16,11 @@ import type { PreparedTransaction } from "../../../transaction/prepare-transacti
1616
import type { TransactionReceipt } from "../../../transaction/types.js";
1717
import { encodeAbiParameters } from "../../../utils/abi/encodeAbiParameters.js";
1818
import { isContractDeployed } from "../../../utils/bytecode/is-contract-deployed.js";
19-
import type { Hex } from "../../../utils/encoding/hex.js";
19+
import { type Hex, toHex } from "../../../utils/encoding/hex.js";
2020
import { hexToBytes } from "../../../utils/encoding/to-bytes.js";
2121
import { isThirdwebUrl } from "../../../utils/fetch.js";
22+
import { keccak256 } from "../../../utils/hashing/keccak256.js";
23+
import { stringify } from "../../../utils/json.js";
2224
import { resolvePromisedValue } from "../../../utils/promise/resolve-promised-value.js";
2325
import type { Account } from "../../interfaces/wallet.js";
2426
import type {
@@ -597,6 +599,54 @@ export async function signUserOp(args: {
597599
}): Promise<UserOperationV06 | UserOperationV07> {
598600
const { userOp, chain, entrypointAddress, adminAccount } = args;
599601

602+
const userOpHash = await getUserOpHash({
603+
client: args.client,
604+
userOp,
605+
chain,
606+
entrypointAddress,
607+
});
608+
609+
if (adminAccount.signMessage) {
610+
const signature = await adminAccount.signMessage({
611+
message: {
612+
raw: hexToBytes(userOpHash),
613+
},
614+
originalMessage: stringify(userOp),
615+
chainId: chain.id,
616+
});
617+
return {
618+
...userOp,
619+
signature,
620+
};
621+
}
622+
throw new Error("signMessage not implemented in signingAccount");
623+
}
624+
625+
/**
626+
* Get the hash of a user operation.
627+
* @param args - The options for getting the user operation hash
628+
* @returns - The user operation hash
629+
* @example
630+
* ```ts
631+
* import { getUserOpHash } from "thirdweb/wallets/smart";
632+
*
633+
* const userOp = await createUnsignedUserOp(...);
634+
* const userOpHash = await getUserOpHash({
635+
* client,
636+
* userOp,
637+
* chain,
638+
* });
639+
* ```
640+
* @walletUtils
641+
*/
642+
export async function getUserOpHash(args: {
643+
client: ThirdwebClient;
644+
userOp: UserOperationV06 | UserOperationV07;
645+
chain: Chain;
646+
entrypointAddress?: string;
647+
}): Promise<Hex> {
648+
const { userOp, chain, entrypointAddress } = args;
649+
600650
const entrypointVersion = getEntryPointVersion(
601651
entrypointAddress || ENTRYPOINT_ADDRESS_v0_6,
602652
);
@@ -623,19 +673,7 @@ export async function signUserOp(args: {
623673
userOp: userOp as UserOperationV06,
624674
});
625675
}
626-
627-
if (adminAccount.signMessage) {
628-
const signature = await adminAccount.signMessage({
629-
message: {
630-
raw: hexToBytes(userOpHash),
631-
},
632-
});
633-
return {
634-
...userOp,
635-
signature,
636-
};
637-
}
638-
throw new Error("signMessage not implemented in signingAccount");
676+
return userOpHash;
639677
}
640678

641679
async function getAccountInitCode(options: {

0 commit comments

Comments
 (0)