Skip to content

Commit e331e43

Browse files
authored
[SDK] Refactor: More Viem to Ox migration (#5790)
1 parent 23d8001 commit e331e43

25 files changed

+334
-339
lines changed

.changeset/hip-llamas-wash.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+
Migrated underlying functionality to Ox

.github/workflows/auto-assign.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Auto Author Assign
22

33
on:
44
pull_request:
5-
types: [opened, reopened]
5+
types: [opened, reopened, ready_for_review, draft]
66

77
permissions:
88
pull-requests: write

packages/thirdweb/src/auth/is-erc6492-signature.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { sliceHex } from "viem";
1+
import * as ox__Hex from "ox/Hex";
22
import type { Hex } from "../utils/encoding/hex.js";
33
import { ERC_6492_MAGIC_VALUE } from "./constants.js";
44

@@ -19,5 +19,5 @@ import { ERC_6492_MAGIC_VALUE } from "./constants.js";
1919
* @auth
2020
*/
2121
export function isErc6492Signature(signature: Hex): boolean {
22-
return sliceHex(signature, -32) === ERC_6492_MAGIC_VALUE;
22+
return ox__Hex.slice(signature, -32) === ERC_6492_MAGIC_VALUE;
2323
}

packages/thirdweb/src/auth/parse-erc6492-signature.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { decodeAbiParameters, isErc6492Signature } from "viem";
1+
import * as ox__AbiParameters from "ox/AbiParameters";
2+
import * as ox__Address from "ox/Address";
3+
import { WrappedSignature as ox__WrappedSignature } from "ox/erc6492";
24
import type { Hex } from "../utils/encoding/hex.js";
35
import type { OneOf } from "../utils/type-utils.js";
46
import type { Erc6492Signature } from "./types.js";
@@ -29,13 +31,17 @@ export type ParseErc6492SignatureReturnType = OneOf<
2931
export function parseErc6492Signature(
3032
signature: Hex,
3133
): ParseErc6492SignatureReturnType {
32-
if (!isErc6492Signature(signature)) {
34+
if (!ox__WrappedSignature.validate(signature)) {
3335
return { signature };
3436
}
3537

36-
const [address, data, originalSignature] = decodeAbiParameters(
38+
const [address, data, originalSignature] = ox__AbiParameters.decode(
3739
[{ type: "address" }, { type: "bytes" }, { type: "bytes" }],
3840
signature,
3941
);
40-
return { address: address, data, signature: originalSignature };
42+
return {
43+
address: ox__Address.checksum(address),
44+
data,
45+
signature: originalSignature,
46+
};
4147
}

packages/thirdweb/src/auth/verify-hash.ts

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
import {
2-
type Signature,
3-
encodeDeployData,
4-
encodeFunctionData,
5-
isErc6492Signature,
6-
serializeSignature,
7-
universalSignatureValidatorAbi,
8-
universalSignatureValidatorByteCode,
9-
} from "viem";
1+
import * as ox__Abi from "ox/Abi";
2+
import * as ox__AbiConstructor from "ox/AbiConstructor";
3+
import * as ox__AbiFunction from "ox/AbiFunction";
4+
import * as ox__Signature from "ox/Signature";
5+
import { WrappedSignature as ox__WrappedSignature } from "ox/erc6492";
106
import type { Chain } from "../chains/types.js";
117
import type { ThirdwebClient } from "../client/client.js";
128
import { type ThirdwebContract, getContract } from "../contract/contract.js";
@@ -20,7 +16,7 @@ import { serializeErc6492Signature } from "./serialize-erc6492-signature.js";
2016

2117
export type VerifyHashParams = {
2218
hash: Hex;
23-
signature: string | Uint8Array | Signature;
19+
signature: string | Uint8Array | ox__Signature.Signature;
2420
address: string;
2521
client: ThirdwebClient;
2622
chain: Chain;
@@ -71,7 +67,7 @@ export async function verifyHash({
7167
const signatureHex = (() => {
7268
if (isHex(signature)) return signature;
7369
if (typeof signature === "object" && "r" in signature && "s" in signature)
74-
return serializeSignature(signature);
70+
return ox__Signature.toHex(signature);
7571
if (signature instanceof Uint8Array) return fromBytes(signature, "hex");
7672
// We should never hit this but TS doesn't know that
7773
throw new Error(
@@ -85,7 +81,7 @@ export async function verifyHash({
8581
if (!accountFactory) return signatureHex;
8682

8783
// If this sigature was already wrapped for ERC-6492, carry on
88-
if (isErc6492Signature(signatureHex)) return signatureHex;
84+
if (ox__WrappedSignature.validate(signatureHex)) return signatureHex;
8985

9086
// Otherwise, serialize the signature for ERC-6492 validation
9187
return serializeErc6492Signature({
@@ -100,23 +96,23 @@ export async function verifyHash({
10096
data: Hex;
10197
};
10298
const zkSyncChain = await isZkSyncChain(chain);
99+
const abi = ox__Abi.from(ox__WrappedSignature.universalSignatureValidatorAbi);
103100
if (zkSyncChain) {
104101
// zksync chains dont support deploying code with eth_call
105102
// need to call a deployed contract instead
106103
verificationData = {
107104
to: ZKSYNC_VALIDATOR_ADDRESS,
108-
data: encodeFunctionData({
109-
abi: universalSignatureValidatorAbi,
110-
functionName: "isValidSig",
111-
args: [address, hash, wrappedSignature],
112-
}),
105+
data: ox__AbiFunction.encodeData(
106+
ox__AbiFunction.fromAbi(abi, "isValidSig"),
107+
[address, hash, wrappedSignature],
108+
),
113109
};
114110
} else {
111+
const validatorConstructor = ox__AbiConstructor.fromAbi(abi);
115112
verificationData = {
116-
data: encodeDeployData({
117-
abi: universalSignatureValidatorAbi,
113+
data: ox__AbiConstructor.encode(validatorConstructor, {
118114
args: [address, hash, wrappedSignature],
119-
bytecode: universalSignatureValidatorByteCode,
115+
bytecode: ox__WrappedSignature.universalSignatureValidatorBytecode,
120116
}),
121117
};
122118
}

packages/thirdweb/src/auth/verify-signature.test.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import * as ox__Bytes from "ox/Bytes";
12
import { describe, expect, it, test } from "vitest";
23
import { FORKED_ETHEREUM_CHAIN } from "../../test/src/chains.js";
34
import { TEST_CLIENT } from "../../test/src/test-clients.js";
45
import { TEST_ACCOUNT_A } from "../../test/src/test-wallets.js";
5-
import { mainnet } from "../chains/chain-definitions/ethereum.js";
6+
import { ethereum, mainnet } from "../chains/chain-definitions/ethereum.js";
67
import { sepolia } from "../chains/chain-definitions/sepolia.js";
7-
import { verifyEOASignature, verifySignature } from "./verify-signature.js";
8+
import { smartWallet } from "../wallets/smart/smart-wallet.js";
9+
import {
10+
verifyContractWalletSignature,
11+
verifyEOASignature,
12+
verifySignature,
13+
} from "./verify-signature.js";
814

915
describe("verifyEOASignature", () => {
1016
test("should return true for a valid signature", async () => {
@@ -98,3 +104,43 @@ describe.runIf(process.env.TW_SECRET_KEY)(
98104
});
99105
},
100106
);
107+
108+
describe.runIf(process.env.TW_SECRET_KEY)(
109+
"verifyContractWalletSignature",
110+
async () => {
111+
const message = "Hakuna matata";
112+
const wallet = smartWallet({
113+
chain: ethereum,
114+
gasless: true,
115+
});
116+
const smartAccount = await wallet.connect({
117+
client: TEST_CLIENT,
118+
personalAccount: TEST_ACCOUNT_A,
119+
});
120+
121+
test("should verify a smart account signature", async () => {
122+
const rawSignature = await smartAccount.signMessage({ message });
123+
const result = await verifyContractWalletSignature({
124+
signature: rawSignature,
125+
message,
126+
address: smartAccount.address,
127+
chain: ethereum,
128+
client: TEST_CLIENT,
129+
});
130+
expect(result).toBe(true);
131+
});
132+
133+
test("should verify a smart account signature as bytes", async () => {
134+
const rawSignature = await smartAccount.signMessage({ message });
135+
const bytesSignature = ox__Bytes.fromHex(rawSignature);
136+
const result = await verifyContractWalletSignature({
137+
signature: bytesSignature,
138+
message,
139+
address: smartAccount.address,
140+
chain: ethereum,
141+
client: TEST_CLIENT,
142+
});
143+
expect(result).toBe(true);
144+
});
145+
},
146+
);

packages/thirdweb/src/auth/verify-signature.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
1-
import { type SignableMessage, type Signature, recoverAddress } from "viem";
1+
import * as ox__Bytes from "ox/Bytes";
2+
import * as ox__Secp256k1 from "ox/Secp256k1";
3+
import * as ox__Signature from "ox/Signature";
24
import type { Chain } from "../chains/types.js";
35
import type { ThirdwebClient } from "../client/client.js";
46
import { type Hex, isHex } from "../utils/encoding/hex.js";
57
import { hashMessage } from "../utils/hashing/hashMessage.js";
68
import type { Prettify } from "../utils/type-utils.js";
79
import { verifyHash } from "./verify-hash.js";
810

11+
type Message = Prettify<
12+
| string
13+
| {
14+
raw: Hex | Uint8Array;
15+
}
16+
>;
17+
918
/**
1019
* @auth
1120
*/
1221
export type VerifyEOASignatureParams = {
13-
message: string | SignableMessage;
14-
signature: string | Uint8Array | Signature;
22+
message: string | Message;
23+
signature: string | Uint8Array;
1524
address: string;
1625
};
1726

@@ -39,9 +48,9 @@ export async function verifyEOASignature(options: VerifyEOASignatureParams) {
3948
return false;
4049
}
4150

42-
const recoveredAddress = await recoverAddress({
43-
hash: messageHash,
44-
signature: options.signature,
51+
const recoveredAddress = ox__Secp256k1.recoverAddress({
52+
payload: messageHash,
53+
signature: ox__Signature.fromHex(options.signature),
4554
});
4655

4756
if (recoveredAddress.toLowerCase() === options.address.toLowerCase()) {
@@ -103,9 +112,17 @@ export async function verifyContractWalletSignature({
103112
accountFactory,
104113
}: VerifyContractWalletSignatureParams) {
105114
const messageHash = hashMessage(message);
115+
116+
const parsedSignature = (() => {
117+
if (ox__Bytes.validate(signature)) {
118+
return ox__Bytes.toHex(signature);
119+
}
120+
return signature;
121+
})();
122+
106123
return verifyHash({
107124
hash: messageHash,
108-
signature,
125+
signature: parsedSignature,
109126
address,
110127
client,
111128
chain,

packages/thirdweb/src/auth/verify-typed-data.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
import type { Signature, TypedData, TypedDataDefinition } from "viem";
2-
import { hashTypedData } from "viem";
1+
import type * as ox__Signature from "ox/Signature";
2+
import * as ox__TypedData from "ox/TypedData";
33
import type { Chain } from "../chains/types.js";
44
import type { ThirdwebClient } from "../client/client.js";
55
import type { Hex } from "../utils/encoding/hex.js";
66
import type { HashTypedDataParams } from "../utils/hashing/hashTypedData.js";
77
import { type VerifyHashParams, verifyHash } from "./verify-hash.js";
88

99
export type VerifyTypedDataParams<
10-
typedData extends TypedData | Record<string, unknown> = TypedData,
10+
typedData extends
11+
| ox__TypedData.TypedData
12+
| Record<string, unknown> = ox__TypedData.TypedData,
1113
primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
1214
> = Omit<VerifyHashParams, "hash"> &
13-
TypedDataDefinition<typedData, primaryType> & {
15+
ox__TypedData.Definition<typedData, primaryType> & {
1416
address: string;
15-
signature: string | Uint8Array | Signature;
17+
signature: string | Uint8Array | ox__Signature.Signature;
1618
client: ThirdwebClient;
1719
chain: Chain;
1820
accountFactory?: {
@@ -80,7 +82,7 @@ export type VerifyTypedDataParams<
8082
* @auth
8183
*/
8284
export async function verifyTypedData<
83-
typedData extends TypedData | Record<string, unknown>,
85+
typedData extends ox__TypedData.TypedData | Record<string, unknown>,
8486
primaryType extends keyof typedData | "EIP712Domain",
8587
>({
8688
address,
@@ -93,7 +95,7 @@ export async function verifyTypedData<
9395
primaryType,
9496
types,
9597
}: VerifyTypedDataParams<typedData, primaryType>): Promise<boolean> {
96-
const messageHash = hashTypedData({
98+
const messageHash = ox__TypedData.getSignPayload({
9799
message,
98100
domain,
99101
primaryType,

packages/thirdweb/src/contract/deployment/utils/create-2-factory.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { getContractAddress } from "viem";
1+
import * as ox__ContractAddress from "ox/ContractAddress";
22
import { getGasPrice } from "../../../gas/get-gas-price.js";
33
import { eth_getBalance } from "../../../rpc/actions/eth_getBalance.js";
44
import { eth_sendRawTransaction } from "../../../rpc/actions/eth_sendRawTransaction.js";
55
import { getRpcClient } from "../../../rpc/rpc.js";
66
import { sendTransaction } from "../../../transaction/actions/send-transaction.js";
77
import { waitForReceipt } from "../../../transaction/actions/wait-for-tx-receipt.js";
88
import { prepareTransaction } from "../../../transaction/prepare-transaction.js";
9+
import { getAddress } from "../../../utils/address.js";
910
import { isEIP155Enforced } from "../../../utils/any-evm/is-eip155-enforced.js";
1011
import { getKeylessTransaction } from "../../../utils/any-evm/keyless-transaction.js";
1112
import { isContractDeployed } from "../../../utils/bytecode/is-contract-deployed.js";
@@ -226,15 +227,15 @@ async function _getCreate2FactoryDeploymentInfo(
226227
},
227228
signature: SIGNATURE,
228229
});
229-
const create2FactoryAddress = getContractAddress({
230+
const create2FactoryAddress = ox__ContractAddress.from({
230231
from: deploymentTransaction.signerAddress,
231232
nonce: 0n,
232233
});
233234

234235
return {
235236
...deploymentTransaction,
236237
valueToSend: gasPrice * gas,
237-
predictedAddress: create2FactoryAddress,
238+
predictedAddress: getAddress(create2FactoryAddress),
238239
};
239240
}
240241

packages/thirdweb/src/contract/verification/constructor-params.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { Abi } from "abitype";
2-
import { decodeAbiParameters } from "viem";
1+
import type * as ox__Abi from "ox/Abi";
2+
import * as ox__AbiConstructor from "ox/AbiConstructor";
3+
import * as ox__AbiParameters from "ox/AbiParameters";
34
import { eth_getTransactionByHash } from "../../rpc/actions/eth_getTransactionByHash.js";
45
import { getRpcClient } from "../../rpc/rpc.js";
56
import type { ThirdwebContract } from "../contract.js";
@@ -10,19 +11,9 @@ type FetchConstructorParamsOptions = {
1011
contract: ThirdwebContract;
1112
explorerApiUrl: string;
1213
explorerApiKey: string;
13-
abi: Abi;
14+
abi: ox__Abi.Abi;
1415
};
1516

16-
// TODO: move to abi helpers (?)
17-
function extractConstructorParamsFromAbi(abi: Abi) {
18-
for (const input of abi) {
19-
if (input.type === "constructor") {
20-
return input.inputs || [];
21-
}
22-
}
23-
return [];
24-
}
25-
2617
const RequestStatus = {
2718
OK: "1",
2819
NOTOK: "0",
@@ -37,7 +28,8 @@ const RequestStatus = {
3728
export async function fetchConstructorParams(
3829
options: FetchConstructorParamsOptions,
3930
): Promise<string> {
40-
const constructorParamTypes = extractConstructorParamsFromAbi(options.abi);
31+
const abiConstructor = ox__AbiConstructor.fromAbi(options.abi);
32+
const constructorParamTypes = ox__AbiParameters.from(abiConstructor.inputs);
4133
if (constructorParamTypes.length === 0) {
4234
return "";
4335
}
@@ -114,7 +106,8 @@ export async function fetchConstructorParams(
114106
try {
115107
// sanity check that the constructor params are valid
116108
// TODO: should we sanity check after each attempt?
117-
decodeAbiParameters(constructorParamTypes, `0x${constructorArgs}`);
109+
110+
ox__AbiParameters.decode(constructorParamTypes, `0x${constructorArgs}`);
118111
} catch {
119112
throw new Error(
120113
"Verifying this contract requires it to be published. Run `npx thirdweb publish` to publish this contract, then try again.",

0 commit comments

Comments
 (0)