thirdweb 5.34.0-nightly-f6ff5a78fc2d65f0f250b154f1405210ca57ce0a-20240707000358 → 5.34.0-nightly-325416ea19905901f30e795cbf93cb8a085be02f-20240708203622
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/auth/core/verify-jwt.js +1 -1
- package/dist/cjs/auth/core/verify-jwt.js.map +1 -1
- package/dist/cjs/auth/verify-hash.js +98 -0
- package/dist/cjs/auth/verify-hash.js.map +1 -0
- package/dist/cjs/auth/verify-signature.js +5 -60
- package/dist/cjs/auth/verify-signature.js.map +1 -1
- package/dist/cjs/auth/verify-typed-data.js +80 -0
- package/dist/cjs/auth/verify-typed-data.js.map +1 -0
- package/dist/cjs/chains/chain-definitions/blast.js +17 -0
- package/dist/cjs/chains/chain-definitions/blast.js.map +1 -0
- package/dist/cjs/exports/chains.js +3 -1
- package/dist/cjs/exports/chains.js.map +1 -1
- package/dist/cjs/exports/extensions/erc20.js +3 -1
- package/dist/cjs/exports/extensions/erc20.js.map +1 -1
- package/dist/cjs/exports/utils.js +10 -1
- package/dist/cjs/exports/utils.js.map +1 -1
- package/dist/cjs/extensions/erc1271/checkContractWalletSignature.js +1 -0
- package/dist/cjs/extensions/erc1271/checkContractWalletSignature.js.map +1 -1
- package/dist/cjs/extensions/erc1271/checkContractWalletSignedTypedData.js +1 -0
- package/dist/cjs/extensions/erc1271/checkContractWalletSignedTypedData.js.map +1 -1
- package/dist/cjs/extensions/erc20/write/transferBatch.js +61 -0
- package/dist/cjs/extensions/erc20/write/transferBatch.js.map +1 -0
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +21 -12
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/PayWIthCreditCard.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js +4 -4
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatTxDetailsTable.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js +1 -3
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js +1 -4
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/Fees.js +4 -4
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/tx-history/BuyTxHistoryButton.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/tx-history/TokenInfoRow.js +1 -1
- package/dist/cjs/utils/hashing/hashTypedData.js +122 -0
- package/dist/cjs/utils/hashing/hashTypedData.js.map +1 -0
- package/dist/cjs/utils/jwt/decode-jwt.js +2 -2
- package/dist/cjs/utils/jwt/decode-jwt.js.map +1 -1
- package/dist/cjs/utils/jwt/refresh-jwt.js +1 -1
- package/dist/cjs/utils/jwt/refresh-jwt.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/auth/core/verify-jwt.js +2 -2
- package/dist/esm/auth/core/verify-jwt.js.map +1 -1
- package/dist/esm/auth/verify-hash.js +95 -0
- package/dist/esm/auth/verify-hash.js.map +1 -0
- package/dist/esm/auth/verify-signature.js +6 -61
- package/dist/esm/auth/verify-signature.js.map +1 -1
- package/dist/esm/auth/verify-typed-data.js +77 -0
- package/dist/esm/auth/verify-typed-data.js.map +1 -0
- package/dist/esm/chains/chain-definitions/blast.js +14 -0
- package/dist/esm/chains/chain-definitions/blast.js.map +1 -0
- package/dist/esm/exports/chains.js +1 -0
- package/dist/esm/exports/chains.js.map +1 -1
- package/dist/esm/exports/extensions/erc20.js +1 -0
- package/dist/esm/exports/extensions/erc20.js.map +1 -1
- package/dist/esm/exports/utils.js +6 -0
- package/dist/esm/exports/utils.js.map +1 -1
- package/dist/esm/extensions/erc1271/checkContractWalletSignature.js +1 -0
- package/dist/esm/extensions/erc1271/checkContractWalletSignature.js.map +1 -1
- package/dist/esm/extensions/erc1271/checkContractWalletSignedTypedData.js +1 -0
- package/dist/esm/extensions/erc1271/checkContractWalletSignedTypedData.js.map +1 -1
- package/dist/esm/extensions/erc20/write/transferBatch.js +58 -0
- package/dist/esm/extensions/erc20/write/transferBatch.js.map +1 -0
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +21 -12
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/PayWIthCreditCard.js +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.js +4 -4
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatTxDetailsTable.js +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js +1 -3
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js +1 -4
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.js +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/Fees.js +4 -4
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.js +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/tx-history/BuyTxHistoryButton.js +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/tx-history/TokenInfoRow.js +1 -1
- package/dist/esm/utils/hashing/hashTypedData.js +118 -0
- package/dist/esm/utils/hashing/hashTypedData.js.map +1 -0
- package/dist/esm/utils/jwt/decode-jwt.js +1 -1
- package/dist/esm/utils/jwt/decode-jwt.js.map +1 -1
- package/dist/esm/utils/jwt/refresh-jwt.js +2 -2
- package/dist/esm/utils/jwt/refresh-jwt.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/types/auth/verify-hash.d.ts +45 -0
- package/dist/types/auth/verify-hash.d.ts.map +1 -0
- package/dist/types/auth/verify-signature.d.ts.map +1 -1
- package/dist/types/auth/verify-typed-data.d.ts +75 -0
- package/dist/types/auth/verify-typed-data.d.ts.map +1 -0
- package/dist/types/chains/chain-definitions/blast.d.ts +27 -0
- package/dist/types/chains/chain-definitions/blast.d.ts.map +1 -0
- package/dist/types/exports/chains.d.ts +1 -0
- package/dist/types/exports/chains.d.ts.map +1 -1
- package/dist/types/exports/extensions/erc20.d.ts +1 -0
- package/dist/types/exports/extensions/erc20.d.ts.map +1 -1
- package/dist/types/exports/utils.d.ts +4 -0
- package/dist/types/exports/utils.d.ts.map +1 -1
- package/dist/types/extensions/erc1271/checkContractWalletSignature.d.ts +1 -0
- package/dist/types/extensions/erc1271/checkContractWalletSignature.d.ts.map +1 -1
- package/dist/types/extensions/erc1271/checkContractWalletSignedTypedData.d.ts +1 -0
- package/dist/types/extensions/erc1271/checkContractWalletSignedTypedData.d.ts.map +1 -1
- package/dist/types/extensions/erc20/write/transferBatch.d.ts +42 -0
- package/dist/types/extensions/erc20/write/transferBatch.d.ts.map +1 -0
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.d.ts.map +1 -1
- package/dist/types/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.d.ts.map +1 -1
- package/dist/types/utils/hashing/hashTypedData.d.ts +17 -0
- package/dist/types/utils/hashing/hashTypedData.d.ts.map +1 -0
- package/dist/types/utils/jwt/decode-jwt.d.ts +1 -1
- package/dist/types/utils/jwt/decode-jwt.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/auth/core/verify-jwt.ts +2 -2
- package/src/auth/verify-hash.test.ts +66 -0
- package/src/auth/verify-hash.ts +126 -0
- package/src/auth/verify-signature.ts +6 -77
- package/src/auth/verify-typed-data.test.ts +82 -0
- package/src/auth/verify-typed-data.ts +110 -0
- package/src/chains/chain-definitions/blast.ts +14 -0
- package/src/exports/chains.ts +1 -0
- package/src/exports/extensions/erc20.ts +4 -0
- package/src/exports/utils.ts +8 -0
- package/src/extensions/erc1271/checkContractWalletSignature.ts +1 -0
- package/src/extensions/erc1271/checkContractWalletSignedTypedData.ts +1 -0
- package/src/extensions/erc20/write/transferBatch.ts +78 -0
- package/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +42 -12
- package/src/react/web/ui/ConnectWallet/screens/Buy/PayWIthCreditCard.tsx +1 -1
- package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatSteps.tsx +5 -5
- package/src/react/web/ui/ConnectWallet/screens/Buy/fiat/FiatTxDetailsTable.tsx +1 -1
- package/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts +1 -5
- package/src/react/web/ui/ConnectWallet/screens/Buy/main/useUISelectionStates.ts +1 -6
- package/src/react/web/ui/ConnectWallet/screens/Buy/swap/ConfirmationScreen.tsx +2 -2
- package/src/react/web/ui/ConnectWallet/screens/Buy/swap/Fees.tsx +4 -4
- package/src/react/web/ui/ConnectWallet/screens/Buy/swap/PayWithCrypto.tsx +1 -1
- package/src/react/web/ui/ConnectWallet/screens/Buy/tx-history/BuyTxHistoryButton.tsx +1 -1
- package/src/react/web/ui/ConnectWallet/screens/Buy/tx-history/TokenInfoRow.tsx +1 -1
- package/src/utils/hashing/hashTypedData.test.ts +207 -0
- package/src/utils/hashing/hashTypedData.ts +210 -0
- package/src/utils/jwt/decode-jwt.ts +1 -1
- package/src/utils/jwt/refresh-jwt.ts +2 -2
- package/src/version.ts +1 -1
@@ -1,27 +1,10 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
type Signature,
|
4
|
-
encodeDeployData,
|
5
|
-
recoverAddress,
|
6
|
-
serializeSignature,
|
7
|
-
} from "viem";
|
1
|
+
import { type Signature, recoverAddress } from "viem";
|
8
2
|
import type { Chain } from "../chains/types.js";
|
9
3
|
import type { ThirdwebClient } from "../client/client.js";
|
10
|
-
import { getContract } from "../contract/contract.js";
|
11
|
-
import { eth_call } from "../rpc/actions/eth_call.js";
|
12
|
-
import { getRpcClient } from "../rpc/rpc.js";
|
13
|
-
import { fromBytes } from "../utils/encoding/from-bytes.js";
|
14
4
|
import { type Hex, isHex } from "../utils/encoding/hex.js";
|
15
|
-
import { toBytes } from "../utils/encoding/to-bytes.js";
|
16
5
|
import { hashMessage } from "../utils/hashing/hashMessage.js";
|
17
6
|
import type { Prettify } from "../utils/type-utils.js";
|
18
|
-
import {
|
19
|
-
import {
|
20
|
-
universalSignatureValidatorAbi,
|
21
|
-
universalSignatureValidatorByteCode,
|
22
|
-
} from "./constants.js";
|
23
|
-
import { isErc6492Signature } from "./is-erc6492-signature.js";
|
24
|
-
import { serializeErc6492Signature } from "./serialize-erc6492-signature.js";
|
7
|
+
import { verifyHash } from "./verify-hash.js";
|
25
8
|
|
26
9
|
export type VerifyEOASignatureParams = {
|
27
10
|
message: string;
|
@@ -113,69 +96,15 @@ export async function verifyContractWalletSignature({
|
|
113
96
|
client,
|
114
97
|
accountFactory,
|
115
98
|
}: VerifyContractWalletSignatureParams) {
|
116
|
-
console.log("verifyContractWalletSignature");
|
117
99
|
const messageHash = hashMessage(message);
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
if (typeof signature === "object" && "r" in signature && "s" in signature)
|
122
|
-
return serializeSignature(signature);
|
123
|
-
if (signature instanceof Uint8Array) return fromBytes(signature, "hex");
|
124
|
-
// We should never hit this but TS doesn't know that
|
125
|
-
throw new Error(
|
126
|
-
`Invalid signature type for signature ${signature}: ${typeof signature}`,
|
127
|
-
);
|
128
|
-
})();
|
129
|
-
|
130
|
-
const accountContract = getContract({
|
100
|
+
return verifyHash({
|
101
|
+
hash: messageHash,
|
102
|
+
signature,
|
131
103
|
address,
|
132
|
-
chain,
|
133
104
|
client,
|
134
|
-
});
|
135
|
-
|
136
|
-
const wrappedSignature = await (async () => {
|
137
|
-
// If this sigature was already wrapped for ERC-6492, carry on
|
138
|
-
if (isErc6492Signature(signatureHex)) return signatureHex;
|
139
|
-
|
140
|
-
// If the contract is already deployed, return the original signature
|
141
|
-
const { isContractDeployed } = await import(
|
142
|
-
"../utils/bytecode/is-contract-deployed.js"
|
143
|
-
);
|
144
|
-
const isDeployed = await isContractDeployed(accountContract);
|
145
|
-
if (!isDeployed) return signatureHex;
|
146
|
-
|
147
|
-
// Otherwise, serialize the signature for ERC-6492 validation
|
148
|
-
return serializeErc6492Signature({
|
149
|
-
address: accountFactory?.address ?? DEFAULT_ACCOUNT_FACTORY,
|
150
|
-
data: accountFactory?.verificationCalldata ?? "0x",
|
151
|
-
signature: signatureHex,
|
152
|
-
});
|
153
|
-
})();
|
154
|
-
|
155
|
-
const verificationData = encodeDeployData({
|
156
|
-
abi: universalSignatureValidatorAbi,
|
157
|
-
args: [address, messageHash, wrappedSignature],
|
158
|
-
bytecode: universalSignatureValidatorByteCode,
|
159
|
-
});
|
160
|
-
|
161
|
-
const rpcRequest = getRpcClient({
|
162
105
|
chain,
|
163
|
-
|
106
|
+
accountFactory,
|
164
107
|
});
|
165
|
-
|
166
|
-
try {
|
167
|
-
const result = await eth_call(rpcRequest, {
|
168
|
-
data: verificationData,
|
169
|
-
});
|
170
|
-
|
171
|
-
const hexResult = isHex(result) ? toBytes(result) : result;
|
172
|
-
return equalBytes(hexResult, toBytes("0x1"));
|
173
|
-
} catch (error) {
|
174
|
-
console.log("error", error);
|
175
|
-
// TODO: Improve overall RPC error handling so we can tell if this was an actual verification failure or some other error
|
176
|
-
// Verification failed somehow
|
177
|
-
return false;
|
178
|
-
}
|
179
108
|
}
|
180
109
|
|
181
110
|
export type VerifySignatureParams = Prettify<
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
2
|
+
import { TEST_CLIENT } from "../../test/src/test-clients.js";
|
3
|
+
import { ANVIL_PKEY_A, TEST_ACCOUNT_A } from "../../test/src/test-wallets.js";
|
4
|
+
import { typedData } from "../../test/src/typed-data.js";
|
5
|
+
import { mainnet } from "../chains/chain-definitions/ethereum.js";
|
6
|
+
import { signTypedData } from "../utils/signatures/sign-typed-data.js";
|
7
|
+
import { verifyTypedData } from "./verify-typed-data.js";
|
8
|
+
|
9
|
+
describe("verifyTypedData", async () => {
|
10
|
+
test("valid EOA signature", async () => {
|
11
|
+
const signature = signTypedData({
|
12
|
+
...typedData.basic,
|
13
|
+
primaryType: "Mail",
|
14
|
+
privateKey: ANVIL_PKEY_A,
|
15
|
+
});
|
16
|
+
|
17
|
+
expect(
|
18
|
+
await verifyTypedData({
|
19
|
+
...typedData.basic,
|
20
|
+
primaryType: "Mail",
|
21
|
+
address: TEST_ACCOUNT_A.address,
|
22
|
+
signature,
|
23
|
+
chain: mainnet,
|
24
|
+
client: TEST_CLIENT,
|
25
|
+
}),
|
26
|
+
).toBe(true);
|
27
|
+
});
|
28
|
+
|
29
|
+
test("invalid EOA signature", async () => {
|
30
|
+
expect(
|
31
|
+
await verifyTypedData({
|
32
|
+
...typedData.basic,
|
33
|
+
primaryType: "Mail",
|
34
|
+
address: TEST_ACCOUNT_A.address,
|
35
|
+
signature: "0xdead",
|
36
|
+
chain: mainnet,
|
37
|
+
client: TEST_CLIENT,
|
38
|
+
}),
|
39
|
+
).toBe(false);
|
40
|
+
});
|
41
|
+
|
42
|
+
test("valid smart account signature", async () => {
|
43
|
+
expect(
|
44
|
+
await verifyTypedData({
|
45
|
+
...typedData.basic,
|
46
|
+
primaryType: "Mail",
|
47
|
+
address: "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145",
|
48
|
+
signature:
|
49
|
+
"0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c",
|
50
|
+
chain: mainnet,
|
51
|
+
client: TEST_CLIENT,
|
52
|
+
}),
|
53
|
+
).toBe(true);
|
54
|
+
});
|
55
|
+
|
56
|
+
test("invalid smart account signature", async () => {
|
57
|
+
expect(
|
58
|
+
await verifyTypedData({
|
59
|
+
...typedData.basic,
|
60
|
+
primaryType: "Mail",
|
61
|
+
address: "0x3FCf42e10CC70Fe75A62EB3aDD6D305Aa840d145",
|
62
|
+
signature: "0xdead",
|
63
|
+
chain: mainnet,
|
64
|
+
client: TEST_CLIENT,
|
65
|
+
}),
|
66
|
+
).toBe(false);
|
67
|
+
});
|
68
|
+
|
69
|
+
test("non-existent account", async () => {
|
70
|
+
expect(
|
71
|
+
await verifyTypedData({
|
72
|
+
...typedData.basic,
|
73
|
+
primaryType: "Mail",
|
74
|
+
address: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
75
|
+
signature:
|
76
|
+
"0x79d756d805073dc97b7bc885b0d56ddf319a2599530fe1e178c2a7de5be88980068d24f20a79b318ea0a84d33ae06f93db77e4235e5d9eeb8b1d7a63922ada3e1c",
|
77
|
+
chain: mainnet,
|
78
|
+
client: TEST_CLIENT,
|
79
|
+
}),
|
80
|
+
).toBe(false);
|
81
|
+
});
|
82
|
+
});
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import type { Signature, TypedData, TypedDataDefinition } from "viem";
|
2
|
+
import { hashTypedData } from "viem";
|
3
|
+
import type { Chain } from "../chains/types.js";
|
4
|
+
import type { ThirdwebClient } from "../client/client.js";
|
5
|
+
import type { Hex } from "../utils/encoding/hex.js";
|
6
|
+
import type { HashTypedDataParams } from "../utils/hashing/hashTypedData.js";
|
7
|
+
import { type VerifyHashParams, verifyHash } from "./verify-hash.js";
|
8
|
+
|
9
|
+
export type VerifyTypedDataParams<
|
10
|
+
typedData extends TypedData | Record<string, unknown> = TypedData,
|
11
|
+
primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
|
12
|
+
> = Omit<VerifyHashParams, "hash"> &
|
13
|
+
TypedDataDefinition<typedData, primaryType> & {
|
14
|
+
address: string;
|
15
|
+
signature: string | Uint8Array | Signature;
|
16
|
+
client: ThirdwebClient;
|
17
|
+
chain: Chain;
|
18
|
+
accountFactory?: {
|
19
|
+
address: string;
|
20
|
+
verificationCalldata: Hex;
|
21
|
+
};
|
22
|
+
};
|
23
|
+
|
24
|
+
/**
|
25
|
+
* @description Verify am [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data signature. This function is interoperable with all wallet types (smart accounts or EOAs).
|
26
|
+
*
|
27
|
+
* @param {string} options.address The address that signed the typed data
|
28
|
+
* @param {string | Uint8Array | Signature} options.signature The signature that was signed
|
29
|
+
* @param {ThirdwebClient} options.client The Thirdweb client
|
30
|
+
* @param {Chain} options.chain The chain that the address is on. For an EOA, this can be any chain.
|
31
|
+
* @param {string} [options.accountFactory.address] The address of the account factory that created the account if using a smart account with a custom account factory
|
32
|
+
* @param {Hex} [options.accountFactory.verificationCalldata] The calldata that was used to create the account if using a smart account with a custom account factory
|
33
|
+
* @param {typeof VerifyTypedDataParams.message} options.message The EIP-712 message that was signed.
|
34
|
+
* @param {typeof VerifyTypedDataParams.domain} options.domain The EIP-712 domain that was signed.
|
35
|
+
* @param {typeof VerifyTypedDataParams.primaryType} options.primaryType The EIP-712 primary type that was signed.
|
36
|
+
* @param {typeof VerifyTypedDataParams.types} options.types The EIP-712 types that were signed.
|
37
|
+
*
|
38
|
+
* @returns {Promise<boolean>} A promise that resolves to `true` if the signature is valid, or `false` otherwise.
|
39
|
+
*
|
40
|
+
* @example
|
41
|
+
* ```ts
|
42
|
+
* import { verifyTypedData } from "thirdweb/utils";
|
43
|
+
* const isValid = await verifyTypedData({
|
44
|
+
* address: "0x...",
|
45
|
+
* signature: "0x...",
|
46
|
+
* client,
|
47
|
+
* chain,
|
48
|
+
* domain: {
|
49
|
+
name: "Ether Mail",
|
50
|
+
version: "1",
|
51
|
+
chainId: 1,
|
52
|
+
verifyingContract: "0x0000000000000000000000000000000000000000",
|
53
|
+
},
|
54
|
+
* primaryType: "Mail",
|
55
|
+
* types: {
|
56
|
+
Person: [
|
57
|
+
{ name: "name", type: "string" },
|
58
|
+
{ name: "wallet", type: "address" },
|
59
|
+
],
|
60
|
+
Mail: [
|
61
|
+
{ name: "from", type: "Person" },
|
62
|
+
{ name: "to", type: "Person" },
|
63
|
+
{ name: "contents", type: "string" },
|
64
|
+
],
|
65
|
+
},
|
66
|
+
message: {
|
67
|
+
from: {
|
68
|
+
name: "Cow",
|
69
|
+
wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
|
70
|
+
},
|
71
|
+
to: {
|
72
|
+
name: "Bob",
|
73
|
+
wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
|
74
|
+
},
|
75
|
+
contents: "Hello, Bob!",
|
76
|
+
},
|
77
|
+
* });
|
78
|
+
* ```
|
79
|
+
*
|
80
|
+
* @auth
|
81
|
+
*/
|
82
|
+
export async function verifyTypedData<
|
83
|
+
typedData extends TypedData | Record<string, unknown>,
|
84
|
+
primaryType extends keyof typedData | "EIP712Domain",
|
85
|
+
>({
|
86
|
+
address,
|
87
|
+
signature,
|
88
|
+
client,
|
89
|
+
chain,
|
90
|
+
accountFactory,
|
91
|
+
message,
|
92
|
+
domain,
|
93
|
+
primaryType,
|
94
|
+
types,
|
95
|
+
}: VerifyTypedDataParams<typedData, primaryType>): Promise<boolean> {
|
96
|
+
const messageHash = hashTypedData({
|
97
|
+
message,
|
98
|
+
domain,
|
99
|
+
primaryType,
|
100
|
+
types,
|
101
|
+
} as HashTypedDataParams);
|
102
|
+
return verifyHash({
|
103
|
+
hash: messageHash,
|
104
|
+
signature,
|
105
|
+
address,
|
106
|
+
chain,
|
107
|
+
client,
|
108
|
+
accountFactory,
|
109
|
+
});
|
110
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { defineChain } from "../utils.js";
|
2
|
+
|
3
|
+
export const blast = /* @__PURE__ */ defineChain({
|
4
|
+
id: 81457,
|
5
|
+
name: "Blast",
|
6
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
7
|
+
blockExplorers: [
|
8
|
+
{
|
9
|
+
name: "Blastscan",
|
10
|
+
url: "https://blastscan.io",
|
11
|
+
apiUrl: "https://api.blastscan.io/api",
|
12
|
+
},
|
13
|
+
],
|
14
|
+
});
|
package/src/exports/chains.ts
CHANGED
@@ -21,6 +21,7 @@ export { baseSepolia } from "../chains/chain-definitions/base-sepolia.js";
|
|
21
21
|
export { base } from "../chains/chain-definitions/base.js";
|
22
22
|
// mainnet = alias for ethereum
|
23
23
|
export { ethereum, mainnet } from "../chains/chain-definitions/ethereum.js";
|
24
|
+
export { blast } from "../chains/chain-definitions/blast.js";
|
24
25
|
export { optimismSepolia } from "../chains/chain-definitions/optimism-sepolia.js";
|
25
26
|
export { optimism } from "../chains/chain-definitions/optimism.js";
|
26
27
|
export { lineaSepolia } from "../chains/chain-definitions/linea-sepolia.js";
|
@@ -33,6 +33,10 @@ export {
|
|
33
33
|
transferFrom,
|
34
34
|
type TransferFromParams,
|
35
35
|
} from "../../extensions/erc20/write/transferFrom.js";
|
36
|
+
export {
|
37
|
+
transferBatch,
|
38
|
+
type TransferBatchParams,
|
39
|
+
} from "../../extensions/erc20/write/transferBatch.js";
|
36
40
|
export {
|
37
41
|
approve,
|
38
42
|
type ApproveParams,
|
package/src/exports/utils.ts
CHANGED
@@ -149,3 +149,11 @@ export { stringify } from "../utils/json.js";
|
|
149
149
|
// values
|
150
150
|
// ------------------------------------------------
|
151
151
|
export { maxUint256 } from "viem";
|
152
|
+
|
153
|
+
// ------------------------------------------------
|
154
|
+
// jwt
|
155
|
+
// ------------------------------------------------
|
156
|
+
export { decodeJWT } from "../utils/jwt/decode-jwt.js";
|
157
|
+
export { encodeJWT, type JWTPayloadInput } from "../utils/jwt/encode-jwt.js";
|
158
|
+
export { refreshJWT, type RefreshJWTParams } from "../utils/jwt/refresh-jwt.js";
|
159
|
+
export type { JWTPayload } from "../utils/jwt/types.js";
|
@@ -12,6 +12,7 @@ const MAGIC_VALUE = "0x1626ba7e";
|
|
12
12
|
|
13
13
|
/**
|
14
14
|
* Checks if a contract wallet signature is valid.
|
15
|
+
* @deprecated Use `verifySignature` instead
|
15
16
|
* @param options - The options for the checkContractWalletSignature function.
|
16
17
|
* @param options.contract - The contract to check the signature against.
|
17
18
|
* @param options.message - The message to check the signature against.
|
@@ -15,6 +15,7 @@ const MAGIC_VALUE = "0x1626ba7e";
|
|
15
15
|
|
16
16
|
/**
|
17
17
|
* Checks if a contract wallet signature is valid.
|
18
|
+
* @deprecated Use `verifyTypedData` instead
|
18
19
|
* @param options - The options for the checkContractWalletSignature function.
|
19
20
|
* @param options.contract - The contract to check the signature against.
|
20
21
|
* @param options.message - The message to check the signature against.
|
@@ -0,0 +1,78 @@
|
|
1
|
+
import type { BaseTransactionOptions } from "../../../transaction/types.js";
|
2
|
+
import type { Prettify } from "../../../utils/type-utils.js";
|
3
|
+
import { toUnits } from "../../../utils/units.js";
|
4
|
+
import { multicall } from "../../common/__generated__/IMulticall/write/multicall.js";
|
5
|
+
import { encodeTransfer } from "../__generated__/IERC20/write/transfer.js";
|
6
|
+
/**
|
7
|
+
* Represents the parameters for a batch transfer operation.
|
8
|
+
*/
|
9
|
+
export type TransferBatchParams = Prettify<{
|
10
|
+
batch: Array<
|
11
|
+
{ to: string } & (
|
12
|
+
| {
|
13
|
+
amount: number | string;
|
14
|
+
}
|
15
|
+
| {
|
16
|
+
amountWei: bigint;
|
17
|
+
}
|
18
|
+
)
|
19
|
+
>;
|
20
|
+
}>;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Transfers a batch of ERC20 tokens from the sender's address to the specified recipient address.
|
24
|
+
* @param options - The options for the batch transfer transaction.
|
25
|
+
* @returns A promise that resolves to the prepared transaction.
|
26
|
+
* @extension ERC20
|
27
|
+
* @example
|
28
|
+
* ```ts
|
29
|
+
* import { transferBatch } from "thirdweb/extensions/erc20";
|
30
|
+
* import { sendTransaction } from "thirdweb";
|
31
|
+
*
|
32
|
+
* const transaction = transferBatch({
|
33
|
+
* contract,
|
34
|
+
* batch: [
|
35
|
+
* {
|
36
|
+
* to: "0x...",
|
37
|
+
* amount: 100,
|
38
|
+
* },
|
39
|
+
* {
|
40
|
+
* to: "0x...",
|
41
|
+
* amount: "0.1",
|
42
|
+
* },
|
43
|
+
* ]);
|
44
|
+
*
|
45
|
+
* await sendTransaction({ transaction, account });
|
46
|
+
* ```
|
47
|
+
*/
|
48
|
+
export function transferBatch(
|
49
|
+
options: BaseTransactionOptions<TransferBatchParams>,
|
50
|
+
) {
|
51
|
+
return multicall({
|
52
|
+
contract: options.contract,
|
53
|
+
asyncParams: async () => {
|
54
|
+
return {
|
55
|
+
data: await Promise.all(
|
56
|
+
options.batch.map(async (transfer) => {
|
57
|
+
let amount: bigint;
|
58
|
+
if ("amount" in transfer) {
|
59
|
+
// if we need to parse the amount from ether to gwei then we pull in the decimals extension
|
60
|
+
const { decimals } = await import("../read/decimals.js");
|
61
|
+
// it's OK to call this multiple times because the call is cached
|
62
|
+
// if this fails we fall back to `18` decimals
|
63
|
+
const d = await decimals(options).catch(() => 18);
|
64
|
+
// turn ether into gwei
|
65
|
+
amount = toUnits(transfer.amount.toString(), d);
|
66
|
+
} else {
|
67
|
+
amount = transfer.amountWei;
|
68
|
+
}
|
69
|
+
return encodeTransfer({
|
70
|
+
to: transfer.to,
|
71
|
+
value: amount,
|
72
|
+
});
|
73
|
+
}),
|
74
|
+
),
|
75
|
+
};
|
76
|
+
},
|
77
|
+
});
|
78
|
+
}
|
@@ -141,6 +141,8 @@ function BuyScreenContent(props: BuyScreenContentProps) {
|
|
141
141
|
id: "main",
|
142
142
|
});
|
143
143
|
|
144
|
+
const [hasEditedAmount, setHasEditedAmount] = useState(false);
|
145
|
+
|
144
146
|
// UI selection
|
145
147
|
const {
|
146
148
|
tokenAmount,
|
@@ -408,6 +410,8 @@ function BuyScreenContent(props: BuyScreenContentProps) {
|
|
408
410
|
supportedDestinations={supportedDestinations}
|
409
411
|
onBack={props.onBack}
|
410
412
|
theme={props.theme}
|
413
|
+
hasEditedAmount={hasEditedAmount}
|
414
|
+
setHasEditedAmount={setHasEditedAmount}
|
411
415
|
/>
|
412
416
|
)}
|
413
417
|
|
@@ -486,6 +490,7 @@ function BuyScreenContent(props: BuyScreenContentProps) {
|
|
486
490
|
}}
|
487
491
|
payer={payer}
|
488
492
|
setTokenAmount={setTokenAmount}
|
493
|
+
setHasEditedAmount={setHasEditedAmount}
|
489
494
|
/>
|
490
495
|
)}
|
491
496
|
</TokenSelectedLayout>
|
@@ -512,8 +517,8 @@ function SelectedTokenInfo(props: {
|
|
512
517
|
}}
|
513
518
|
>
|
514
519
|
<Container flex="row" gap="xs" center="y">
|
515
|
-
<Text color="primaryText" data-testid="tokenAmount" size="
|
516
|
-
{formatNumber(Number(props.tokenAmount),
|
520
|
+
<Text color="primaryText" data-testid="tokenAmount" size="xl">
|
521
|
+
{formatNumber(Number(props.tokenAmount), 6)}
|
517
522
|
</Text>
|
518
523
|
|
519
524
|
<Container flex="row" gap="xxs" center="y">
|
@@ -559,6 +564,8 @@ function MainScreen(props: {
|
|
559
564
|
supportedDestinations: SupportedChainAndTokens;
|
560
565
|
onBack: (() => void) | undefined;
|
561
566
|
theme: "light" | "dark" | Theme;
|
567
|
+
hasEditedAmount: boolean;
|
568
|
+
setHasEditedAmount: (hasEdited: boolean) => void;
|
562
569
|
}) {
|
563
570
|
const { showPaymentSelection, buyWithCryptoEnabled, buyWithFiatEnabled } =
|
564
571
|
useEnabledPaymentMethods({
|
@@ -568,7 +575,6 @@ function MainScreen(props: {
|
|
568
575
|
toToken: props.toToken,
|
569
576
|
});
|
570
577
|
|
571
|
-
const [hasEditedAmount, setHasEditedAmount] = useState(false);
|
572
578
|
const {
|
573
579
|
buyForTx,
|
574
580
|
setTokenAmount,
|
@@ -584,7 +590,7 @@ function MainScreen(props: {
|
|
584
590
|
const { amountNeeded } = useBuyTxStates({
|
585
591
|
setTokenAmount,
|
586
592
|
buyForTx: buyForTx || null,
|
587
|
-
hasEditedAmount,
|
593
|
+
hasEditedAmount: props.hasEditedAmount,
|
588
594
|
account: payerAccount || null,
|
589
595
|
});
|
590
596
|
|
@@ -605,7 +611,7 @@ function MainScreen(props: {
|
|
605
611
|
<Spacer y="lg" />
|
606
612
|
<BuyForTxUI
|
607
613
|
amountNeeded={String(
|
608
|
-
formatNumber(Number(toEther(amountNeeded)),
|
614
|
+
formatNumber(Number(toEther(amountNeeded)), 6),
|
609
615
|
)}
|
610
616
|
buyForTx={props.buyForTx}
|
611
617
|
client={client}
|
@@ -619,7 +625,7 @@ function MainScreen(props: {
|
|
619
625
|
<BuyTokenInput
|
620
626
|
value={tokenAmount}
|
621
627
|
onChange={async (value) => {
|
622
|
-
setHasEditedAmount(true);
|
628
|
+
props.setHasEditedAmount(true);
|
623
629
|
setTokenAmount(value);
|
624
630
|
}}
|
625
631
|
freezeAmount={payOptions.prefillBuy?.allowEdits?.amount === false}
|
@@ -1094,6 +1100,7 @@ function FiatScreenContent(props: {
|
|
1094
1100
|
isEmbed: boolean;
|
1095
1101
|
payer: PayerInfo;
|
1096
1102
|
setTokenAmount: (amount: string) => void;
|
1103
|
+
setHasEditedAmount: (hasEdited: boolean) => void;
|
1097
1104
|
}) {
|
1098
1105
|
const [receiverAddress, setReceiverAddress] = useState(
|
1099
1106
|
props.payer.account.address,
|
@@ -1182,18 +1189,27 @@ function FiatScreenContent(props: {
|
|
1182
1189
|
try {
|
1183
1190
|
if (err.error.code === "MINIMUM_PURCHASE_AMOUNT") {
|
1184
1191
|
const obj = err.error as AmountTooLowError;
|
1185
|
-
|
1186
1192
|
const minAmountUSD = obj.data.minimumAmountUSDCents;
|
1187
1193
|
const currentAmountUSD = obj.data.requestedAmountUSDCents;
|
1194
|
+
|
1195
|
+
// avoid divide by zero
|
1196
|
+
// if we can't calculate the minimum amount in # of tokens, don't show the button to set the minimum amount
|
1197
|
+
if (obj.data.requestedAmountUSDCents === 0) {
|
1198
|
+
return {
|
1199
|
+
msg: [
|
1200
|
+
"Purchase amount is too low",
|
1201
|
+
"Increase the amount and try again",
|
1202
|
+
],
|
1203
|
+
};
|
1204
|
+
}
|
1205
|
+
|
1188
1206
|
const currentAmountToken = Number(props.tokenAmount);
|
1189
1207
|
const minAmountToken =
|
1190
1208
|
(minAmountUSD * currentAmountToken) / currentAmountUSD;
|
1191
1209
|
const minAmountTokenWithBuffer = minAmountToken * 1.2; // 20% buffer
|
1192
|
-
const formattedNum = formatNumber(minAmountTokenWithBuffer, 3);
|
1193
1210
|
|
1194
1211
|
return {
|
1195
|
-
|
1196
|
-
minAmount: formattedNum,
|
1212
|
+
minAmount: minAmountTokenWithBuffer,
|
1197
1213
|
};
|
1198
1214
|
}
|
1199
1215
|
} catch {}
|
@@ -1279,7 +1295,20 @@ function FiatScreenContent(props: {
|
|
1279
1295
|
{/* Error message */}
|
1280
1296
|
{errorMsg && (
|
1281
1297
|
<div>
|
1282
|
-
{errorMsg.
|
1298
|
+
{errorMsg.minAmount && (
|
1299
|
+
<Text color="danger" size="sm" center multiline>
|
1300
|
+
Minimum amount is {formatNumber(errorMsg.minAmount, 6)}{" "}
|
1301
|
+
<TokenSymbol
|
1302
|
+
token={toToken}
|
1303
|
+
chain={toChain}
|
1304
|
+
size="sm"
|
1305
|
+
inline
|
1306
|
+
color="danger"
|
1307
|
+
/>
|
1308
|
+
</Text>
|
1309
|
+
)}
|
1310
|
+
|
1311
|
+
{errorMsg.msg?.map((msg) => (
|
1283
1312
|
<Text color="danger" size="sm" center multiline key={msg}>
|
1284
1313
|
{msg}
|
1285
1314
|
</Text>
|
@@ -1293,6 +1322,7 @@ function FiatScreenContent(props: {
|
|
1293
1322
|
fullWidth
|
1294
1323
|
onClick={() => {
|
1295
1324
|
props.setTokenAmount(String(errorMsg.minAmount));
|
1325
|
+
props.setHasEditedAmount(true);
|
1296
1326
|
}}
|
1297
1327
|
>
|
1298
1328
|
Set Minimum
|
@@ -1376,7 +1406,7 @@ function BuyForTxUI(props: {
|
|
1376
1406
|
<Text size="sm">Your Balance</Text>
|
1377
1407
|
<Container flex="row" gap="xs">
|
1378
1408
|
<Text color="primaryText" size="sm">
|
1379
|
-
{formatNumber(Number(toEther(props.buyForTx.balance)),
|
1409
|
+
{formatNumber(Number(toEther(props.buyForTx.balance)), 6)}{" "}
|
1380
1410
|
{props.buyForTx.tokenSymbol}
|
1381
1411
|
</Text>
|
1382
1412
|
<TokenIcon
|
@@ -82,7 +82,7 @@ export function PayWithCreditCard(props: {
|
|
82
82
|
<Skeleton width="100px" height={fontSize.lg} />
|
83
83
|
) : (
|
84
84
|
<Text size="lg" color={props.value ? "primaryText" : "secondaryText"}>
|
85
|
-
{props.value ? `${formatNumber(Number(props.value),
|
85
|
+
{props.value ? `${formatNumber(Number(props.value), 6)}` : "--"}
|
86
86
|
</Text>
|
87
87
|
)}
|
88
88
|
</div>
|
@@ -163,7 +163,7 @@ export function FiatSteps(props: {
|
|
163
163
|
const onRampTokenInfo = (
|
164
164
|
<div>
|
165
165
|
<Text color="primaryText" size="sm">
|
166
|
-
{formatNumber(Number(onRampTokenAmount),
|
166
|
+
{formatNumber(Number(onRampTokenAmount), 6)}{" "}
|
167
167
|
<TokenSymbol token={onRampToken} chain={onRampChain} size="sm" inline />
|
168
168
|
</Text>
|
169
169
|
</div>
|
@@ -203,7 +203,7 @@ export function FiatSteps(props: {
|
|
203
203
|
textDecoration: "line-through",
|
204
204
|
}}
|
205
205
|
>
|
206
|
-
{formatNumber(Number(toTokenAmount),
|
206
|
+
{formatNumber(Number(toTokenAmount), 6)}{" "}
|
207
207
|
<TokenSymbol
|
208
208
|
token={toToken}
|
209
209
|
chain={toChain}
|
@@ -213,7 +213,7 @@ export function FiatSteps(props: {
|
|
213
213
|
/>
|
214
214
|
</Text>{" "}
|
215
215
|
<Text color="danger" size="sm" inline>
|
216
|
-
{formatNumber(Number(props.status.destination.amount),
|
216
|
+
{formatNumber(Number(props.status.destination.amount), 6)}{" "}
|
217
217
|
<TokenSymbol
|
218
218
|
token={{
|
219
219
|
address: props.status.destination.token.tokenAddress,
|
@@ -231,7 +231,7 @@ export function FiatSteps(props: {
|
|
231
231
|
|
232
232
|
const toTokenInfo = partialSuccessToTokenInfo || (
|
233
233
|
<Text color="primaryText" size="sm">
|
234
|
-
{formatNumber(Number(toTokenAmount),
|
234
|
+
{formatNumber(Number(toTokenAmount), 6)}{" "}
|
235
235
|
<TokenSymbol token={toToken} chain={toChain} size="sm" inline />
|
236
236
|
</Text>
|
237
237
|
);
|
@@ -330,7 +330,7 @@ export function FiatSteps(props: {
|
|
330
330
|
icon: fiatIcon,
|
331
331
|
primaryText: (
|
332
332
|
<Text color="primaryText" size="sm">
|
333
|
-
{formatNumber(Number(fromCurrencyAmount),
|
333
|
+
{formatNumber(Number(fromCurrencyAmount), 6)} {fromCurrencySymbol}
|
334
334
|
</Text>
|
335
335
|
),
|
336
336
|
}}
|
@@ -75,7 +75,7 @@ export function OnRampTxDetailsTable(props: {
|
|
75
75
|
<Container flex="row" gap="xs" center="y">
|
76
76
|
<currencyMeta.icon size={iconSize.sm} />
|
77
77
|
<Text color="primaryText">
|
78
|
-
{formatNumber(Number(props.fiat.amount),
|
78
|
+
{formatNumber(Number(props.fiat.amount), 2)}{" "}
|
79
79
|
{props.fiat.currencySymbol}
|
80
80
|
</Text>
|
81
81
|
</Container>
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import { useEffect, useState } from "react";
|
2
|
-
import { formatNumber } from "../../../../../../../utils/formatNumber.js";
|
3
2
|
import { toEther } from "../../../../../../../utils/units.js";
|
4
3
|
import type { Account } from "../../../../../../../wallets/interfaces/wallet.js";
|
5
4
|
import { getTotalTxCostForBuy } from "../../../../../../core/hooks/transaction/useSendTransaction.js";
|
@@ -48,10 +47,7 @@ export function useBuyTxStates(options: {
|
|
48
47
|
|
49
48
|
if (shouldRefreshTokenAmount) {
|
50
49
|
if (totalCost > buyForTx.balance) {
|
51
|
-
|
52
|
-
formatNumber(Number(toEther(totalCost - buyForTx.balance)), 4),
|
53
|
-
);
|
54
|
-
setTokenAmount(_tokenAmount);
|
50
|
+
setTokenAmount(toEther(totalCost - buyForTx.balance));
|
55
51
|
}
|
56
52
|
}
|
57
53
|
} catch {
|