Skip to content

Commit cf22c23

Browse files
[SDK] Optimize 4337 signature performance (#7693)
1 parent 170b020 commit cf22c23

File tree

7 files changed

+57
-214
lines changed

7 files changed

+57
-214
lines changed

.changeset/thirty-eels-decide.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+
Optimize 4337 signature performance

packages/thirdweb/src/pay/buyWithCrypto/getQuote.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,14 @@ export async function getBuyWithCryptoQuote(
300300
Number(
301301
Value.format(quote.originAmount, firstStep.originToken.decimals),
302302
) *
303-
(firstStep.originToken.prices["USD"] || 0) *
303+
(firstStep.originToken.prices.USD || 0) *
304304
100,
305305
amountWei: quote.originAmount.toString(),
306306
token: {
307307
chainId: firstStep.originToken.chainId,
308308
decimals: firstStep.originToken.decimals,
309309
name: firstStep.originToken.name,
310-
priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
310+
priceUSDCents: (firstStep.originToken.prices.USD || 0) * 100,
311311
symbol: firstStep.originToken.symbol,
312312
tokenAddress: firstStep.originToken.address,
313313
},
@@ -323,7 +323,7 @@ export async function getBuyWithCryptoQuote(
323323
chainId: firstStep.originToken.chainId,
324324
decimals: firstStep.originToken.decimals,
325325
name: firstStep.originToken.name,
326-
priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
326+
priceUSDCents: (firstStep.originToken.prices.USD || 0) * 100,
327327
symbol: firstStep.originToken.symbol,
328328
tokenAddress: firstStep.originToken.address,
329329
},
@@ -337,7 +337,7 @@ export async function getBuyWithCryptoQuote(
337337
Number(
338338
Value.format(quote.originAmount, firstStep.originToken.decimals),
339339
) *
340-
(firstStep.originToken.prices["USD"] || 0) *
340+
(firstStep.originToken.prices.USD || 0) *
341341
100,
342342
gasCostUSDCents: 0,
343343
slippageBPS: 0,
@@ -348,7 +348,7 @@ export async function getBuyWithCryptoQuote(
348348
firstStep.destinationToken.decimals,
349349
),
350350
) *
351-
(firstStep.destinationToken.prices["USD"] || 0) *
351+
(firstStep.destinationToken.prices.USD || 0) *
352352
100,
353353
toAmountUSDCents:
354354
Number(
@@ -357,7 +357,7 @@ export async function getBuyWithCryptoQuote(
357357
firstStep.destinationToken.decimals,
358358
),
359359
) *
360-
(firstStep.destinationToken.prices["USD"] || 0) *
360+
(firstStep.destinationToken.prices.USD || 0) *
361361
100,
362362
},
363363
fromAddress: quote.intent.sender,
@@ -372,7 +372,7 @@ export async function getBuyWithCryptoQuote(
372372
chainId: firstStep.originToken.chainId,
373373
decimals: firstStep.originToken.decimals,
374374
name: firstStep.originToken.name,
375-
priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
375+
priceUSDCents: (firstStep.originToken.prices.USD || 0) * 100,
376376
symbol: firstStep.originToken.symbol,
377377
tokenAddress: firstStep.originToken.address,
378378
},
@@ -395,7 +395,7 @@ export async function getBuyWithCryptoQuote(
395395
chainId: firstStep.destinationToken.chainId,
396396
decimals: firstStep.destinationToken.decimals,
397397
name: firstStep.destinationToken.name,
398-
priceUSDCents: (firstStep.destinationToken.prices["USD"] || 0) * 100,
398+
priceUSDCents: (firstStep.destinationToken.prices.USD || 0) * 100,
399399
symbol: firstStep.destinationToken.symbol,
400400
tokenAddress: firstStep.destinationToken.address,
401401
},

packages/thirdweb/src/pay/buyWithCrypto/getTransfer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,14 @@ export async function getBuyWithCryptoTransfer(
198198
Number(
199199
Value.format(quote.originAmount, firstStep.originToken.decimals),
200200
) *
201-
(firstStep.originToken.prices["USD"] || 0) *
201+
(firstStep.originToken.prices.USD || 0) *
202202
100,
203203
amountWei: quote.originAmount.toString(),
204204
token: {
205205
chainId: firstStep.originToken.chainId,
206206
decimals: firstStep.originToken.decimals,
207207
name: firstStep.originToken.name,
208-
priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
208+
priceUSDCents: (firstStep.originToken.prices.USD || 0) * 100,
209209
symbol: firstStep.originToken.symbol,
210210
tokenAddress: firstStep.originToken.address,
211211
},
@@ -226,7 +226,7 @@ export async function getBuyWithCryptoTransfer(
226226
firstStep.originToken.decimals,
227227
),
228228
) *
229-
(firstStep.originToken.prices["USD"] || 0) *
229+
(firstStep.originToken.prices.USD || 0) *
230230
100
231231
: 0,
232232
amountWei:
@@ -237,7 +237,7 @@ export async function getBuyWithCryptoTransfer(
237237
chainId: firstStep.originToken.chainId,
238238
decimals: firstStep.originToken.decimals,
239239
name: firstStep.originToken.name,
240-
priceUSDCents: (firstStep.originToken.prices["USD"] || 0) * 100,
240+
priceUSDCents: (firstStep.originToken.prices.USD || 0) * 100,
241241
symbol: firstStep.originToken.symbol,
242242
tokenAddress: firstStep.originToken.address,
243243
},

packages/thirdweb/src/pay/buyWithFiat/getQuote.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ export async function getBuyWithFiatQuote(
372372
chainId: token.chainId,
373373
decimals: token.decimals,
374374
name: token.name,
375-
priceUSDCents: Math.round((token.prices["USD"] || 0) * 100),
375+
priceUSDCents: Math.round((token.prices.USD || 0) * 100),
376376
symbol: token.symbol,
377377
tokenAddress: token.address,
378378
});
@@ -408,7 +408,7 @@ export async function getBuyWithFiatQuote(
408408
const onRampTokenObject = {
409409
amount: onRampTokenAmount,
410410
amountUSDCents: Math.round(
411-
Number(onRampTokenAmount) * (onRampTokenRaw.prices["USD"] || 0) * 100,
411+
Number(onRampTokenAmount) * (onRampTokenRaw.prices.USD || 0) * 100,
412412
),
413413
amountWei: onRampTokenAmountWei.toString(),
414414
token: tokenToPayTokenInfo(onRampTokenRaw),
@@ -434,7 +434,7 @@ export async function getBuyWithFiatQuote(
434434
routingTokenObject = {
435435
amount: routingAmount,
436436
amountUSDCents: Math.round(
437-
Number(routingAmount) * (routingTokenRaw.prices["USD"] || 0) * 100,
437+
Number(routingAmount) * (routingTokenRaw.prices.USD || 0) * 100,
438438
),
439439
amountWei: routingAmountWei.toString(),
440440
token: tokenToPayTokenInfo(routingTokenRaw),

packages/thirdweb/src/react/core/hooks/usePaymentMethods.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,10 @@ export function usePaymentMethods(options: {
240240
validOwnedTokens.sort((a, b) => {
241241
const aDollarBalance =
242242
Number.parseFloat(toTokens(a.balance, a.originToken.decimals)) *
243-
(a.originToken.prices["USD"] || 0);
243+
(a.originToken.prices.USD || 0);
244244
const bDollarBalance =
245245
Number.parseFloat(toTokens(b.balance, b.originToken.decimals)) *
246-
(b.originToken.prices["USD"] || 0);
246+
(b.originToken.prices.USD || 0);
247247
return bDollarBalance - aDollarBalance;
248248
});
249249

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

Lines changed: 34 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
import type * as ox__TypedData from "ox/TypedData";
22
import { serializeErc6492Signature } from "../../../auth/serialize-erc6492-signature.js";
3-
import {
4-
verifyEip1271Signature,
5-
verifyHash,
6-
} from "../../../auth/verify-hash.js";
3+
import { verifyEip1271Signature } from "../../../auth/verify-hash.js";
74
import type { Chain } from "../../../chains/types.js";
85
import type { ThirdwebClient } from "../../../client/client.js";
9-
import {
10-
getContract,
11-
type ThirdwebContract,
12-
} from "../../../contract/contract.js";
6+
import type { ThirdwebContract } from "../../../contract/contract.js";
137
import { encode } from "../../../transaction/actions/encode.js";
14-
import { readContract } from "../../../transaction/read-contract.js";
158
import { encodeAbiParameters } from "../../../utils/abi/encodeAbiParameters.js";
169
import { isContractDeployed } from "../../../utils/bytecode/is-contract-deployed.js";
17-
import type { Hex } from "../../../utils/encoding/hex.js";
1810
import { hashMessage } from "../../../utils/hashing/hashMessage.js";
1911
import { hashTypedData } from "../../../utils/hashing/hashTypedData.js";
2012
import type { SignableMessage } from "../../../utils/types.js";
@@ -40,33 +32,24 @@ export async function smartAccountSignMessage({
4032
message: SignableMessage;
4133
}) {
4234
const originalMsgHash = hashMessage(message);
43-
const is712Factory = await checkFor712Factory({
44-
accountContract,
45-
factoryContract,
46-
originalMsgHash,
47-
});
4835

4936
let sig: `0x${string}`;
50-
if (is712Factory) {
51-
const wrappedMessageHash = encodeAbiParameters(
52-
[{ type: "bytes32" }],
53-
[originalMsgHash],
54-
);
37+
const wrappedMessageHash = encodeAbiParameters(
38+
[{ type: "bytes32" }],
39+
[originalMsgHash],
40+
);
5541

56-
sig = await options.personalAccount.signTypedData({
57-
domain: {
58-
chainId: options.chain.id,
59-
name: "Account",
60-
verifyingContract: accountContract.address,
61-
version: "1",
62-
},
63-
message: { message: wrappedMessageHash },
64-
primaryType: "AccountMessage",
65-
types: { AccountMessage: [{ name: "message", type: "bytes" }] },
66-
});
67-
} else {
68-
sig = await options.personalAccount.signMessage({ message });
69-
}
42+
sig = await options.personalAccount.signTypedData({
43+
domain: {
44+
chainId: options.chain.id,
45+
name: "Account",
46+
verifyingContract: accountContract.address,
47+
version: "1",
48+
},
49+
message: { message: wrappedMessageHash },
50+
primaryType: "AccountMessage",
51+
types: { AccountMessage: [{ name: "message", type: "bytes" }] },
52+
});
7053

7154
const isDeployed = await isContractDeployed(accountContract);
7255
if (isDeployed) {
@@ -96,19 +79,7 @@ export async function smartAccountSignMessage({
9679
signature: sig,
9780
});
9881

99-
// check if the signature is valid
100-
const isValid = await verifyHash({
101-
address: accountContract.address,
102-
chain: accountContract.chain,
103-
client: accountContract.client,
104-
hash: originalMsgHash,
105-
signature: erc6492Sig,
106-
});
107-
108-
if (isValid) {
109-
return erc6492Sig;
110-
}
111-
throw new Error("Unable to verify ERC-6492 signature after signing.");
82+
return erc6492Sig;
11283
}
11384
}
11485

@@ -138,33 +109,23 @@ export async function smartAccountSignTypedData<
138109
}
139110

140111
const originalMsgHash = hashTypedData(typedData);
141-
// check if the account contract supports EIP721 domain separator based signing
142-
const is712Factory = await checkFor712Factory({
143-
accountContract,
144-
factoryContract,
145-
originalMsgHash,
146-
});
147112

148113
let sig: `0x${string}`;
149-
if (is712Factory) {
150-
const wrappedMessageHash = encodeAbiParameters(
151-
[{ type: "bytes32" }],
152-
[originalMsgHash],
153-
);
154-
sig = await options.personalAccount.signTypedData({
155-
domain: {
156-
chainId: options.chain.id,
157-
name: "Account",
158-
verifyingContract: accountContract.address,
159-
version: "1",
160-
},
161-
message: { message: wrappedMessageHash },
162-
primaryType: "AccountMessage",
163-
types: { AccountMessage: [{ name: "message", type: "bytes" }] },
164-
});
165-
} else {
166-
sig = await options.personalAccount.signTypedData(typedData);
167-
}
114+
const wrappedMessageHash = encodeAbiParameters(
115+
[{ type: "bytes32" }],
116+
[originalMsgHash],
117+
);
118+
sig = await options.personalAccount.signTypedData({
119+
domain: {
120+
chainId: options.chain.id,
121+
name: "Account",
122+
verifyingContract: accountContract.address,
123+
version: "1",
124+
},
125+
message: { message: wrappedMessageHash },
126+
primaryType: "AccountMessage",
127+
types: { AccountMessage: [{ name: "message", type: "bytes" }] },
128+
});
168129

169130
const isDeployed = await isContractDeployed(accountContract);
170131
if (isDeployed) {
@@ -194,21 +155,7 @@ export async function smartAccountSignTypedData<
194155
signature: sig,
195156
});
196157

197-
// check if the signature is valid
198-
const isValid = await verifyHash({
199-
address: accountContract.address,
200-
chain: accountContract.chain,
201-
client: accountContract.client,
202-
hash: originalMsgHash,
203-
signature: erc6492Sig,
204-
});
205-
206-
if (isValid) {
207-
return erc6492Sig;
208-
}
209-
throw new Error(
210-
"Unable to verify signature on smart account, please make sure the admin wallet has permissions and the signature is valid.",
211-
);
158+
return erc6492Sig;
212159
}
213160
}
214161

@@ -233,40 +180,6 @@ export async function confirmContractDeployment(args: {
233180
}
234181
}
235182

236-
async function checkFor712Factory({
237-
factoryContract,
238-
accountContract,
239-
originalMsgHash,
240-
}: {
241-
factoryContract: ThirdwebContract;
242-
accountContract: ThirdwebContract;
243-
originalMsgHash: Hex;
244-
}) {
245-
try {
246-
const implementationAccount = await readContract({
247-
contract: factoryContract,
248-
method: "function accountImplementation() public view returns (address)",
249-
});
250-
// check if the account contract supports EIP721 domain separator or modular based signing
251-
const is712Factory = await readContract({
252-
contract: getContract({
253-
address: implementationAccount,
254-
chain: accountContract.chain,
255-
client: accountContract.client,
256-
}),
257-
method:
258-
"function getMessageHash(bytes32 _hash) public view returns (bytes32)",
259-
params: [originalMsgHash],
260-
})
261-
.then((res) => res !== "0x")
262-
.catch(() => false);
263-
264-
return is712Factory;
265-
} catch {
266-
return false;
267-
}
268-
}
269-
270183
/**
271184
* Deployes a smart account via a dummy transaction. If the account is already deployed, this will do nothing.
272185
*

0 commit comments

Comments
 (0)