thirdweb 5.65.0 → 5.65.2
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/chains/utils.js +14 -6
- package/dist/cjs/chains/utils.js.map +1 -1
- package/dist/cjs/extensions/erc20/write/transferBatch.js +65 -18
- package/dist/cjs/extensions/erc20/write/transferBatch.js.map +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +1 -1
- package/dist/cjs/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
- package/dist/cjs/utils/any-evm/zksync/isZkSyncChain.js +13 -2
- package/dist/cjs/utils/any-evm/zksync/isZkSyncChain.js.map +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/wallets/smart/index.js +1 -1
- package/dist/cjs/wallets/smart/index.js.map +1 -1
- package/dist/esm/chains/utils.js +8 -2
- package/dist/esm/chains/utils.js.map +1 -1
- package/dist/esm/extensions/erc20/write/transferBatch.js +64 -18
- package/dist/esm/extensions/erc20/write/transferBatch.js.map +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js +1 -1
- package/dist/esm/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.js.map +1 -1
- package/dist/esm/utils/any-evm/zksync/isZkSyncChain.js +13 -2
- package/dist/esm/utils/any-evm/zksync/isZkSyncChain.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/wallets/smart/index.js +1 -1
- package/dist/esm/wallets/smart/index.js.map +1 -1
- package/dist/types/chains/utils.d.ts +10 -0
- package/dist/types/chains/utils.d.ts.map +1 -1
- package/dist/types/extensions/erc20/write/transferBatch.d.ts +31 -0
- package/dist/types/extensions/erc20/write/transferBatch.d.ts.map +1 -1
- package/dist/types/utils/any-evm/zksync/isZkSyncChain.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/chains/utils.test.ts +123 -0
- package/src/chains/utils.ts +8 -2
- package/src/extensions/erc20/write/transferBatch.test.ts +102 -10
- package/src/extensions/erc20/write/transferBatch.ts +79 -25
- package/src/react/web/ui/ConnectWallet/screens/Buy/BuyScreen.tsx +2 -1
- package/src/utils/any-evm/zksync/isZkSyncChain.ts +12 -2
- package/src/version.ts +1 -1
- package/src/wallets/smart/index.ts +1 -1
- package/src/wallets/utils/getWalletBalance.test.ts +2 -4
package/src/chains/utils.ts
CHANGED
@@ -10,7 +10,10 @@ import type {
|
|
10
10
|
LegacyChain,
|
11
11
|
} from "./types.js";
|
12
12
|
|
13
|
-
|
13
|
+
/**
|
14
|
+
* @internal Exported for tests
|
15
|
+
*/
|
16
|
+
export const CUSTOM_CHAIN_MAP = new Map<number, Chain>();
|
14
17
|
|
15
18
|
/**
|
16
19
|
* Defines a chain with the given options.
|
@@ -98,7 +101,10 @@ function isLegacyChain(
|
|
98
101
|
return "rpc" in chain && Array.isArray(chain.rpc) && "slug" in chain;
|
99
102
|
}
|
100
103
|
|
101
|
-
|
104
|
+
/**
|
105
|
+
* @internal
|
106
|
+
*/
|
107
|
+
export function convertLegacyChain(legacyChain: LegacyChain): Chain {
|
102
108
|
const RPC_URL = getThirdwebDomains().rpc;
|
103
109
|
return {
|
104
110
|
id: legacyChain.chainId,
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
1
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
2
2
|
import { ANVIL_CHAIN } from "~test/chains.js";
|
3
3
|
import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
|
4
4
|
import { TEST_CLIENT } from "~test/test-clients.js";
|
@@ -8,19 +8,23 @@ import {
|
|
8
8
|
TEST_ACCOUNT_C,
|
9
9
|
TEST_ACCOUNT_D,
|
10
10
|
} from "~test/test-wallets.js";
|
11
|
-
import {
|
11
|
+
import {
|
12
|
+
type ThirdwebContract,
|
13
|
+
getContract,
|
14
|
+
} from "../../../contract/contract.js";
|
12
15
|
import { deployERC20Contract } from "../../../extensions/prebuilts/deploy-erc20.js";
|
13
16
|
import { sendAndConfirmTransaction } from "../../../transaction/actions/send-and-confirm-transaction.js";
|
14
17
|
import { balanceOf } from "../__generated__/IERC20/read/balanceOf.js";
|
15
18
|
import { mintTo } from "./mintTo.js";
|
16
|
-
import { transferBatch } from "./transferBatch.js";
|
19
|
+
import { optimizeTransferContent, transferBatch } from "./transferBatch.js";
|
17
20
|
|
18
21
|
const chain = ANVIL_CHAIN;
|
19
22
|
const client = TEST_CLIENT;
|
20
23
|
const account = TEST_ACCOUNT_A;
|
24
|
+
let contract: ThirdwebContract;
|
21
25
|
|
22
26
|
describe.runIf(process.env.TW_SECRET_KEY)("erc20: transferBatch", () => {
|
23
|
-
|
27
|
+
beforeAll(async () => {
|
24
28
|
const address = await deployERC20Contract({
|
25
29
|
type: "TokenERC20",
|
26
30
|
account,
|
@@ -31,15 +35,16 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc20: transferBatch", () => {
|
|
31
35
|
contractURI: TEST_CONTRACT_URI,
|
32
36
|
},
|
33
37
|
});
|
34
|
-
|
38
|
+
contract = getContract({
|
35
39
|
address,
|
36
40
|
chain,
|
37
41
|
client,
|
38
42
|
});
|
39
|
-
|
40
|
-
|
43
|
+
}, 60_000_000);
|
44
|
+
it("should transfer tokens to multiple recipients", async () => {
|
45
|
+
// Mint 200 tokens
|
41
46
|
await sendAndConfirmTransaction({
|
42
|
-
transaction: mintTo({ contract, to: account.address, amount:
|
47
|
+
transaction: mintTo({ contract, to: account.address, amount: 200 }),
|
43
48
|
account,
|
44
49
|
});
|
45
50
|
|
@@ -61,6 +66,14 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc20: transferBatch", () => {
|
|
61
66
|
to: TEST_ACCOUNT_D.address,
|
62
67
|
amount: 25,
|
63
68
|
},
|
69
|
+
{
|
70
|
+
to: TEST_ACCOUNT_B.address.toLowerCase(),
|
71
|
+
amount: 25,
|
72
|
+
},
|
73
|
+
{
|
74
|
+
to: TEST_ACCOUNT_B.address,
|
75
|
+
amountWei: 25n * 10n ** 18n,
|
76
|
+
},
|
64
77
|
],
|
65
78
|
}),
|
66
79
|
});
|
@@ -73,9 +86,88 @@ describe.runIf(process.env.TW_SECRET_KEY)("erc20: transferBatch", () => {
|
|
73
86
|
balanceOf({ contract, address: TEST_ACCOUNT_D.address }),
|
74
87
|
]);
|
75
88
|
|
76
|
-
expect(balanceA).toBe(
|
77
|
-
expect(balanceB).toBe(
|
89
|
+
expect(balanceA).toBe(75n * 10n ** 18n);
|
90
|
+
expect(balanceB).toBe(75n * 10n ** 18n);
|
78
91
|
expect(balanceC).toBe(25n * 10n ** 18n);
|
79
92
|
expect(balanceD).toBe(25n * 10n ** 18n);
|
80
93
|
});
|
94
|
+
|
95
|
+
it("should optimize the transfer content", async () => {
|
96
|
+
const content = await optimizeTransferContent({
|
97
|
+
contract,
|
98
|
+
batch: [
|
99
|
+
{
|
100
|
+
to: TEST_ACCOUNT_B.address,
|
101
|
+
amount: 25,
|
102
|
+
},
|
103
|
+
{
|
104
|
+
to: TEST_ACCOUNT_C.address,
|
105
|
+
amount: 25,
|
106
|
+
},
|
107
|
+
{
|
108
|
+
to: TEST_ACCOUNT_D.address,
|
109
|
+
amount: 25,
|
110
|
+
},
|
111
|
+
{
|
112
|
+
// Should work
|
113
|
+
to: TEST_ACCOUNT_B.address.toLowerCase(),
|
114
|
+
amount: 25,
|
115
|
+
},
|
116
|
+
{
|
117
|
+
to: TEST_ACCOUNT_B.address,
|
118
|
+
amountWei: 25n * 10n ** 18n,
|
119
|
+
},
|
120
|
+
],
|
121
|
+
});
|
122
|
+
|
123
|
+
expect(content).toStrictEqual([
|
124
|
+
{
|
125
|
+
to: TEST_ACCOUNT_B.address,
|
126
|
+
amountWei: 75n * 10n ** 18n,
|
127
|
+
},
|
128
|
+
{
|
129
|
+
to: TEST_ACCOUNT_C.address,
|
130
|
+
amountWei: 25n * 10n ** 18n,
|
131
|
+
},
|
132
|
+
{
|
133
|
+
to: TEST_ACCOUNT_D.address,
|
134
|
+
amountWei: 25n * 10n ** 18n,
|
135
|
+
},
|
136
|
+
]);
|
137
|
+
});
|
138
|
+
|
139
|
+
it("an already-optimized content should not be changed", async () => {
|
140
|
+
const content = await optimizeTransferContent({
|
141
|
+
contract,
|
142
|
+
batch: [
|
143
|
+
{
|
144
|
+
to: TEST_ACCOUNT_B.address,
|
145
|
+
amountWei: 25n * 10n ** 18n,
|
146
|
+
},
|
147
|
+
{
|
148
|
+
to: TEST_ACCOUNT_C.address,
|
149
|
+
amount: 25,
|
150
|
+
},
|
151
|
+
{
|
152
|
+
to: TEST_ACCOUNT_D.address,
|
153
|
+
amount: 25,
|
154
|
+
},
|
155
|
+
],
|
156
|
+
});
|
157
|
+
|
158
|
+
expect(content).toStrictEqual([
|
159
|
+
{
|
160
|
+
to: TEST_ACCOUNT_B.address,
|
161
|
+
amountWei: 25n * 10n ** 18n,
|
162
|
+
},
|
163
|
+
{
|
164
|
+
to: TEST_ACCOUNT_C.address,
|
165
|
+
amountWei: 25n * 10n ** 18n,
|
166
|
+
},
|
167
|
+
{
|
168
|
+
to: TEST_ACCOUNT_D.address,
|
169
|
+
amountWei: 25n * 10n ** 18n,
|
170
|
+
},
|
171
|
+
]);
|
172
|
+
});
|
81
173
|
});
|
@@ -53,34 +53,88 @@ export function transferBatch(
|
|
53
53
|
return multicall({
|
54
54
|
contract: options.contract,
|
55
55
|
asyncParams: async () => {
|
56
|
+
const content = await optimizeTransferContent(options);
|
56
57
|
return {
|
57
|
-
data:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
const d = await decimals(options).catch(() => 18);
|
66
|
-
// turn ether into gwei
|
67
|
-
amount = toUnits(transfer.amount.toString(), d);
|
68
|
-
} else {
|
69
|
-
amount = transfer.amountWei;
|
70
|
-
}
|
71
|
-
return encodeTransfer({
|
72
|
-
to: transfer.to,
|
73
|
-
value: amount,
|
74
|
-
overrides: {
|
75
|
-
erc20Value: {
|
76
|
-
amountWei: amount,
|
77
|
-
tokenAddress: options.contract.address,
|
78
|
-
},
|
58
|
+
data: content.map((item) => {
|
59
|
+
return encodeTransfer({
|
60
|
+
to: item.to,
|
61
|
+
value: item.amountWei,
|
62
|
+
overrides: {
|
63
|
+
erc20Value: {
|
64
|
+
amountWei: item.amountWei,
|
65
|
+
tokenAddress: options.contract.address,
|
79
66
|
},
|
80
|
-
}
|
81
|
-
})
|
82
|
-
),
|
67
|
+
},
|
68
|
+
});
|
69
|
+
}),
|
83
70
|
};
|
84
71
|
},
|
85
72
|
});
|
86
73
|
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Records with the same recipient (`to`) can be packed into one transaction
|
77
|
+
* For example, the data below:
|
78
|
+
* ```ts
|
79
|
+
* [
|
80
|
+
* {
|
81
|
+
* to: "wallet-a",
|
82
|
+
* amount: 1,
|
83
|
+
* },
|
84
|
+
* {
|
85
|
+
* to: "wallet-A",
|
86
|
+
* amountWei: 1000000000000000000n,
|
87
|
+
* },
|
88
|
+
* ]
|
89
|
+
* ```
|
90
|
+
*
|
91
|
+
* can be packed to:
|
92
|
+
* ```ts
|
93
|
+
* [
|
94
|
+
* {
|
95
|
+
* to: "wallet-a",
|
96
|
+
* amountWei: 2000000000000000000n,
|
97
|
+
* },
|
98
|
+
* ]
|
99
|
+
* ```
|
100
|
+
* @internal
|
101
|
+
*/
|
102
|
+
export async function optimizeTransferContent(
|
103
|
+
options: BaseTransactionOptions<TransferBatchParams>,
|
104
|
+
): Promise<Array<{ to: string; amountWei: bigint }>> {
|
105
|
+
const groupedRecords = await options.batch.reduce(
|
106
|
+
async (accPromise, record) => {
|
107
|
+
const acc = await accPromise;
|
108
|
+
let amountInWei: bigint;
|
109
|
+
if ("amount" in record) {
|
110
|
+
// it's OK to call this multiple times because the call is cached
|
111
|
+
const { decimals } = await import("../read/decimals.js");
|
112
|
+
// if this fails we fall back to `18` decimals
|
113
|
+
const d = await decimals(options).catch(() => undefined);
|
114
|
+
if (d === undefined) {
|
115
|
+
throw new Error(
|
116
|
+
`Failed to get the decimals for contract: ${options.contract.address}`,
|
117
|
+
);
|
118
|
+
}
|
119
|
+
amountInWei = toUnits(record.amount.toString(), d);
|
120
|
+
} else {
|
121
|
+
amountInWei = record.amountWei;
|
122
|
+
}
|
123
|
+
const existingRecord = acc.find(
|
124
|
+
(r) => r.to.toLowerCase() === record.to.toLowerCase(),
|
125
|
+
);
|
126
|
+
if (existingRecord) {
|
127
|
+
existingRecord.amountWei = existingRecord.amountWei + amountInWei;
|
128
|
+
} else {
|
129
|
+
acc.push({
|
130
|
+
to: record.to,
|
131
|
+
amountWei: amountInWei,
|
132
|
+
});
|
133
|
+
}
|
134
|
+
|
135
|
+
return acc;
|
136
|
+
},
|
137
|
+
Promise.resolve([] as { to: string; amountWei: bigint }[]),
|
138
|
+
);
|
139
|
+
return groupedRecords;
|
140
|
+
}
|
@@ -984,7 +984,8 @@ function SwapScreenContent(props: {
|
|
984
984
|
|
985
985
|
const disableContinue =
|
986
986
|
(swapRequired && !quoteQuery.data) || isNotEnoughBalance;
|
987
|
-
const switchChainRequired =
|
987
|
+
const switchChainRequired =
|
988
|
+
props.payer.wallet.getChain()?.id !== fromChain.id;
|
988
989
|
|
989
990
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
990
991
|
function getErrorMessage(err: any) {
|
@@ -16,10 +16,20 @@ export async function isZkSyncChain(chain: Chain) {
|
|
16
16
|
chain.id === 4654 ||
|
17
17
|
chain.id === 333271 ||
|
18
18
|
chain.id === 37111 ||
|
19
|
-
chain.id === 978658
|
19
|
+
chain.id === 978658 ||
|
20
|
+
chain.id === 531050104 ||
|
21
|
+
chain.id === 4457845
|
20
22
|
) {
|
21
23
|
return true;
|
22
24
|
}
|
23
25
|
|
24
|
-
|
26
|
+
// fallback to checking the stack on rpc
|
27
|
+
try {
|
28
|
+
const { getChainMetadata } = await import("../../../chains/utils.js");
|
29
|
+
const chainMetadata = await getChainMetadata(chain);
|
30
|
+
return chainMetadata.stackType === "zksync_stack";
|
31
|
+
} catch {
|
32
|
+
// If the network check fails, assume it's not a ZkSync chain
|
33
|
+
return false;
|
34
|
+
}
|
25
35
|
}
|
package/src/version.ts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
export const version = "5.65.
|
1
|
+
export const version = "5.65.2";
|
@@ -3,7 +3,6 @@ import { ANVIL_CHAIN } from "~test/chains.js";
|
|
3
3
|
import { TEST_CONTRACT_URI } from "~test/ipfs-uris.js";
|
4
4
|
import { TEST_CLIENT } from "~test/test-clients.js";
|
5
5
|
import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
|
6
|
-
import { defineChain } from "../../chains/utils.js";
|
7
6
|
import { getContract } from "../../contract/contract.js";
|
8
7
|
import { mintTo } from "../../extensions/erc20/write/mintTo.js";
|
9
8
|
import { deployERC20Contract } from "../../extensions/prebuilts/deploy-erc20.js";
|
@@ -57,12 +56,11 @@ describe.runIf(process.env.TW_SECRET_KEY)("getWalletBalance", () => {
|
|
57
56
|
expect(result.displayValue).toBe(amount.toString());
|
58
57
|
});
|
59
58
|
|
60
|
-
it("should work for
|
59
|
+
it("should work for native currency", async () => {
|
61
60
|
const result = await getWalletBalance({
|
62
61
|
address: TEST_ACCOUNT_A.address,
|
63
62
|
client: TEST_CLIENT,
|
64
|
-
chain:
|
65
|
-
tokenAddress: "0xd66c6B4F0be8CE5b39D52E0Fd1344c389929B378",
|
63
|
+
chain: ANVIL_CHAIN,
|
66
64
|
});
|
67
65
|
expect(result).toBeDefined();
|
68
66
|
expect(result.decimals).toBe(18);
|