From 393d0cfb504401d6449a75cbe8422946d157fc93 Mon Sep 17 00:00:00 2001
From: MananTank
Date: Sun, 1 Dec 2024 18:15:48 +0000
Subject: [PATCH 01/15] Fix Portal > Contracts overview page links (#5541)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes: DASH-487
---
## PR-Codex overview
This PR focuses on updating references to smart contract interaction links throughout the application, simplifying the URLs to point directly to `/contracts` instead of specific interaction overview pages.
### Detailed summary
- Updated `href` in `page.tsx` from `/contracts/interact/overview` to `/contracts`.
- Changed references in `interact-blockchain/page.mdx` to point to `/contracts`.
- Modified links in `contracts/page.mdx` to `/contracts`.
- Adjusted links in `sdk.tsx` to `https://portal.thirdweb.com/contracts`.
- Updated SDK documentation links in `ipfs/page.mdx` to `/contracts`.
- Changed SDK references in `gas/page.mdx` to `/contracts`.
- Updated redirects in `redirects.mjs` for `/sdk`, `/sdk/how-it-works`, and `/sdk/getting-started` to `/contracts`.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
---
apps/dashboard/src/pages/sdk.tsx | 2 +-
apps/portal/redirects.mjs | 7 ++++---
.../in-app-wallet/guides/interact-blockchain/page.mdx | 2 +-
.../explore/pre-built-contracts/nft-drop/page.mdx | 2 +-
apps/portal/src/app/contracts/page.mdx | 2 +-
apps/portal/src/app/glossary/gas/page.mdx | 2 +-
apps/portal/src/app/glossary/ipfs/page.mdx | 2 +-
apps/portal/src/app/page.tsx | 2 +-
.../v5/in-app-wallet/how-to/interact-blockchain/page.mdx | 2 +-
9 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/apps/dashboard/src/pages/sdk.tsx b/apps/dashboard/src/pages/sdk.tsx
index 8f83faa38a0..49ef8e84130 100644
--- a/apps/dashboard/src/pages/sdk.tsx
+++ b/apps/dashboard/src/pages/sdk.tsx
@@ -61,7 +61,7 @@ const Web3SDK: ThirdwebNextPage = () => {
title="Powerful SDKs for every stack"
description="Build web3 applications that can interact with your smart contracts using our powerful SDKs and CLI."
buttonText="Get started"
- buttonLink="https://portal.thirdweb.com/contracts/interact/overview"
+ buttonLink="https://portal.thirdweb.com/contracts"
image={require("../../public/assets/product-pages/sdk/hero.png")}
gradient="linear-gradient(147.15deg, #410AB6 30.17%, #5BFF40 100.01%)"
>
diff --git a/apps/portal/redirects.mjs b/apps/portal/redirects.mjs
index 78b98454c77..35cfce9d263 100644
--- a/apps/portal/redirects.mjs
+++ b/apps/portal/redirects.mjs
@@ -798,9 +798,9 @@ const contractRedirects = {
"/publish/getting-started": "/contracts/publish/publish-contract",
"/publish/deployment-options": "/contracts/publish/publish-options",
"/publish/get-featured-on-explore": "/contracts/publish/overview",
- "/sdk": "/contracts/interact/overview",
- "/sdk/how-it-works": "/contracts/interact/overview",
- "/sdk/getting-started": "/contracts/interact/overview",
+ "/sdk": "/contracts",
+ "/sdk/how-it-works": "/contracts",
+ "/sdk/getting-started": "/contracts",
//design documentation
"/contracts/design/Drop": "/contracts/design-docs/drop",
"/contracts/design/Marketplace": "/contracts/design-docs/marketplace",
@@ -808,6 +808,7 @@ const contractRedirects = {
"/contracts/design/Multiwrap": "/contracts/design-docs/multiwrap",
"/contracts/design/Pack": "/contracts/design-docs/pack",
"/contracts/design/SignatureDrop": "/contracts",
+ "/interact": "/contracts",
};
const infrastructureRedirects = {
diff --git a/apps/portal/src/app/connect/in-app-wallet/guides/interact-blockchain/page.mdx b/apps/portal/src/app/connect/in-app-wallet/guides/interact-blockchain/page.mdx
index 888e978b658..86858e82637 100644
--- a/apps/portal/src/app/connect/in-app-wallet/guides/interact-blockchain/page.mdx
+++ b/apps/portal/src/app/connect/in-app-wallet/guides/interact-blockchain/page.mdx
@@ -22,7 +22,7 @@ export const metadata = createMetadata({
# Interact with the blockchain
-One connected, in-app wallets can be used alongside the [Contract SDK](/contracts/interact/overview) to interact with the blockchain.
+Once connected, in-app wallets can be used alongside the [Contract SDK](/contracts) to interact with the blockchain.
## Initialize the SDK With Your Wallet
diff --git a/apps/portal/src/app/contracts/explore/pre-built-contracts/nft-drop/page.mdx b/apps/portal/src/app/contracts/explore/pre-built-contracts/nft-drop/page.mdx
index 6339aa00017..076e1b363f5 100644
--- a/apps/portal/src/app/contracts/explore/pre-built-contracts/nft-drop/page.mdx
+++ b/apps/portal/src/app/contracts/explore/pre-built-contracts/nft-drop/page.mdx
@@ -198,4 +198,4 @@ const sdk = useSDK(
## Interact with your contract
-For easy-to-use code snippets for your contract, view the Code Snippets on your contract's dashboard or visit the [SDK documentation](/contracts/interact/overview).
+For easy-to-use code snippets for your contract, view the Code Snippets on your contract's dashboard or visit the [SDK documentation](/contracts).
diff --git a/apps/portal/src/app/contracts/page.mdx b/apps/portal/src/app/contracts/page.mdx
index 448c44ad590..d139bd81522 100644
--- a/apps/portal/src/app/contracts/page.mdx
+++ b/apps/portal/src/app/contracts/page.mdx
@@ -20,7 +20,7 @@ Trusted, modular smart contracts that can be deployed securely on any EVM chain.
- [Build:](/contracts/build/overview) Write your own smart contracts
- [Deploy:](/contracts/deploy/overview) Contract deployment built for any use-case
- [Publish:](/contracts/publish/overview) Publish your contracts onchain
-- [Interact:](/contracts/interact/overview) Integrate smart contract interactions directly into your app
+- [Interact:](/contracts) Integrate smart contract interactions directly into your app
### Templates
diff --git a/apps/portal/src/app/glossary/gas/page.mdx b/apps/portal/src/app/glossary/gas/page.mdx
index e16fecb2903..8f4415a929c 100644
--- a/apps/portal/src/app/glossary/gas/page.mdx
+++ b/apps/portal/src/app/glossary/gas/page.mdx
@@ -7,7 +7,7 @@ Gas fees are typically paid in the currency that is native to the blockchain you
While using thirdweb is free, you will need to cover the cost of performing actions that update the blockchain's state.
-This includes actions performed via the thirdweb [dashboard](https://thirdweb.com/team), our [SDKs](/contracts/interact/overview), or any of our other products, including:
+This includes actions performed via the thirdweb [dashboard](https://thirdweb.com/team), our [SDKs](/contracts), or any of our other products, including:
- Deploying smart contracts
- [Lazy-minting](/glossary/lazy-minting) the metadata of your NFTs
diff --git a/apps/portal/src/app/glossary/ipfs/page.mdx b/apps/portal/src/app/glossary/ipfs/page.mdx
index e780e953690..0268d26c7c8 100644
--- a/apps/portal/src/app/glossary/ipfs/page.mdx
+++ b/apps/portal/src/app/glossary/ipfs/page.mdx
@@ -9,6 +9,6 @@ To secure content on IPFS forever, a node must [pin](https://docs.ipfs.tech/conc
[Learn more about how IPFS works](https://ipfs.tech/#how).
To read data from IPFS, an [IPFS Gateway](https://docs.ipfs.tech/concepts/ipfs-gateway/) is required. This allows you to access data from the IPFS protocol on browsers and other HTTP clients, such
-as when building an application using our [SDK](/contracts/interact/overview).
+as when building an application using our [SDK](/contracts).
Out of the box, all of our tools use [Storage](/storage) to store, pin, and retrieve data from IPFS via a gateway, currently powered by [Pinata](https://pinata.cloud/) under the hood.
diff --git a/apps/portal/src/app/page.tsx b/apps/portal/src/app/page.tsx
index 711f17a4d39..2ec7194ef0e 100644
--- a/apps/portal/src/app/page.tsx
+++ b/apps/portal/src/app/page.tsx
@@ -318,7 +318,7 @@ function ContractsSection() {
title="Interact"
description="Add smart contract interactions in your app"
icon={ContractInteractIcon}
- href="/contracts/interact/overview"
+ href="/contracts"
/>
Date: Sun, 1 Dec 2024 20:52:25 +0000
Subject: [PATCH 02/15] refactor(sdk): use ox in serializeTransaction
---
.../transaction/serialize-transaction.test.ts | 364 +++++++-----------
.../src/transaction/serialize-transaction.ts | 122 +++++-
2 files changed, 236 insertions(+), 250 deletions(-)
diff --git a/packages/thirdweb/src/transaction/serialize-transaction.test.ts b/packages/thirdweb/src/transaction/serialize-transaction.test.ts
index 4ce29c44c36..4090524e808 100644
--- a/packages/thirdweb/src/transaction/serialize-transaction.test.ts
+++ b/packages/thirdweb/src/transaction/serialize-transaction.test.ts
@@ -1,18 +1,10 @@
-import { assertType, describe, expect, test } from "vitest";
-
-import {
- type TransactionSerializable,
- type TransactionSerializableBase,
- type TransactionSerializableEIP1559,
- type TransactionSerializableEIP2930,
- type TransactionSerializableLegacy,
- type TransactionSerializedEIP1559,
- type TransactionSerializedEIP2930,
- type TransactionSerializedLegacy,
- parseTransaction,
-} from "viem";
-
-import { checksumAddress } from "../utils/address.js";
+import * as ox__Hex from "ox/Hex";
+import * as ox__TransactionEnvelopeEip1559 from "ox/TransactionEnvelopeEip1559";
+import * as ox__TransactionEnvelopeEip2930 from "ox/TransactionEnvelopeEip2930";
+import * as ox__TransactionEnvelopeLegacy from "ox/TransactionEnvelopeLegacy";
+import { describe, expect, test } from "vitest";
+
+import { type Address, checksumAddress } from "../utils/address.js";
import { fromGwei, toWei } from "../utils/units.js";
import { serializeTransaction } from "./serialize-transaction.js";
@@ -24,7 +16,7 @@ const BASE_TRANSACTION = {
to: checksumAddress(TEST_ACCOUNT_B.address),
nonce: 785,
value: toWei("1"),
-} as const satisfies TransactionSerializableBase;
+} as const;
describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
const baseEip1559 = {
@@ -32,21 +24,23 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
chainId: 1,
maxFeePerGas: fromGwei("2"),
maxPriorityFeePerGas: fromGwei("2"),
- } as const satisfies TransactionSerializableEIP1559;
+ } as const;
test("default", () => {
- const serialized = serializeTransaction({
+ const serialized = serializeTransaction({
transaction: baseEip1559,
});
- assertType(serialized);
expect(serialized).toEqual(
"0x02ef0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ );
// The parsed transaction to address is not guaranteed to be checksummed, but our input address is
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
...baseEip1559,
+ nonce: BigInt(baseEip1559.nonce),
type: "eip1559",
});
});
@@ -58,18 +52,22 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
} as const;
const serialized = serializeTransaction({ transaction: args });
expect(serialized).toEqual("0x02c90180808080808080c0");
- expect(parseTransaction(serialized)).toEqual(args);
+
+ const tx = ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ );
+ expect(tx).toEqual(args);
});
test("default (all zeros)", () => {
const baseEip1559Zero = {
- to: TEST_ACCOUNT_B.address,
+ to: TEST_ACCOUNT_B.address as Address,
nonce: 0,
chainId: 1,
maxFeePerGas: 0n,
maxPriorityFeePerGas: 0n,
value: 0n,
- } satisfies TransactionSerializableEIP1559;
+ };
const serialized = serializeTransaction({
transaction: baseEip1559Zero,
@@ -78,7 +76,9 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
expect(serialized).toEqual(
"0x02dd01808080809470997970c51812dc3a010c7d01b50e0d17dc79c88080c0",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ );
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
chainId: 1,
to: TEST_ACCOUNT_B.address,
@@ -95,7 +95,14 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
transaction: args,
});
expect(serialized).toEqual("0x02c90180800180808080c0");
- expect(parseTransaction(serialized)).toEqual({ ...args, type: "eip1559" });
+ expect(
+ ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ ),
+ ).toEqual({
+ ...args,
+ type: "eip1559",
+ });
});
test("args: gas", () => {
@@ -109,7 +116,11 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
expect(serialized).toEqual(
"0x02f101820311847735940084773594008252099470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c0",
);
- expect(parseTransaction(serialized).gas).toEqual(args.gas);
+ expect(
+ ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ ).gas,
+ ).toEqual(args.gas);
});
test("args: accessList", () => {
@@ -121,31 +132,39 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
storageKeys: [
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
- ],
+ ] as const,
},
],
- } satisfies TransactionSerializableEIP1559;
- const serialized = serializeTransaction({
+ };
+ const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual(
"0x02f88b0182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080f85bf859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000001a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
);
- expect(parseTransaction(serialized).accessList).toEqual(args.accessList);
+ expect(
+ ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ ).accessList,
+ ).toEqual(args.accessList);
});
test("args: data", () => {
const args = {
...baseEip1559,
data: "0x1234",
- } satisfies TransactionSerializableEIP1559;
+ } as const;
const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual(
"0x02f10182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000821234c0",
);
- expect(parseTransaction(serialized).data).toEqual(args.data);
+ expect(
+ ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ ).data,
+ ).toEqual(args.data);
});
test("signed", async () => {
@@ -162,7 +181,11 @@ describe.runIf(process.env.TW_SECRET_KEY)("eip1559", () => {
expect(serialized).toEqual(
"0x02f8720182031184773594008477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080c001a0ce18214ff9d06ecaacb61811f9d6dc2be922e8cebddeaf6df0b30d5c498f6d33a05f0487c6dbbf2139f7c705d8054dbb16ecac8ae6256ce2c4c6f2e7ef35b3a496",
);
- expect(parseTransaction(serialized).yParity).toEqual(1);
+ expect(
+ ox__TransactionEnvelopeEip1559.deserialize(
+ serialized as ox__TransactionEnvelopeEip1559.Serialized,
+ ).yParity,
+ ).toEqual(1);
});
test("signature", () => {
@@ -270,19 +293,21 @@ describe("eip2930", () => {
},
],
gasPrice: fromGwei("2"),
- } as const satisfies TransactionSerializableEIP2930;
+ } as const;
test("default", () => {
- const serialized = serializeTransaction({
+ const serialized = serializeTransaction({
transaction: BASE_EIP2930_TRANSACTION,
});
- assertType(serialized);
expect(serialized).toEqual(
"0x01f863018203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080f838f7941234512345123451234512345123451234512345e1a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ );
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
...BASE_EIP2930_TRANSACTION,
+ nonce: BigInt(BASE_EIP2930_TRANSACTION.nonce),
type: "eip2930",
});
});
@@ -295,7 +320,7 @@ describe("eip2930", () => {
value: 0n,
gasPrice: 0n,
accessList: [],
- } satisfies TransactionSerializableEIP2930;
+ };
const serialized = serializeTransaction({
transaction: baseEip2930Zero,
@@ -305,7 +330,9 @@ describe("eip2930", () => {
"0x01dc018080809470997970c51812dc3a010c7d01b50e0d17dc79c88080c0",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ );
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
chainId: 1,
to: checksumAddress(TEST_ACCOUNT_B.address),
@@ -325,26 +352,34 @@ describe("eip2930", () => {
},
],
gasPrice: fromGwei("2"),
- } satisfies TransactionSerializableEIP2930;
- const serialized = serializeTransaction({
+ } as const;
+ const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual(
"0x01f8450180847735940080808080f838f7940000000000000000000000000000000000000000e1a00000000000000000000000000000000000000000000000000000000000000001",
);
- expect(parseTransaction(serialized).accessList).toEqual(args.accessList);
+ expect(
+ ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ ).accessList,
+ ).toEqual(args.accessList);
});
test("minimal (w/ type)", () => {
const args = {
chainId: 1,
type: "eip2930",
- } satisfies TransactionSerializableEIP2930;
+ };
const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual("0x01c801808080808080c0");
- expect(parseTransaction(serialized).type).toEqual("eip2930");
+ expect(
+ ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ ).type,
+ ).toEqual("eip2930");
});
test("args: gas", () => {
@@ -356,19 +391,27 @@ describe("eip2930", () => {
expect(serialized).toEqual(
"0x01f8650182031184773594008252099470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080f838f7941234512345123451234512345123451234512345e1a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
);
- expect(parseTransaction(serialized).gas).toEqual(args.gas);
+ expect(
+ ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ ).gas,
+ ).toEqual(args.gas);
});
test("args: data", () => {
const args = {
...BASE_EIP2930_TRANSACTION,
data: "0x1234",
- } satisfies TransactionSerializableEIP2930;
+ } as const;
const serialized = serializeTransaction({ transaction: args });
expect(serialized).toEqual(
"0x01f865018203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000821234f838f7941234512345123451234512345123451234512345e1a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
);
- expect(parseTransaction(serialized).data).toEqual(args.data);
+ expect(
+ ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ ).data,
+ ).toEqual(args.data);
});
test("signed", async () => {
@@ -384,10 +427,14 @@ describe("eip2930", () => {
expect(serialized).toEqual(
"0x01f8a6018203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080f838f7941234512345123451234512345123451234512345e1a060fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe01a0dc7b3483c0b183823f07d77247c60678d861080acdc4fb8b9fd131770b475c40a040f16567391132746735aff4d5a3fa5ae42ff3d5d538e341870e0259dc40741a",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeEip2930.deserialize(
+ serialized as ox__TransactionEnvelopeEip2930.Serialized,
+ );
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
...BASE_EIP2930_TRANSACTION,
- ...signature,
+ nonce: BigInt(BASE_EIP2930_TRANSACTION.nonce),
+ r: ox__Hex.toBigInt(signature.r),
+ s: ox__Hex.toBigInt(signature.s),
type: "eip2930",
yParity: 1,
});
@@ -490,16 +537,19 @@ describe("legacy", () => {
};
test("default", () => {
- const serialized = serializeTransaction({
+ const serialized = serializeTransaction({
transaction: BASE_LEGACY_TRANSACTION,
});
- assertType(serialized);
+
expect(serialized).toEqual(
"0xe88203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeLegacy.deserialize(
+ serialized as ox__TransactionEnvelopeLegacy.Serialized,
+ );
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
...BASE_LEGACY_TRANSACTION,
+ nonce: BigInt(BASE_LEGACY_TRANSACTION.nonce),
type: "legacy",
});
});
@@ -510,7 +560,7 @@ describe("legacy", () => {
nonce: 0,
value: 0n,
gasPrice: 0n,
- } satisfies TransactionSerializableLegacy;
+ };
const serialized = serializeTransaction({
transaction: baseLegacyZero,
@@ -520,7 +570,9 @@ describe("legacy", () => {
"0xda8080809470997970c51812dc3a010c7d01b50e0d17dc79c88080",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeLegacy.deserialize(
+ serialized as ox__TransactionEnvelopeLegacy.Serialized,
+ );
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
to: checksumAddress(TEST_ACCOUNT_B.address),
type: "legacy",
@@ -530,23 +582,27 @@ describe("legacy", () => {
test("minimal (w/ gasPrice)", () => {
const args = {
gasPrice: fromGwei("2"),
- } satisfies TransactionSerializableLegacy;
+ };
const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual("0xca80847735940080808080");
- expect(parseTransaction(serialized).gasPrice).toEqual(args.gasPrice);
+ expect(
+ ox__TransactionEnvelopeLegacy.deserialize(serialized).gasPrice,
+ ).toEqual(args.gasPrice);
});
test("minimal (w/ type)", () => {
const args = {
type: "legacy",
- } satisfies TransactionSerializableLegacy;
+ };
const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual("0xc6808080808080");
- expect(parseTransaction(serialized).type).toEqual(args.type);
+ expect(ox__TransactionEnvelopeLegacy.deserialize(serialized).type).toEqual(
+ args.type,
+ );
});
test("args: gas", () => {
@@ -560,33 +616,39 @@ describe("legacy", () => {
expect(serialized).toEqual(
"0xea82031184773594008252099470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080",
);
- expect(parseTransaction(serialized).gas).toEqual(args.gas);
+ expect(ox__TransactionEnvelopeLegacy.deserialize(serialized).gas).toEqual(
+ args.gas,
+ );
});
test("args: data", () => {
const args = {
...BASE_LEGACY_TRANSACTION,
data: "0x1234",
- } satisfies TransactionSerializableLegacy;
+ } as const;
const serialized = serializeTransaction({
transaction: args,
});
expect(serialized).toEqual(
"0xea8203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000821234",
);
- expect(parseTransaction(serialized).data).toEqual(args.data);
+ expect(ox__TransactionEnvelopeLegacy.deserialize(serialized).data).toEqual(
+ args.data,
+ );
});
test("args: chainId", () => {
const args = {
...BASE_LEGACY_TRANSACTION,
chainId: 69,
- } satisfies TransactionSerializableLegacy;
+ } as const;
const serialized = serializeTransaction({ transaction: args });
expect(serialized).toEqual(
"0xeb8203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a764000080458080",
);
- expect(parseTransaction(serialized).chainId).toEqual(args.chainId);
+ expect(
+ ox__TransactionEnvelopeLegacy.deserialize(serialized).chainId,
+ ).toEqual(args.chainId);
});
test("signed", async () => {
@@ -602,10 +664,13 @@ describe("legacy", () => {
expect(serialized).toEqual(
"0xf86b8203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a7640000801ca06cb0e8d21e5baf998fb9a05f47acd83692dc148f90b81b332a152f020da0ae98a0344e49bacb1ef7af7c2ffed9e88d3f0ae0aa4945c9da0a660a03717dd5621f98",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeLegacy.deserialize(serialized);
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
...BASE_LEGACY_TRANSACTION,
- ...signature,
+ nonce: BigInt(BASE_LEGACY_TRANSACTION.nonce),
+ r: ox__Hex.toBigInt(signature.r),
+ s: ox__Hex.toBigInt(signature.s),
+ v: 28,
yParity: 1,
type: "legacy",
});
@@ -653,13 +718,15 @@ describe("legacy", () => {
expect(serialized).toEqual(
"0xf86c8203118477359400809470997970c51812dc3a010c7d01b50e0d17dc79c8880de0b6b3a76400008081ada02f43314322cf4c5dd645b028aa0b0dadff0fb73c41a6f0620ff1dfb11601ac30a066f37a65e139fa4b6df33a42ab5ccaeaa7a109382e7430caefd1deee63962626",
);
- const tx = parseTransaction(serialized);
+ const tx = ox__TransactionEnvelopeLegacy.deserialize(serialized);
expect({ ...tx, to: tx.to ? checksumAddress(tx.to) : undefined }).toEqual({
...args,
- ...signature,
+ nonce: BigInt(args.nonce),
+ r: ox__Hex.toBigInt(signature.r),
+ s: ox__Hex.toBigInt(signature.s),
yParity: 0,
type: "legacy",
- v: 173n,
+ v: 173,
});
});
@@ -686,164 +753,3 @@ test("cannot infer type from transaction object", () => {
}),
).toThrow();
});
-
-describe("miscellaneous", () => {
- test("https://github.com/wevm/viem/issues/1433", () => {
- expect(
- serializeTransaction({
- transaction: {
- blockHash: null,
- blockNumber: null,
- from: "0xc702b9950e44f7d321fa16ee88bf8e1a561249ba",
- gas: 51627n,
- gasPrice: 3000000000n,
- hash: "0x3eaa88a766e82cbe53c95218ab4c3cf316325802b5f75d086b5121007b918e92",
- input:
- "0xa9059cbb00000000000000000000000082fc882199816bcec06baf848eaec51c2f96c85b000000000000000000000000000000000000000000000000eccae3078bacd15d",
- nonce: 117,
- to: "0x55d398326f99059ff775485246999027b3197955",
- transactionIndex: null,
- value: 0n,
- type: "legacy",
- v: 84475n,
- r: "0x73b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bf",
- s: "0x354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23",
- chainId: undefined,
- typeHex: "0x0",
- } as TransactionSerializable,
- }),
- ).toMatchInlineSnapshot(
- '"0xf8667584b2d05e0082c9ab9455d398326f99059ff775485246999027b31979558080830149fba073b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bfa0354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23"',
- );
-
- expect(
- serializeTransaction({
- transaction: {
- blockHash: null,
- blockNumber: null,
- from: "0xc702b9950e44f7d321fa16ee88bf8e1a561249ba",
- gas: 51627n,
- gasPrice: 3000000000n,
- hash: "0x3eaa88a766e82cbe53c95218ab4c3cf316325802b5f75d086b5121007b918e92",
- input:
- "0xa9059cbb00000000000000000000000082fc882199816bcec06baf848eaec51c2f96c85b000000000000000000000000000000000000000000000000eccae3078bacd15d",
- nonce: 117,
- to: "0x55d398326f99059ff775485246999027b3197955",
- transactionIndex: null,
- value: 0n,
- type: "legacy",
- v: 84476n,
- r: "0x73b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bf",
- s: "0x354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23",
- chainId: undefined,
- typeHex: "0x0",
- } as TransactionSerializable,
- }),
- ).toMatchInlineSnapshot(
- '"0xf8667584b2d05e0082c9ab9455d398326f99059ff775485246999027b31979558080830149fca073b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bfa0354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23"',
- );
-
- expect(
- serializeTransaction({
- transaction: {
- blockHash: null,
- blockNumber: null,
- from: "0xc702b9950e44f7d321fa16ee88bf8e1a561249ba",
- gas: 51627n,
- gasPrice: 3000000000n,
- hash: "0x3eaa88a766e82cbe53c95218ab4c3cf316325802b5f75d086b5121007b918e92",
- input:
- "0xa9059cbb00000000000000000000000082fc882199816bcec06baf848eaec51c2f96c85b000000000000000000000000000000000000000000000000eccae3078bacd15d",
- nonce: 117,
- to: "0x55d398326f99059ff775485246999027b3197955",
- transactionIndex: null,
- value: 0n,
- type: "legacy",
- v: 35n,
- r: "0x73b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bf",
- s: "0x354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23",
- chainId: undefined,
- typeHex: "0x0",
- } as TransactionSerializable,
- }),
- ).toMatchInlineSnapshot(
- '"0xf8637584b2d05e0082c9ab9455d398326f99059ff775485246999027b319795580801ba073b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bfa0354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23"',
- );
-
- expect(
- serializeTransaction({
- transaction: {
- blockHash: null,
- blockNumber: null,
- from: "0xc702b9950e44f7d321fa16ee88bf8e1a561249ba",
- gas: 51627n,
- gasPrice: 3000000000n,
- hash: "0x3eaa88a766e82cbe53c95218ab4c3cf316325802b5f75d086b5121007b918e92",
- input:
- "0xa9059cbb00000000000000000000000082fc882199816bcec06baf848eaec51c2f96c85b000000000000000000000000000000000000000000000000eccae3078bacd15d",
- nonce: 117,
- to: "0x55d398326f99059ff775485246999027b3197955",
- transactionIndex: null,
- value: 0n,
- type: "legacy",
- v: 36n,
- r: "0x73b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bf",
- s: "0x354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23",
- chainId: undefined,
- typeHex: "0x0",
- } as TransactionSerializable,
- }),
- ).toMatchInlineSnapshot(
- '"0xf8637584b2d05e0082c9ab9455d398326f99059ff775485246999027b319795580801ca073b39769ff4a36515c8fca546550a3fdafebbf37fa9e22be2d92b44653ade7bfa0354c756a1aa3346e9b3ea5423ac99acfc005e9cce2cd698e14d792f43fa15a23"',
- );
- });
-
- test("https://github.com/wevm/viem/issues/1849", async () => {
- const tx = {
- blockHash:
- "0xbfafd5da42c7e715149a1fbcc20c40bcf5f62e013f60af9cdf07c27142b6a0ff",
- blockNumber: 19295009n,
- gas: 421374n,
- gasPrice: 20701311233n,
- input:
- "0x3a2b7b980000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000726000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000065d96f1b00000000000000000000000000000000000000000000000000000000000000030b010c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000c23190af48df1c00000000000000000000000000000000000000000000000000000000000001000000000000000000000000002648f5592c09a260c601acde44e7f8f2944944fb0000000000000000000000000000000000000000000000000f43fc2c04ee000000000000000000000000000000000000000000000000000000c23190af48df1c00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bbe33f57f41a20b2f00dec91dcc1169597f36221f002710c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000023a03a3f85888a471f4effcafa03431511e388560000000000000000000000000000000000000000000000000000000000000000",
- nonce: 38,
- to: "0x2648f5592c09a260c601acde44e7f8f2944944fb",
- transactionIndex: 74,
- value: 57108134443873424n,
- type: "legacy",
- chainId: 1,
- v: 38n,
- r: "0x5abc283bf902f90884f800b2f810febd5e90f8d5077d89e150631bbcc547b7d1",
- s: "0x5acc7718573bcd268256c996f2ae1bdd2bd97019992f44d5806b1cc843cde2e7",
- typeHex: "0x0",
- } as const;
-
- const serialized = serializeTransaction({
- transaction: { ...tx, data: tx.input },
- });
-
- expect(keccak256(serialized)).toEqual(
- "0x6ed21df69b02678dfb290ef2a43d490303562eb387f70795766b37bfa9d09bd2",
- );
- });
-
- test("Successfully handles hex as bigint", () => {
- const transaction = {
- to: "0x0000000000000000000000000000000000000000",
- chainId: 31337,
- data: "0x",
- gas: 21001n,
- nonce: 0,
- accessList: undefined,
- value: "0x0",
- maxFeePerGas: 3100000000n,
- maxPriorityFeePerGas: 1100000000n,
- } as unknown as TransactionSerializable;
-
- const signed = serializeTransaction({ transaction });
- expect(signed).toMatchInlineSnapshot(
- `"0x02ec827a6980844190ab0084b8c63f008252099400000000000000000000000000000000000000008330783080c0"`,
- );
- });
-});
diff --git a/packages/thirdweb/src/transaction/serialize-transaction.ts b/packages/thirdweb/src/transaction/serialize-transaction.ts
index 3f596440af3..c2ca659f226 100644
--- a/packages/thirdweb/src/transaction/serialize-transaction.ts
+++ b/packages/thirdweb/src/transaction/serialize-transaction.ts
@@ -1,15 +1,33 @@
-import {
- type GetTransactionType,
- type SerializedTransactionReturnType,
- type Signature,
- type TransactionSerializable,
- type TransactionType,
- serializeTransaction as _serializeTransaction,
-} from "viem";
+import * as ox__Hex from "ox/Hex";
+import * as ox__Signature from "ox/Signature";
+import * as ox__TransactionEnvelopeEip1559 from "ox/TransactionEnvelopeEip1559";
+import * as ox__TransactionEnvelopeEip2930 from "ox/TransactionEnvelopeEip2930";
+import * as ox__TransactionEnvelopeLegacy from "ox/TransactionEnvelopeLegacy";
+import type { Hex } from "../utils/encoding/hex.js";
+
+export type SerializableTransaction = {
+ type?: string | undefined;
+ r?: Hex;
+ s?: Hex;
+ v?: bigint;
+ yParity?: number;
+ accessList?:
+ | ox__TransactionEnvelopeEip2930.TransactionEnvelopeEip2930["accessList"]
+ | undefined;
+ chainId?: number | undefined;
+ gasPrice?: bigint | undefined;
+ maxFeePerGas?: bigint | undefined;
+ maxPriorityFeePerGas?: bigint | undefined;
+ data?: Hex | undefined;
+ to?: Hex | undefined;
+ nonce?: number | bigint | undefined;
+ value?: bigint | undefined;
+ gasLimit?: bigint | undefined;
+};
export type SerializeTransactionOptions = {
- transaction: TransactionSerializable;
- signature?: Signature | undefined;
+ transaction: SerializableTransaction;
+ signature?: ox__Signature.Signature | undefined;
};
/**
@@ -32,18 +50,20 @@ export type SerializeTransactionOptions = {
* });
* ```
*/
-export function serializeTransaction<
- const transaction extends TransactionSerializable,
- _transactionType extends TransactionType = GetTransactionType,
->(
+export function serializeTransaction(
options: SerializeTransactionOptions,
-): SerializedTransactionReturnType {
+): Hex {
const { transaction } = options;
+ const type = getTransactionEnvelopeType(transaction);
+
// This is to maintain compatibility with our old interface (including the signature in the transaction object)
const signature = (() => {
if (options.signature) return options.signature;
- if (transaction.v === undefined && transaction.yParity === undefined) {
+ if (
+ typeof transaction.v === "undefined" &&
+ typeof transaction.yParity === "undefined"
+ ) {
return undefined;
}
@@ -52,12 +72,72 @@ export function serializeTransaction<
}
return {
- v: transaction.v,
- r: transaction.r,
- s: transaction.s,
- yParity: transaction.yParity,
+ r: ox__Hex.toBigInt(transaction.r),
+ s: ox__Hex.toBigInt(transaction.s),
+ yParity:
+ typeof transaction.v !== "undefined" &&
+ typeof transaction.yParity === "undefined"
+ ? ox__Signature.vToYParity(Number(transaction.v))
+ : (transaction.yParity as number),
};
})();
- return _serializeTransaction(transaction, signature as Signature | undefined); // Trust the options type-checking did its job and that the converted signature mirrors that type
+ if (type === "eip1559") {
+ const typedTransaction =
+ transaction as ox__TransactionEnvelopeEip1559.TransactionEnvelopeEip1559;
+ ox__TransactionEnvelopeEip1559.assert(typedTransaction);
+
+ return ox__TransactionEnvelopeEip1559.serialize(typedTransaction, {
+ signature,
+ });
+ }
+
+ if (type === "legacy") {
+ const typedTransaction =
+ transaction as ox__TransactionEnvelopeLegacy.TransactionEnvelopeLegacy;
+ ox__TransactionEnvelopeLegacy.assert(typedTransaction);
+
+ return ox__TransactionEnvelopeLegacy.serialize(typedTransaction, {
+ signature,
+ });
+ }
+
+ if (type === "eip2930") {
+ const typedTransaction =
+ transaction as ox__TransactionEnvelopeEip2930.TransactionEnvelopeEip2930;
+ ox__TransactionEnvelopeEip2930.assert(typedTransaction);
+
+ return ox__TransactionEnvelopeEip2930.serialize(typedTransaction, {
+ signature,
+ });
+ }
+
+ throw new Error("Invalid transaction type");
+}
+
+/**
+ * @internal
+ */
+function getTransactionEnvelopeType(
+ transactionEnvelope: SerializableTransaction,
+) {
+ if (typeof transactionEnvelope.type !== "undefined") {
+ return transactionEnvelope.type;
+ }
+
+ if (
+ typeof transactionEnvelope.maxFeePerGas !== "undefined" ||
+ typeof transactionEnvelope.maxPriorityFeePerGas !== "undefined"
+ ) {
+ return "eip1559";
+ }
+
+ if (typeof transactionEnvelope.gasPrice !== "undefined") {
+ if (typeof transactionEnvelope.accessList !== "undefined") {
+ return "eip2930";
+ }
+ return "legacy";
+ }
+
+ throw new Error("Invalid transaction type");
}
From c176cc9b2b86d55729903fd8f6bd53de0c7faf26 Mon Sep 17 00:00:00 2001
From: gregfromstl
Date: Sun, 1 Dec 2024 23:26:20 +0000
Subject: [PATCH 03/15] lint
---
packages/thirdweb/src/transaction/serialize-transaction.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/thirdweb/src/transaction/serialize-transaction.ts b/packages/thirdweb/src/transaction/serialize-transaction.ts
index c2ca659f226..686074d49f1 100644
--- a/packages/thirdweb/src/transaction/serialize-transaction.ts
+++ b/packages/thirdweb/src/transaction/serialize-transaction.ts
@@ -5,7 +5,7 @@ import * as ox__TransactionEnvelopeEip2930 from "ox/TransactionEnvelopeEip2930";
import * as ox__TransactionEnvelopeLegacy from "ox/TransactionEnvelopeLegacy";
import type { Hex } from "../utils/encoding/hex.js";
-export type SerializableTransaction = {
+type SerializableTransaction = {
type?: string | undefined;
r?: Hex;
s?: Hex;
@@ -19,7 +19,7 @@ export type SerializableTransaction = {
maxFeePerGas?: bigint | undefined;
maxPriorityFeePerGas?: bigint | undefined;
data?: Hex | undefined;
- to?: Hex | undefined;
+ to?: string | null | undefined; // Must allow null for backwards compatibility
nonce?: number | bigint | undefined;
value?: bigint | undefined;
gasLimit?: bigint | undefined;
From 8aa454cbbbc2079749fa8250102ab0307d1b33af Mon Sep 17 00:00:00 2001
From: Phillip Ho
Date: Mon, 2 Dec 2024 08:07:37 +0800
Subject: [PATCH 04/15] chore: Add promptTokens and completionTokens fields
(#5542)
---
.changeset/tall-donuts-divide.md | 5 +++++
packages/service-utils/src/core/usage.ts | 2 ++
2 files changed, 7 insertions(+)
create mode 100644 .changeset/tall-donuts-divide.md
diff --git a/.changeset/tall-donuts-divide.md b/.changeset/tall-donuts-divide.md
new file mode 100644
index 00000000000..f1099a3827e
--- /dev/null
+++ b/.changeset/tall-donuts-divide.md
@@ -0,0 +1,5 @@
+---
+"@thirdweb-dev/service-utils": patch
+---
+
+feat: add promptTokens and completionTokens fields to usage
diff --git a/packages/service-utils/src/core/usage.ts b/packages/service-utils/src/core/usage.ts
index f71ec2ccd24..faaf2e9cc33 100644
--- a/packages/service-utils/src/core/usage.ts
+++ b/packages/service-utils/src/core/usage.ts
@@ -76,5 +76,7 @@ export const usageEventSchema = z.object({
onRampId: z.string().optional(),
evmRequestParams: z.string().optional(),
providerIp: z.string().optional(),
+ promptTokens: z.number().int().nonnegative().optional(),
+ completionTokens: z.number().int().nonnegative().optional(),
});
export type UsageEvent = z.infer;
From 93379251b79375784c1aac292dcaa209a1580b5e Mon Sep 17 00:00:00 2001
From: Joaquim Verges
Date: Mon, 2 Dec 2024 12:30:52 +1100
Subject: [PATCH 05/15] fix: improve transaction cost estimation for pay modal
(#5548)
---
.changeset/ten-rockets-stare.md | 5 +++
.../hooks/transaction/useSendTransaction.ts | 41 +------------------
.../screens/Buy/main/useBuyTxStates.ts | 30 +-------------
packages/thirdweb/src/transaction/utils.ts | 33 +++++++++++++++
4 files changed, 41 insertions(+), 68 deletions(-)
create mode 100644 .changeset/ten-rockets-stare.md
diff --git a/.changeset/ten-rockets-stare.md b/.changeset/ten-rockets-stare.md
new file mode 100644
index 00000000000..ffaf2132167
--- /dev/null
+++ b/.changeset/ten-rockets-stare.md
@@ -0,0 +1,5 @@
+---
+"thirdweb": patch
+---
+
+Fix tx cost estimation for pay transaction modal
diff --git a/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts b/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts
index b8b281938cd..922a213eda5 100644
--- a/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts
+++ b/packages/thirdweb/src/react/core/hooks/transaction/useSendTransaction.ts
@@ -1,14 +1,13 @@
import { type UseMutationResult, useMutation } from "@tanstack/react-query";
import type { Chain } from "../../../../chains/types.js";
-import { getGasPrice } from "../../../../gas/get-gas-price.js";
import type { BuyWithCryptoStatus } from "../../../../pay/buyWithCrypto/getStatus.js";
import type { BuyWithFiatStatus } from "../../../../pay/buyWithFiat/getStatus.js";
import type { FiatProvider } from "../../../../pay/utils/commonTypes.js";
-import { estimateGasCost } from "../../../../transaction/actions/estimate-gas-cost.js";
import type { GaslessOptions } from "../../../../transaction/actions/gasless/types.js";
import { sendTransaction } from "../../../../transaction/actions/send-transaction.js";
import type { WaitForReceiptOptions } from "../../../../transaction/actions/wait-for-tx-receipt.js";
import type { PreparedTransaction } from "../../../../transaction/prepare-transaction.js";
+import { getTransactionGasCost } from "../../../../transaction/utils.js";
import { resolvePromisedValue } from "../../../../utils/promise/resolve-promised-value.js";
import type { Wallet } from "../../../../wallets/interfaces/wallet.js";
import { getTokenBalance } from "../../../../wallets/utils/getTokenBalance.js";
@@ -215,7 +214,7 @@ export function useSendTransactionCore(args: {
tokenAddress: _erc20Value.tokenAddress,
})
: undefined,
- getTotalTxCostForBuy(tx, account.address),
+ getTransactionGasCost(tx, account.address),
]);
const gasSponsored = hasSponsoredTransactionsEnabled(wallet);
@@ -248,39 +247,3 @@ export function useSendTransactionCore(args: {
},
});
}
-
-async function getTotalTxCostForBuy(tx: PreparedTransaction, from?: string) {
- try {
- const gasCost = await estimateGasCost({
- transaction: tx,
- from,
- });
-
- const bufferCost = gasCost.wei / 10n;
-
- // Note: get tx.value AFTER estimateGasCost
- const txValue = await resolvePromisedValue(tx.value);
-
- // add 10% extra gas cost to the estimate to ensure user buys enough to cover the tx cost
- return gasCost.wei + bufferCost + (txValue || 0n);
- } catch {
- if (from) {
- // try again without passing from
- return await getTotalTxCostForBuy(tx);
- }
- // fallback if both fail, use the tx value + 2M * gas price
- const value = await resolvePromisedValue(tx.value);
-
- const gasPrice = await getGasPrice({
- client: tx.client,
- chain: tx.chain,
- });
-
- const buffer = 2_000_000n * gasPrice;
-
- if (!value) {
- return 0n + buffer;
- }
- return value + buffer;
- }
-}
diff --git a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts
index 919da47731f..f913825b356 100644
--- a/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts
+++ b/packages/thirdweb/src/react/web/ui/ConnectWallet/screens/Buy/main/useBuyTxStates.ts
@@ -4,10 +4,9 @@ import { getChainMetadata } from "../../../../../../../chains/utils.js";
import { NATIVE_TOKEN_ADDRESS } from "../../../../../../../constants/addresses.js";
import { getContract } from "../../../../../../../contract/contract.js";
import { getCurrencyMetadata } from "../../../../../../../extensions/erc20/read/getCurrencyMetadata.js";
-import { getGasPrice } from "../../../../../../../gas/get-gas-price.js";
import { encode } from "../../../../../../../transaction/actions/encode.js";
-import { estimateGasCost } from "../../../../../../../transaction/actions/estimate-gas-cost.js";
import type { PreparedTransaction } from "../../../../../../../transaction/prepare-transaction.js";
+import { getTransactionGasCost } from "../../../../../../../transaction/utils.js";
import type { Hex } from "../../../../../../../utils/encoding/hex.js";
import { resolvePromisedValue } from "../../../../../../../utils/promise/resolve-promised-value.js";
import type { Account } from "../../../../../../../wallets/interfaces/wallet.js";
@@ -138,30 +137,3 @@ export function useTransactionCostAndData(args: {
},
});
}
-
-async function getTransactionGasCost(tx: PreparedTransaction, from?: string) {
- try {
- const gasCost = await estimateGasCost({
- transaction: tx,
- from,
- });
-
- const bufferCost = gasCost.wei / 10n;
-
- // Note: get tx.value AFTER estimateGasCost
- // add 10% extra gas cost to the estimate to ensure user buys enough to cover the tx cost
- return gasCost.wei + bufferCost;
- } catch {
- if (from) {
- // try again without passing from
- return await getTransactionGasCost(tx);
- }
- // fallback if both fail, use the tx value + 2M * gas price
- const gasPrice = await getGasPrice({
- client: tx.client,
- chain: tx.chain,
- });
-
- return 2_000_000n * gasPrice;
- }
-}
diff --git a/packages/thirdweb/src/transaction/utils.ts b/packages/thirdweb/src/transaction/utils.ts
index ca52bd5edd8..de8df87c4cd 100644
--- a/packages/thirdweb/src/transaction/utils.ts
+++ b/packages/thirdweb/src/transaction/utils.ts
@@ -1,4 +1,7 @@
import type { AbiFunction } from "abitype";
+import { getGasPrice } from "../gas/get-gas-price.js";
+import { estimateGasCost } from "./actions/estimate-gas-cost.js";
+import type { PreparedTransaction } from "./prepare-transaction.js";
/**
* @internal
@@ -11,3 +14,33 @@ export function isAbiFunction(item: unknown): item is AbiFunction {
item.type === "function"
);
}
+
+export async function getTransactionGasCost(
+ tx: PreparedTransaction,
+ from?: string,
+) {
+ try {
+ const gasCost = await estimateGasCost({
+ transaction: tx,
+ from,
+ });
+
+ const bufferCost = gasCost.wei / 10n;
+
+ // Note: get tx.value AFTER estimateGasCost
+ // add 10% extra gas cost to the estimate to ensure user buys enough to cover the tx cost
+ return gasCost.wei + bufferCost;
+ } catch {
+ if (from) {
+ // try again without passing from
+ return await getTransactionGasCost(tx);
+ }
+ // fallback if both fail, use the tx value + 1M * gas price
+ const gasPrice = await getGasPrice({
+ client: tx.client,
+ chain: tx.chain,
+ });
+
+ return 1_000_000n * gasPrice;
+ }
+}
From 1d80944752076a38e133ae79ae401ad1c7f9b40c Mon Sep 17 00:00:00 2001
From: Jonas Daniels
Date: Sun, 1 Dec 2024 17:50:45 -0800
Subject: [PATCH 06/15] Version Packages (#5506)
Co-authored-by: github-actions[bot]
---
.changeset/clever-carrots-march.md | 5 --
.changeset/pink-ducks-flash.md | 5 --
.changeset/selfish-deers-destroy.md | 30 ------------
.changeset/tall-donuts-divide.md | 5 --
.changeset/ten-rockets-stare.md | 5 --
packages/service-utils/CHANGELOG.md | 6 +++
packages/service-utils/package.json | 2 +-
packages/thirdweb/CHANGELOG.md | 38 +++++++++++++++
packages/thirdweb/package.json | 74 +++++++++++++++++++++--------
9 files changed, 100 insertions(+), 70 deletions(-)
delete mode 100644 .changeset/clever-carrots-march.md
delete mode 100644 .changeset/pink-ducks-flash.md
delete mode 100644 .changeset/selfish-deers-destroy.md
delete mode 100644 .changeset/tall-donuts-divide.md
delete mode 100644 .changeset/ten-rockets-stare.md
diff --git a/.changeset/clever-carrots-march.md b/.changeset/clever-carrots-march.md
deleted file mode 100644
index e85a2296560..00000000000
--- a/.changeset/clever-carrots-march.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"thirdweb": minor
----
-
-Add headless components: ChainProvider, ChainIcon & ChainName
diff --git a/.changeset/pink-ducks-flash.md b/.changeset/pink-ducks-flash.md
deleted file mode 100644
index 2b444837147..00000000000
--- a/.changeset/pink-ducks-flash.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"thirdweb": patch
----
-
-Fix UI issue when assetTabs is set to an empty array
diff --git a/.changeset/selfish-deers-destroy.md b/.changeset/selfish-deers-destroy.md
deleted file mode 100644
index 84905ddb343..00000000000
--- a/.changeset/selfish-deers-destroy.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-"thirdweb": minor
----
-
-Adds LoyaltyCard extensions and support for ERC721 deployment.
-
-```ts
-import { deployERC721Contract } from "thirdweb/deploys";
-
-const loyaltyCardContractAddress = await deployERC721Contract({
- chain: "your-chain-id", // replace with your chain ID
- client: yourThirdwebClient, // replace with your Thirdweb client instance
- account: yourAccount, // replace with your account details
- type: "LoyaltyCard",
- params: {
- name: "MyLoyaltyCard",
- symbol: "LOYAL",
- description: "A loyalty card NFT contract",
- image: "path/to/image.png", // replace with your image path
- defaultAdmin: "0xYourAdminAddress", // replace with your admin address
- royaltyRecipient: "0xYourRoyaltyRecipient", // replace with your royalty recipient address
- royaltyBps: 500n, // 5% royalty
- trustedForwarders: ["0xTrustedForwarderAddress"], // replace with your trusted forwarder addresses
- saleRecipient: "0xYourSaleRecipient", // replace with your sale recipient address
- platformFeeBps: 200n, // 2% platform fee
- platformFeeRecipient: "0xYourPlatformFeeRecipient", // replace with your platform fee recipient address
- },
-});
-
-```
diff --git a/.changeset/tall-donuts-divide.md b/.changeset/tall-donuts-divide.md
deleted file mode 100644
index f1099a3827e..00000000000
--- a/.changeset/tall-donuts-divide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"@thirdweb-dev/service-utils": patch
----
-
-feat: add promptTokens and completionTokens fields to usage
diff --git a/.changeset/ten-rockets-stare.md b/.changeset/ten-rockets-stare.md
deleted file mode 100644
index ffaf2132167..00000000000
--- a/.changeset/ten-rockets-stare.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-"thirdweb": patch
----
-
-Fix tx cost estimation for pay transaction modal
diff --git a/packages/service-utils/CHANGELOG.md b/packages/service-utils/CHANGELOG.md
index a04e48e52e9..c1829948076 100644
--- a/packages/service-utils/CHANGELOG.md
+++ b/packages/service-utils/CHANGELOG.md
@@ -1,5 +1,11 @@
# @thirdweb-dev/service-utils
+## 0.4.51
+
+### Patch Changes
+
+- [#5542](https://github.com/thirdweb-dev/js/pull/5542) [`8aa454c`](https://github.com/thirdweb-dev/js/commit/8aa454cbbbc2079749fa8250102ab0307d1b33af) Thanks [@arcoraven](https://github.com/arcoraven)! - feat: add promptTokens and completionTokens fields to usage
+
## 0.4.50
### Patch Changes
diff --git a/packages/service-utils/package.json b/packages/service-utils/package.json
index 4f8206323df..609a219bb30 100644
--- a/packages/service-utils/package.json
+++ b/packages/service-utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/service-utils",
- "version": "0.4.50",
+ "version": "0.4.51",
"type": "module",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
diff --git a/packages/thirdweb/CHANGELOG.md b/packages/thirdweb/CHANGELOG.md
index e5c7e7b3fc1..93eee62c40a 100644
--- a/packages/thirdweb/CHANGELOG.md
+++ b/packages/thirdweb/CHANGELOG.md
@@ -1,5 +1,43 @@
# thirdweb
+## 5.72.0
+
+### Minor Changes
+
+- [#5495](https://github.com/thirdweb-dev/js/pull/5495) [`d1845f3`](https://github.com/thirdweb-dev/js/commit/d1845f3d6096d81e24bdb3cff38d19efd652ada1) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add headless components: ChainProvider, ChainIcon & ChainName
+
+- [#5529](https://github.com/thirdweb-dev/js/pull/5529) [`7488102`](https://github.com/thirdweb-dev/js/commit/7488102d20604a1d8cfd4653a34aa9a975f5c7f1) Thanks [@gregfromstl](https://github.com/gregfromstl)! - Adds LoyaltyCard extensions and support for ERC721 deployment.
+
+ ```ts
+ import { deployERC721Contract } from "thirdweb/deploys";
+
+ const loyaltyCardContractAddress = await deployERC721Contract({
+ chain: "your-chain-id", // replace with your chain ID
+ client: yourThirdwebClient, // replace with your Thirdweb client instance
+ account: yourAccount, // replace with your account details
+ type: "LoyaltyCard",
+ params: {
+ name: "MyLoyaltyCard",
+ symbol: "LOYAL",
+ description: "A loyalty card NFT contract",
+ image: "path/to/image.png", // replace with your image path
+ defaultAdmin: "0xYourAdminAddress", // replace with your admin address
+ royaltyRecipient: "0xYourRoyaltyRecipient", // replace with your royalty recipient address
+ royaltyBps: 500n, // 5% royalty
+ trustedForwarders: ["0xTrustedForwarderAddress"], // replace with your trusted forwarder addresses
+ saleRecipient: "0xYourSaleRecipient", // replace with your sale recipient address
+ platformFeeBps: 200n, // 2% platform fee
+ platformFeeRecipient: "0xYourPlatformFeeRecipient", // replace with your platform fee recipient address
+ },
+ });
+ ```
+
+### Patch Changes
+
+- [#5517](https://github.com/thirdweb-dev/js/pull/5517) [`480fb4e`](https://github.com/thirdweb-dev/js/commit/480fb4e8ec02b79fdb8b00d709994c50ef929a28) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Fix UI issue when assetTabs is set to an empty array
+
+- [#5548](https://github.com/thirdweb-dev/js/pull/5548) [`9337925`](https://github.com/thirdweb-dev/js/commit/93379251b79375784c1aac292dcaa209a1580b5e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix tx cost estimation for pay transaction modal
+
## 5.71.0
### Minor Changes
diff --git a/packages/thirdweb/package.json b/packages/thirdweb/package.json
index 9da061cecd7..47659fb1e43 100644
--- a/packages/thirdweb/package.json
+++ b/packages/thirdweb/package.json
@@ -1,6 +1,6 @@
{
"name": "thirdweb",
- "version": "5.71.0",
+ "version": "5.72.0",
"repository": {
"type": "git",
"url": "git+https://github.com/thirdweb-dev/js.git#main"
@@ -127,24 +127,60 @@
},
"typesVersions": {
"*": {
- "adapters/*": ["./dist/types/exports/adapters/*.d.ts"],
- "auth": ["./dist/types/exports/auth.d.ts"],
- "chains": ["./dist/types/exports/chains.d.ts"],
- "contract": ["./dist/types/exports/contract.d.ts"],
- "deploys": ["./dist/types/exports/deploys.d.ts"],
- "event": ["./dist/types/exports/event.d.ts"],
- "extensions/*": ["./dist/types/exports/extensions/*.d.ts"],
- "pay": ["./dist/types/exports/pay.d.ts"],
- "react": ["./dist/types/exports/react.d.ts"],
- "react-native": ["./dist/types/exports/react-native.d.ts"],
- "rpc": ["./dist/types/exports/rpc.d.ts"],
- "storage": ["./dist/types/exports/storage.d.ts"],
- "transaction": ["./dist/types/exports/transaction.d.ts"],
- "utils": ["./dist/types/exports/utils.d.ts"],
- "wallets": ["./dist/types/exports/wallets.d.ts"],
- "wallets/*": ["./dist/types/exports/wallets/*.d.ts"],
- "modules": ["./dist/types/exports/modules.d.ts"],
- "social": ["./dist/types/exports/social.d.ts"]
+ "adapters/*": [
+ "./dist/types/exports/adapters/*.d.ts"
+ ],
+ "auth": [
+ "./dist/types/exports/auth.d.ts"
+ ],
+ "chains": [
+ "./dist/types/exports/chains.d.ts"
+ ],
+ "contract": [
+ "./dist/types/exports/contract.d.ts"
+ ],
+ "deploys": [
+ "./dist/types/exports/deploys.d.ts"
+ ],
+ "event": [
+ "./dist/types/exports/event.d.ts"
+ ],
+ "extensions/*": [
+ "./dist/types/exports/extensions/*.d.ts"
+ ],
+ "pay": [
+ "./dist/types/exports/pay.d.ts"
+ ],
+ "react": [
+ "./dist/types/exports/react.d.ts"
+ ],
+ "react-native": [
+ "./dist/types/exports/react-native.d.ts"
+ ],
+ "rpc": [
+ "./dist/types/exports/rpc.d.ts"
+ ],
+ "storage": [
+ "./dist/types/exports/storage.d.ts"
+ ],
+ "transaction": [
+ "./dist/types/exports/transaction.d.ts"
+ ],
+ "utils": [
+ "./dist/types/exports/utils.d.ts"
+ ],
+ "wallets": [
+ "./dist/types/exports/wallets.d.ts"
+ ],
+ "wallets/*": [
+ "./dist/types/exports/wallets/*.d.ts"
+ ],
+ "modules": [
+ "./dist/types/exports/modules.d.ts"
+ ],
+ "social": [
+ "./dist/types/exports/social.d.ts"
+ ]
}
},
"browser": {
From 8d47654d57e260c11c893649e0da980cc2a0b003 Mon Sep 17 00:00:00 2001
From: Prithvish Baidya
Date: Mon, 2 Dec 2024 07:22:31 +0530
Subject: [PATCH 07/15] feat: edit documentation for Account Abstraction and
add Smart Backend Wallets (#5516)
---
.../features/account-abstraction/page.mdx | 15 +++++-
.../engine/features/backend-wallets/page.mdx | 53 ++++++++++++++++++-
2 files changed, 65 insertions(+), 3 deletions(-)
diff --git a/apps/portal/src/app/engine/features/account-abstraction/page.mdx b/apps/portal/src/app/engine/features/account-abstraction/page.mdx
index 788541693a6..20ae45d8cf2 100644
--- a/apps/portal/src/app/engine/features/account-abstraction/page.mdx
+++ b/apps/portal/src/app/engine/features/account-abstraction/page.mdx
@@ -1,4 +1,4 @@
-import { createMetadata } from "@doc";
+import { createMetadata, Callout } from "@doc";
export const metadata = createMetadata({
title: "Account Abstraction | thirdweb Engine",
@@ -8,6 +8,19 @@ export const metadata = createMetadata({
# Account Abstraction
+
+Consider using [Smart Backend Wallets](/engine/features/backend-wallets#smart-backend-wallets) instead. They combine EOA and smart account management into a single wallet, with automatic deployment and simplified integration:
+
+- No need to deploy or manage account factories
+- No separate account and backend wallet addresses to track
+- Works with all existing Engine endpoints without modification
+- Built-in transaction batching and recovery
+- Automatic gas management with paymaster
+
+Smart backend wallets are recommended when you need account abstraction for your own backend operations. Use the approach described below when you need to manage smart accounts for your users.
+
+
+
Use Engine to deploy, manage, and transact with smart accounts on behalf of your users.
## Deploy an account factory
diff --git a/apps/portal/src/app/engine/features/backend-wallets/page.mdx b/apps/portal/src/app/engine/features/backend-wallets/page.mdx
index a506013a2ee..64c9acf2ee6 100644
--- a/apps/portal/src/app/engine/features/backend-wallets/page.mdx
+++ b/apps/portal/src/app/engine/features/backend-wallets/page.mdx
@@ -1,4 +1,5 @@
import { createMetadata, Details } from "@doc";
+import { Callout } from "@doc";
export const metadata = createMetadata({
title: "Backend Wallets | thirdweb Engine",
@@ -12,6 +13,50 @@ Engine performs blockchain actions using backend wallets that you own and manage
There are multiple options for securing backend wallets.
+## Smart Backend Wallets
+
+Smart backend wallets are the recommended way to perform blockchain operations with Engine. Each smart backend wallet consists of an EOA (managed internally by Engine) and a smart account (using thirdweb's default account implementation).
+
+### Benefits
+
+Smart backend wallets inherit smart account benefits and offer several advantages over traditional EOAs:
+
+- **Gas Management**: Built-in paymaster eliminates the need to maintain gas tokens. This means you never need to hold crypto or top up gas.
+- **Better Nonce Management**: Smart accounts use multi-dimensional nonces, which are more efficient than EOAs.
+- **Simple Integration**: Works with all existing Engine endpoints without any code changes.
+
+### How it works
+
+The smart account is automatically deployed the first time you send a transaction on a chain. You don't need to think about deploying or managing the smart account - Engine handles everything behind the scenes. All transactions are sent as UserOperations to the EntryPoint contract on chain.
+
+
+ Unlike the [previous account abstraction implementation in
+ Engine](account-abstraction) where you had to manage both the account address
+ (`x-account-address`) and the backend wallet address
+ (`x-backend-wallet-address`) separately, smart backend wallets simplify this.
+ The backend wallet address is now the smart account address itself.
+
+
+
+ At this time, Smart Backend Wallets do not allow for importing a smart
+ account. In use cases where you need to import a smart account (such as with
+ session keys), you should use [Engine AA features that utilise the
+ `x-account-address` header](account-abstraction).
+
+
+### Configuration Types
+
+- `smart:local` - Smart account backed by a local key
+- `smart:aws-kms` - Smart account backed by AWS KMS
+- `smart:gcp-kms` - Smart account backed by Google Cloud KMS
+
+For AWS and Google Cloud KMS options, follow the setup instructions in the respective sections below.
+
+### Pricing
+
+Smart backend wallets have no additional costs to use with your Engine instance. Transactions sent from smart backend wallets follow the [same billing model as regular account abstraction transactions with thirdweb](/connect/account-abstraction/infrastructure#pricing--billing).
+Smart accounts need to be deployed on each chain. This costs gas and is billed to your account like any other transaction.
+
## Local wallet
A [local wallet](/references/wallets/v2/LocalWallet) is a wallet created or imported from a private key. Ensure your private key is backed up before transacting with a local wallet in a production environment.
@@ -31,7 +76,7 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
- `kms:Sign`
- `kms:CreateAlias`
- `kms:Verify`
-1. On the user page, navigate to **Security credentials > Access keys**.
+1. On the user page, navigate to **Security credentials > Access keys**.
1. Select **Create access key** to get an **Access Key** and **Secret Key**.
1. In the dashboard, navigate to **Configuration > Backend Wallets**.
1. Select **AWS KMS** and provide the following:
@@ -50,7 +95,6 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
- AWS KMS Key ID (example: `0489da75-9830-4a5a-97e3-e4a6df7775b3`)
- AWS KMS ARN (example: `arn:aws:kms:us-west-1:632186309261:key/0489da75-9830-4a5a-97e3-e4a6df7775b3`)
-
## Google Cloud KMS wallet
#### Setup
@@ -71,6 +115,7 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
This is the Project ID of the GCP project where the key was created.
**Where to find**:
+
- Navigate to the Google Cloud Console.
- Click on the project dropdown at the top of the page.
- The Project ID is displayed under your project's name.
@@ -82,6 +127,7 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
This is the location where the keyring was created (e.g., us-central1, europe-west1).
**Where to find**:
+
- In the Google Cloud Console, go to **Security > Cryptographic Keys**.
- Click on the keyring that contains your key.
- The location is displayed in the Location field.
@@ -93,6 +139,7 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
This is the ID of the keyring where your key is stored.
**Where to find**:
+
- In the Google Cloud Console, go to **Security > Cryptographic Keys**.
- Select the keyring that contains your key.
- The KeyRing ID is displayed in the list or the URL.
@@ -104,6 +151,7 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
This is the email associated with the service account used for accessing the KMS key.
**Where to find**:
+
- In the Google Cloud Console, go to **IAM & Admin > Service Accounts**.
- Find the service account you are using. its email will be in the format: `name@project.iam.gserviceaccount.com`
@@ -114,6 +162,7 @@ An [AWS KMS Wallet](/references/wallets/v2/AwsKmsWallet) is a wallet securely st
This is the private key of the service account that is used for authenticating API requests.
**Where to find**:
+
- Open the JSON file downloaded above.
- Copy the value of the `private_key` field.
From 1e9a6c7d8ae28012956b688f6e0af6f94c075181 Mon Sep 17 00:00:00 2001
From: Joaquim Verges
Date: Mon, 2 Dec 2024 13:10:15 +1100
Subject: [PATCH 08/15] fix: Handle zero maxPriorityFeePerGas in 712
transactions (#5531)
---
.changeset/wild-games-vanish.md | 5 +++++
.../transaction/actions/zksync/send-eip712-transaction.ts | 6 +++++-
2 files changed, 10 insertions(+), 1 deletion(-)
create mode 100644 .changeset/wild-games-vanish.md
diff --git a/.changeset/wild-games-vanish.md b/.changeset/wild-games-vanish.md
new file mode 100644
index 00000000000..3e26297745a
--- /dev/null
+++ b/.changeset/wild-games-vanish.md
@@ -0,0 +1,5 @@
+---
+"thirdweb": patch
+---
+
+Handle 0 value for maxPriorityFeePerGas in 712 transactions
diff --git a/packages/thirdweb/src/transaction/actions/zksync/send-eip712-transaction.ts b/packages/thirdweb/src/transaction/actions/zksync/send-eip712-transaction.ts
index a1241e12b97..83cfc233883 100644
--- a/packages/thirdweb/src/transaction/actions/zksync/send-eip712-transaction.ts
+++ b/packages/thirdweb/src/transaction/actions/zksync/send-eip712-transaction.ts
@@ -173,7 +173,11 @@ export async function getZkGasFees(args: {
resolvePromisedValue(transaction.eip712),
]);
let gasPerPubdata = eip712?.gasPerPubdata;
- if (!gas || !maxFeePerGas || !maxPriorityFeePerGas) {
+ if (
+ gas === undefined ||
+ maxFeePerGas === undefined ||
+ maxPriorityFeePerGas === undefined
+ ) {
const rpc = getRpcClient(transaction);
const params = await formatTransaction({ transaction, from });
const result = (await rpc({
From 57fa96b268671f19c7f57fa2208fb83e2b0e75b5 Mon Sep 17 00:00:00 2001
From: kien-ngo
Date: Mon, 2 Dec 2024 10:21:57 +0000
Subject: [PATCH 09/15] [SDK] Add 2 new Pay functions: convertFiatToCrypto and
convertCryptoToFiat (#5457)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
CNCT-2369
## Problem solved
Short description of the bug fixed or feature added
---
## PR-Codex overview
This PR introduces two new functions for converting between fiat and crypto currencies in the `thirdweb` package, along with corresponding tests and endpoints.
### Detailed summary
- Added `convertFiatToCrypto` and `convertCryptoToFiat` functions.
- Created endpoints for fiat to crypto and crypto to fiat conversions.
- Implemented tests for both conversion functions, covering various scenarios and error handling.
- Updated documentation with examples for the new functions.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
---
.changeset/stupid-buses-wink.md | 41 +++++++
packages/thirdweb/src/exports/pay.ts | 10 ++
.../src/pay/convert/cryptoToFiat.test.ts | 95 +++++++++++++++
.../thirdweb/src/pay/convert/cryptoToFiat.ts | 111 ++++++++++++++++++
.../src/pay/convert/fiatToCrypto.test.ts | 96 +++++++++++++++
.../thirdweb/src/pay/convert/fiatToCrypto.ts | 110 +++++++++++++++++
.../thirdweb/src/pay/utils/definitions.ts | 6 +
7 files changed, 469 insertions(+)
create mode 100644 .changeset/stupid-buses-wink.md
create mode 100644 packages/thirdweb/src/pay/convert/cryptoToFiat.test.ts
create mode 100644 packages/thirdweb/src/pay/convert/cryptoToFiat.ts
create mode 100644 packages/thirdweb/src/pay/convert/fiatToCrypto.test.ts
create mode 100644 packages/thirdweb/src/pay/convert/fiatToCrypto.ts
diff --git a/.changeset/stupid-buses-wink.md b/.changeset/stupid-buses-wink.md
new file mode 100644
index 00000000000..99248b52728
--- /dev/null
+++ b/.changeset/stupid-buses-wink.md
@@ -0,0 +1,41 @@
+---
+"thirdweb": minor
+---
+
+Add 2 new Pay functions: convertFiatToCrypto and convertCryptoToFiat
+
+Examples:
+
+### Convert fiat (USD) to crypto
+```ts
+import { convertFiatToCrypto } from "thirdweb/pay";
+import { ethereum } from "thirdweb/chains";
+
+// Convert 2 cents to ETH
+const result = await convertFiatToCrypto({
+ from: "USD",
+ // the token address. For native token, use NATIVE_TOKEN_ADDRESS
+ to: "0x...",
+ // the chain (of the chain where the token belong to)
+ chain: ethereum,
+ // 2 cents
+ fromAmount: 0.02,
+});
+// Result: 0.0000057 (a number)
+```
+
+### Convert crypto to fiat (USD)
+
+```ts
+import { convertCryptoToFiat } from "thirdweb/pay";
+
+// Get Ethereum price
+const result = convertCryptoToFiat({
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
+ to: "USD",
+ chain: ethereum,
+ fromAmount: 1,
+});
+
+// Result: 3404.11 (number)
+```
\ No newline at end of file
diff --git a/packages/thirdweb/src/exports/pay.ts b/packages/thirdweb/src/exports/pay.ts
index 96c7b94fce2..f901ec55cd7 100644
--- a/packages/thirdweb/src/exports/pay.ts
+++ b/packages/thirdweb/src/exports/pay.ts
@@ -66,3 +66,13 @@ export type {
PayTokenInfo,
PayOnChainTransactionDetails,
} from "../pay/utils/commonTypes.js";
+
+export {
+ convertFiatToCrypto,
+ type ConvertFiatToCryptoParams,
+} from "../pay/convert/fiatToCrypto.js";
+
+export {
+ convertCryptoToFiat,
+ type ConvertCryptoToFiatParams,
+} from "../pay/convert/cryptoToFiat.js";
diff --git a/packages/thirdweb/src/pay/convert/cryptoToFiat.test.ts b/packages/thirdweb/src/pay/convert/cryptoToFiat.test.ts
new file mode 100644
index 00000000000..fed1b8e0aca
--- /dev/null
+++ b/packages/thirdweb/src/pay/convert/cryptoToFiat.test.ts
@@ -0,0 +1,95 @@
+import { describe, expect, it } from "vitest";
+import { TEST_CLIENT } from "~test/test-clients.js";
+import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
+import { base } from "../../chains/chain-definitions/base.js";
+import { ethereum } from "../../chains/chain-definitions/ethereum.js";
+import { sepolia } from "../../chains/chain-definitions/sepolia.js";
+import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
+import { convertCryptoToFiat } from "./cryptoToFiat.js";
+
+describe.runIf(process.env.TW_SECRET_KEY)("Pay: crypto-to-fiat", () => {
+ it("should convert ETH price to USD on Ethereum mainnet", async () => {
+ const data = await convertCryptoToFiat({
+ chain: ethereum,
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
+ fromAmount: 1,
+ to: "USD",
+ client: TEST_CLIENT,
+ });
+ expect(data.result).toBeDefined();
+ // Should be a number
+ expect(!Number.isNaN(data.result)).toBe(true);
+ // Since eth is around US$3000, we can add a test to check if the price is greater than $1500 (as a safe margin)
+ // let's hope that scenario does not happen :(
+ expect(Number(data.result) > 1500).toBe(true);
+ });
+
+ it("should convert ETH price to USD on Base mainnet", async () => {
+ const data = await convertCryptoToFiat({
+ chain: base,
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
+ fromAmount: 1,
+ to: "USD",
+ client: TEST_CLIENT,
+ });
+ expect(data.result).toBeDefined();
+ // Should be a number
+ expect(!Number.isNaN(data.result)).toBe(true);
+ // Since eth is around US$3000, we can add a test to check if the price is greater than $1500 (as a safe margin)
+ // let's hope that scenario does not happen :(
+ expect(data.result > 1500).toBe(true);
+ });
+
+ it("should return zero if fromAmount is zero", async () => {
+ const data = await convertCryptoToFiat({
+ chain: base,
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
+ fromAmount: 0,
+ to: "USD",
+ client: TEST_CLIENT,
+ });
+ expect(data.result).toBe(0);
+ });
+
+ it("should throw error for testnet chain (because testnets are not supported", async () => {
+ await expect(() =>
+ convertCryptoToFiat({
+ chain: sepolia,
+ fromTokenAddress: NATIVE_TOKEN_ADDRESS,
+ fromAmount: 1,
+ to: "USD",
+ client: TEST_CLIENT,
+ }),
+ ).rejects.toThrowError(
+ `Cannot fetch price for a testnet (chainId: ${sepolia.id})`,
+ );
+ });
+
+ it("should throw error if fromTokenAddress is set to an invalid EVM address", async () => {
+ await expect(() =>
+ convertCryptoToFiat({
+ chain: ethereum,
+ fromTokenAddress: "haha",
+ fromAmount: 1,
+ to: "USD",
+ client: TEST_CLIENT,
+ }),
+ ).rejects.toThrowError(
+ "Invalid fromTokenAddress. Expected a valid EVM contract address",
+ );
+ });
+
+ it("should throw error if fromTokenAddress is set to a wallet address", async () => {
+ await expect(() =>
+ convertCryptoToFiat({
+ chain: base,
+ fromTokenAddress: TEST_ACCOUNT_A.address,
+ fromAmount: 1,
+ to: "USD",
+ client: TEST_CLIENT,
+ }),
+ ).rejects.toThrowError(
+ `Error: ${TEST_ACCOUNT_A.address} on chainId: ${base.id} is not a valid contract address.`,
+ );
+ });
+});
diff --git a/packages/thirdweb/src/pay/convert/cryptoToFiat.ts b/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
new file mode 100644
index 00000000000..cf98821b254
--- /dev/null
+++ b/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
@@ -0,0 +1,111 @@
+import type { Address } from "abitype";
+import type { Chain } from "../../chains/types.js";
+import type { ThirdwebClient } from "../../client/client.js";
+import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
+import { getBytecode } from "../../contract/actions/get-bytecode.js";
+import { getContract } from "../../contract/contract.js";
+import { isAddress } from "../../utils/address.js";
+import { getClientFetch } from "../../utils/fetch.js";
+import { getPayConvertCryptoToFiatEndpoint } from "../utils/definitions.js";
+
+/**
+ * Props for the `convertCryptoToFiat` function
+ * @buyCrypto
+ */
+export type ConvertCryptoToFiatParams = {
+ client: ThirdwebClient;
+ /**
+ * The contract address of the token
+ * For native token, use NATIVE_TOKEN_ADDRESS
+ */
+ fromTokenAddress: Address;
+ /**
+ * The amount of token to convert to fiat value
+ */
+ fromAmount: number;
+ /**
+ * The chain that the token is deployed to
+ */
+ chain: Chain;
+ /**
+ * The fiat symbol. e.g "USD"
+ * Only USD is supported at the moment.
+ */
+ to: "USD";
+};
+
+/**
+ * Get a price of a token (using tokenAddress + chainId) in fiat.
+ * Only USD is supported at the moment.
+ * @example
+ * ### Basic usage
+ * For native token (non-ERC20), you should use NATIVE_TOKEN_ADDRESS as the value for `tokenAddress`
+ * ```ts
+ * import { convertCryptoToFiat } from "thirdweb/pay";
+ *
+ * // Get Ethereum price
+ * const result = convertCryptoToFiat({
+ * fromTokenAddress: NATIVE_TOKEN_ADDRESS,
+ * to: "USD",
+ * chain: ethereum,
+ * fromAmount: 1,
+ * });
+ *
+ * // Result: 3404.11
+ * ```
+ * @buyCrypto
+ * @returns a number representing the price (in selected fiat) of "x" token, with "x" being the `fromAmount`.
+ */
+export async function convertCryptoToFiat(
+ options: ConvertCryptoToFiatParams,
+): Promise<{ result: number }> {
+ const { client, fromTokenAddress, to, chain, fromAmount } = options;
+ if (Number(fromAmount) === 0) {
+ return { result: 0 };
+ }
+ // Testnets just don't work with our current provider(s)
+ if (chain.testnet === true) {
+ throw new Error(`Cannot fetch price for a testnet (chainId: ${chain.id})`);
+ }
+ // Some provider that we are using will return `0` for unsupported token
+ // so we should do some basic input validations before sending the request
+
+ // Make sure it's a valid EVM address
+ if (!isAddress(fromTokenAddress)) {
+ throw new Error(
+ "Invalid fromTokenAddress. Expected a valid EVM contract address",
+ );
+ }
+ // Make sure it's either a valid contract or a native token address
+ if (fromTokenAddress.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase()) {
+ const bytecode = await getBytecode(
+ getContract({
+ address: fromTokenAddress,
+ chain,
+ client,
+ }),
+ ).catch(() => undefined);
+ if (!bytecode || bytecode === "0x") {
+ throw new Error(
+ `Error: ${fromTokenAddress} on chainId: ${chain.id} is not a valid contract address.`,
+ );
+ }
+ }
+ const params = {
+ fromTokenAddress,
+ to,
+ chainId: String(chain.id),
+ fromAmount: String(fromAmount),
+ };
+ const queryString = new URLSearchParams(params).toString();
+ const url = `${getPayConvertCryptoToFiatEndpoint()}?${queryString}`;
+ const response = await getClientFetch(client)(url);
+ if (!response.ok) {
+ throw new Error(
+ `Failed to fetch ${to} value for token (${fromTokenAddress}) on chainId: ${chain.id}`,
+ );
+ }
+
+ const data: { result: number } = await response.json();
+ return data;
+}
diff --git a/packages/thirdweb/src/pay/convert/fiatToCrypto.test.ts b/packages/thirdweb/src/pay/convert/fiatToCrypto.test.ts
new file mode 100644
index 00000000000..3a74b4ef0d5
--- /dev/null
+++ b/packages/thirdweb/src/pay/convert/fiatToCrypto.test.ts
@@ -0,0 +1,96 @@
+import { describe, expect, it } from "vitest";
+import { TEST_CLIENT } from "~test/test-clients.js";
+import { TEST_ACCOUNT_A } from "~test/test-wallets.js";
+import { base } from "../../chains/chain-definitions/base.js";
+import { ethereum } from "../../chains/chain-definitions/ethereum.js";
+import { sepolia } from "../../chains/chain-definitions/sepolia.js";
+import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
+import { convertFiatToCrypto } from "./fiatToCrypto.js";
+
+describe.runIf(process.env.TW_SECRET_KEY)("Pay: fiatToCrypto", () => {
+ it("should convert fiat price to token on Ethereum mainnet", async () => {
+ const data = await convertFiatToCrypto({
+ chain: ethereum,
+ from: "USD",
+ fromAmount: 1,
+ to: NATIVE_TOKEN_ADDRESS,
+ client: TEST_CLIENT,
+ });
+ expect(data.result).toBeDefined();
+ // Should be a number
+ expect(!Number.isNaN(data.result)).toBe(true);
+ // Since eth is around US$3000, 1 USD should be around 0.0003
+ // we give it some safe margin so the test won't be flaky
+ expect(data.result < 0.001).toBe(true);
+ });
+
+ it("should convert fiat price to token on Base mainnet", async () => {
+ const data = await convertFiatToCrypto({
+ chain: base,
+ from: "USD",
+ fromAmount: 1,
+ to: NATIVE_TOKEN_ADDRESS,
+ client: TEST_CLIENT,
+ });
+
+ expect(data.result).toBeDefined();
+ // Should be a number
+ expect(!Number.isNaN(data.result)).toBe(true);
+ // Since eth is around US$3000, 1 USD should be around 0.0003
+ // we give it some safe margin so the test won't be flaky
+ expect(data.result < 0.001).toBe(true);
+ });
+
+ it("should return zero if the fromAmount is zero", async () => {
+ const data = await convertFiatToCrypto({
+ chain: base,
+ from: "USD",
+ fromAmount: 0,
+ to: NATIVE_TOKEN_ADDRESS,
+ client: TEST_CLIENT,
+ });
+ expect(data.result).toBe(0);
+ });
+
+ it("should throw error for testnet chain (because testnets are not supported", async () => {
+ await expect(() =>
+ convertFiatToCrypto({
+ chain: sepolia,
+ to: NATIVE_TOKEN_ADDRESS,
+ fromAmount: 1,
+ from: "USD",
+ client: TEST_CLIENT,
+ }),
+ ).rejects.toThrowError(
+ `Cannot fetch price for a testnet (chainId: ${sepolia.id})`,
+ );
+ });
+
+ it("should throw error if `to` is set to an invalid EVM address", async () => {
+ await expect(() =>
+ convertFiatToCrypto({
+ chain: ethereum,
+ to: "haha",
+ fromAmount: 1,
+ from: "USD",
+ client: TEST_CLIENT,
+ }),
+ ).rejects.toThrowError(
+ "Invalid `to`. Expected a valid EVM contract address",
+ );
+ });
+
+ it("should throw error if `to` is set to a wallet address", async () => {
+ await expect(() =>
+ convertFiatToCrypto({
+ chain: base,
+ to: TEST_ACCOUNT_A.address,
+ fromAmount: 1,
+ from: "USD",
+ client: TEST_CLIENT,
+ }),
+ ).rejects.toThrowError(
+ `Error: ${TEST_ACCOUNT_A.address} on chainId: ${base.id} is not a valid contract address.`,
+ );
+ });
+});
diff --git a/packages/thirdweb/src/pay/convert/fiatToCrypto.ts b/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
new file mode 100644
index 00000000000..46f6af2cea1
--- /dev/null
+++ b/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
@@ -0,0 +1,110 @@
+import type { Address } from "abitype";
+import type { Chain } from "../../chains/types.js";
+import type { ThirdwebClient } from "../../client/client.js";
+import { NATIVE_TOKEN_ADDRESS } from "../../constants/addresses.js";
+import { getBytecode } from "../../contract/actions/get-bytecode.js";
+import { getContract } from "../../contract/contract.js";
+import { isAddress } from "../../utils/address.js";
+import { getClientFetch } from "../../utils/fetch.js";
+import { getPayConvertFiatToCryptoEndpoint } from "../utils/definitions.js";
+
+/**
+ * Props for the `convertFiatToCrypto` function
+ * @buyCrypto
+ */
+export type ConvertFiatToCryptoParams = {
+ client: ThirdwebClient;
+ /**
+ * The fiat symbol. e.g: "USD"
+ * Currently only USD is supported.
+ */
+ from: "USD";
+ /**
+ * The total amount of fiat to convert
+ * e.g: If you want to convert 2 cents to USD, enter `0.02`
+ */
+ fromAmount: number;
+ /**
+ * The token address
+ * For native token, use NATIVE_TOKEN_ADDRESS
+ */
+ to: Address;
+ /**
+ * The chain that the token is deployed to
+ */
+ chain: Chain;
+};
+
+/**
+ * Convert a fiat value to a token.
+ * Currently only USD is supported.
+ * @example
+ * ### Basic usage
+ * ```ts
+ * import { convertFiatToCrypto } from "thirdweb/pay";
+ *
+ * // Convert 2 cents to ETH
+ * const result = await convertFiatToCrypto({
+ * from: "USD",
+ * // the token address. For native token, use NATIVE_TOKEN_ADDRESS
+ * to: "0x...",
+ * // the chain (of the chain where the token belong to)
+ * chain: ethereum,
+ * // 2 cents
+ * fromAmount: 0.02,
+ * });
+ * ```
+ * Result: `0.0000057` (a number)
+ * @buyCrypto
+ */
+export async function convertFiatToCrypto(
+ options: ConvertFiatToCryptoParams,
+): Promise<{ result: number }> {
+ const { client, from, to, chain, fromAmount } = options;
+ if (Number(fromAmount) === 0) {
+ return { result: 0 };
+ }
+ // Testnets just don't work with our current provider(s)
+ if (chain.testnet === true) {
+ throw new Error(`Cannot fetch price for a testnet (chainId: ${chain.id})`);
+ }
+ // Some provider that we are using will return `0` for unsupported token
+ // so we should do some basic input validations before sending the request
+
+ // Make sure it's a valid EVM address
+ if (!isAddress(to)) {
+ throw new Error("Invalid `to`. Expected a valid EVM contract address");
+ }
+ // Make sure it's either a valid contract or a native token
+ if (to.toLowerCase() !== NATIVE_TOKEN_ADDRESS.toLowerCase()) {
+ const bytecode = await getBytecode(
+ getContract({
+ address: to,
+ chain,
+ client,
+ }),
+ ).catch(() => undefined);
+ if (!bytecode || bytecode === "0x") {
+ throw new Error(
+ `Error: ${to} on chainId: ${chain.id} is not a valid contract address.`,
+ );
+ }
+ }
+ const params = {
+ from,
+ to,
+ chainId: String(chain.id),
+ fromAmount: String(fromAmount),
+ };
+ const queryString = new URLSearchParams(params).toString();
+ const url = `${getPayConvertFiatToCryptoEndpoint()}?${queryString}`;
+ const response = await getClientFetch(client)(url);
+ if (!response.ok) {
+ throw new Error(
+ `Failed to convert ${to} value to token (${to}) on chainId: ${chain.id}`,
+ );
+ }
+
+ const data: { result: number } = await response.json();
+ return data;
+}
diff --git a/packages/thirdweb/src/pay/utils/definitions.ts b/packages/thirdweb/src/pay/utils/definitions.ts
index 313cf16094c..f1763343238 100644
--- a/packages/thirdweb/src/pay/utils/definitions.ts
+++ b/packages/thirdweb/src/pay/utils/definitions.ts
@@ -76,3 +76,9 @@ export const getPaySupportedSources = () =>
*/
export const getPayBuyHistoryEndpoint = () =>
`${getPayBaseUrl()}/wallet/history/v1`;
+
+export const getPayConvertFiatToCryptoEndpoint = () =>
+ `${getPayBaseUrl()}/convert/fiat-to-crypto/v1`;
+
+export const getPayConvertCryptoToFiatEndpoint = () =>
+ `${getPayBaseUrl()}/convert/crypto-to-fiat/v1`;
From ebf94a66dc968c21bb74c4ae71b3e8176f298d8e Mon Sep 17 00:00:00 2001
From: kien-ngo
Date: Mon, 2 Dec 2024 11:45:19 +0000
Subject: [PATCH 10/15] [Dashboard] Remove tw-components stuff (1) (#5540)
part of DASH-388
---
.../Inputs/ClaimerSelection.tsx | 14 +++++-----
.../account/components/nfts-owned.tsx | 5 ++--
.../components/create-account-button.tsx | 9 ++++---
.../nfts/[tokenId]/components/airdrop-tab.tsx | 18 +++++--------
.../nfts/[tokenId]/token-id.tsx | 27 +++++++++----------
.../configuration/components/system.tsx | 5 ++--
.../insight/components/BlueprintsExplorer.tsx | 2 --
.../shared/sources-accordion.tsx | 5 ++--
.../devRelEvents/AmbassadorCards.tsx | 3 +--
.../src/core-ui/batch-upload/batch-table.tsx | 9 +++----
apps/dashboard/src/pages/mission.tsx | 12 ++-------
apps/dashboard/src/pages/rpc-edge.tsx | 18 ++++++-------
apps/dashboard/src/pages/sdk.tsx | 13 +++++----
apps/dashboard/src/pages/storage.tsx | 8 +++---
14 files changed, 65 insertions(+), 83 deletions(-)
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/Inputs/ClaimerSelection.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/Inputs/ClaimerSelection.tsx
index bac9835b312..aab2ee3fd38 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/Inputs/ClaimerSelection.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/claim-conditions-form/Inputs/ClaimerSelection.tsx
@@ -1,6 +1,6 @@
+import { Button } from "@/components/ui/button";
import { Box, Flex, Select } from "@chakra-ui/react";
import { UploadIcon } from "lucide-react";
-import { Button, Text } from "tw-components";
import { useClaimConditionsFormContext } from "..";
import { CustomFormControl } from "../common";
@@ -115,13 +115,13 @@ export const ClaimerSelection = () => {
>
{/* disable the "Edit" button when form is disabled, but not when it's a "See" button */}
setOpenIndex(phaseIndex)}
- rightIcon={ }
>
{isAdmin ? "Edit" : "See"} Claimer Snapshot
+
{
}}
ml={2}
>
-
+
●{" "}
{field.snapshot?.length} address
{field.snapshot?.length === 1 ? "" : "es"}
{" "}
in snapshot
-
+
) : (
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/components/nfts-owned.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/components/nfts-owned.tsx
index b7bd92ff6d0..cc577f6e1a7 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/components/nfts-owned.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/account/components/nfts-owned.tsx
@@ -2,7 +2,6 @@
import { useWalletNFTs } from "@3rdweb-sdk/react";
import type { ThirdwebContract } from "thirdweb";
-import { Text } from "tw-components";
import { NFTCards } from "../../_components/NFTCards";
interface NftsOwnedProps {
@@ -35,8 +34,8 @@ export const NftsOwned: React.FC = ({ contract }) => {
trackingCategory="account_nfts_owned"
/>
) : isWalletNFTsLoading ? null : error ? (
- Failed to fetch NFTs for this account: {error}
+ Failed to fetch NFTs for this account: {error}
) : (
- This account doesn't own any NFTs.
+ This account doesn't own any NFTs.
);
};
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx
index 309bc6719a3..cf6626210d4 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/create-account-button.tsx
@@ -1,5 +1,7 @@
"use client";
+import { Button } from "@/components/ui/button";
+import { Card } from "@/components/ui/card";
import { Tooltip } from "@chakra-ui/react";
import { TransactionButton } from "components/buttons/TransactionButton";
import type { ThirdwebContract } from "thirdweb";
@@ -9,7 +11,6 @@ import {
useReadContract,
useSendAndConfirmTransaction,
} from "thirdweb/react";
-import { Button, Card, Text } from "tw-components";
interface CreateAccountButtonProps {
contract: ThirdwebContract;
@@ -51,8 +52,8 @@ export const CreateAccountButton: React.FC = ({
return (
- You can only initialize one account per EOA.
+
+ You can only initialize one account per EOA.
}
bg="transparent"
@@ -62,7 +63,7 @@ export const CreateAccountButton: React.FC = ({
placement="right"
shouldWrapChildren
>
-
+
Account Created
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx
index d9a86ef36b9..d010079b64d 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/[tokenId]/components/airdrop-tab.tsx
@@ -1,5 +1,6 @@
"use client";
+import { Button } from "@/components/ui/button";
import {
Sheet,
SheetContent,
@@ -18,7 +19,6 @@ import type { ThirdwebContract } from "thirdweb";
import { multicall } from "thirdweb/extensions/common";
import { balanceOf, encodeSafeTransferFrom } from "thirdweb/extensions/erc1155";
import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react";
-import { Button, Text } from "tw-components";
import {
type AirdropAddressInput,
AirdropUpload,
@@ -118,12 +118,8 @@ const AirdropTab: React.FC = ({ contract, tokenId }) => {
- }
- >
- Upload addresses
+
+ Upload addresses
@@ -149,18 +145,18 @@ const AirdropTab: React.FC = ({ contract, tokenId }) => {
color={addresses.length === 0 ? "orange.500" : "green.500"}
>
{addresses.length > 0 && (
-
+
● {addresses.length} addresses ready to be
airdropped
-
+
)}
-
+
You can airdrop to a maximum of 250 addresses at a time. If you have
more, please do it in multiple transactions.
-
+
= ({
if (!nft) {
return (
-
+
No NFT found with token ID {tokenId}. Please check the token ID and try
again.
-
+
);
}
@@ -182,7 +181,7 @@ export const TokenIdPage: React.FC = ({
label={
tb.isDisabled ? (
- {tb.disabledText}
+ {tb.disabledText}
) : (
""
@@ -214,7 +213,7 @@ export const TokenIdPage: React.FC = ({
- Token ID
+ Token ID
= ({
{nft.owner && (
<>
- Owner
+ Owner
@@ -236,23 +235,21 @@ export const TokenIdPage: React.FC = ({
>
)}
- Token Standard
+ Token Standard
{nft.type}
{nft.type !== "ERC721" && (
<>
- Supply
+ Supply
-
- {nft.supply.toString()}
-
+ {nft.supply.toLocaleString("en-US")}
>
)}
- Token URI
+ Token URI
= ({
{nft.metadata.image && (
<>
- Media URI
+ Media URI
= ({
{properties ? (
- Attributes
+ Attributes
{Array.isArray(properties) &&
String(properties[0]?.value) !== "undefined" ? (
diff --git a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/configuration/components/system.tsx b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/configuration/components/system.tsx
index 05bce7cb136..f077f07a5ee 100644
--- a/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/configuration/components/system.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/(team)/~/engine/(instance)/[engineId]/configuration/components/system.tsx
@@ -3,7 +3,6 @@ import {
useEngineSystemHealth,
useEngineSystemMetrics,
} from "@3rdweb-sdk/react/hooks/useEngine";
-import { Text } from "tw-components";
interface EngineSystemProps {
instance: EngineInstance;
@@ -17,7 +16,7 @@ export const EngineSystem: React.FC = ({ instance }) => {
}
return (
-
+
Version: {healthQuery.data.engineVersion ?? "..."}
Enabled: {healthQuery.data.features?.join(", ")}
@@ -25,6 +24,6 @@ export const EngineSystem: React.FC = ({ instance }) => {
CPU: {metricsQuery.data?.data?.cpu?.toFixed(2) ?? "..."}%
Memory: {metricsQuery.data?.data?.memory?.toFixed(0) ?? "..."}MB
-
+
);
};
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/components/BlueprintsExplorer.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/components/BlueprintsExplorer.tsx
index 1c4ff6582ef..578e1893d4e 100644
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/components/BlueprintsExplorer.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/insight/components/BlueprintsExplorer.tsx
@@ -1,7 +1,5 @@
"use client";
-import {} from "@/components/ui/dropdown-menu";
-import {} from "@/components/ui/select";
import { Layers3 } from "lucide-react";
import Link from "next/link";
diff --git a/apps/dashboard/src/components/contract-components/shared/sources-accordion.tsx b/apps/dashboard/src/components/contract-components/shared/sources-accordion.tsx
index 641b24f2671..bd5bcab863c 100644
--- a/apps/dashboard/src/components/contract-components/shared/sources-accordion.tsx
+++ b/apps/dashboard/src/components/contract-components/shared/sources-accordion.tsx
@@ -7,7 +7,6 @@ import {
AccordionPanel,
} from "@chakra-ui/react";
import type { Abi } from "abitype";
-import { Heading } from "tw-components";
import type { SourceFile } from "../types";
interface SourcesAccordionProps {
@@ -33,7 +32,7 @@ export const SourcesAccordion: React.FC = ({
{({ isExpanded }) => (
<>
- ABI
+ ABI
@@ -58,7 +57,7 @@ export const SourcesAccordion: React.FC = ({
{({ isExpanded }) => (
<>
- {signature.filename}
+ {signature.filename}
diff --git a/apps/dashboard/src/components/devRelEvents/AmbassadorCards.tsx b/apps/dashboard/src/components/devRelEvents/AmbassadorCards.tsx
index 2d00d7e76b4..26273a2d1da 100644
--- a/apps/dashboard/src/components/devRelEvents/AmbassadorCards.tsx
+++ b/apps/dashboard/src/components/devRelEvents/AmbassadorCards.tsx
@@ -1,7 +1,6 @@
import { Flex } from "@chakra-ui/react";
import { ChakraNextImage } from "components/Image";
import type { StaticImageData } from "next/image";
-import { Text } from "tw-components";
import type { ComponentWithChildren } from "types/component-with-children";
interface AmbassadorProps {
@@ -41,7 +40,7 @@ export const AmbassadorCard: ComponentWithChildren = ({
lineHeight={1.6}
textAlign="center"
>
- {children}
+ {children}
);
diff --git a/apps/dashboard/src/core-ui/batch-upload/batch-table.tsx b/apps/dashboard/src/core-ui/batch-upload/batch-table.tsx
index 6c9ed3b5769..a9c75bca356 100644
--- a/apps/dashboard/src/core-ui/batch-upload/batch-table.tsx
+++ b/apps/dashboard/src/core-ui/batch-upload/batch-table.tsx
@@ -28,7 +28,6 @@ import {
import { useMemo } from "react";
import { type Column, usePagination, useTable } from "react-table";
import type { NFTInput } from "thirdweb/utils";
-import { Text } from "tw-components";
const FileImage: React.FC = ({ src, ...props }) => {
const img = useImageFileOrUrl(
@@ -174,9 +173,9 @@ export const BatchTable: React.FC = ({
{headerGroup.headers.map((column, i) => (
// biome-ignore lint/suspicious/noArrayIndexKey: FIXME
-
+
{column.render("Header")}
-
+
))}
@@ -227,10 +226,10 @@ export const BatchTable: React.FC = ({
icon={ }
onClick={() => previousPage()}
/>
-
+
Page {pageIndex + 1} of{" "}
{pageOptions.length}
-
+
{
maxW="80%"
/>
-
+
solves for both.
-
+
diff --git a/apps/dashboard/src/pages/rpc-edge.tsx b/apps/dashboard/src/pages/rpc-edge.tsx
index 15dabb6b1ac..a899464cd47 100644
--- a/apps/dashboard/src/pages/rpc-edge.tsx
+++ b/apps/dashboard/src/pages/rpc-edge.tsx
@@ -1,3 +1,4 @@
+import { Card } from "@/components/ui/card";
import { Container, Flex } from "@chakra-ui/react";
import { LandingEndCTA } from "components/landing-pages/end-cta";
import { LandingGridSection } from "components/landing-pages/grid-section";
@@ -8,7 +9,6 @@ import { LandingLayout } from "components/landing-pages/layout";
import { LandingSectionHeading } from "components/landing-pages/section-heading";
import { getAbsoluteUrl } from "lib/vercel-utils";
import { PageId } from "page-id";
-import { Card } from "tw-components";
import type { ThirdwebNextPage } from "utils/types";
const TRACKING_CATEGORY = "rpc-edge-landing";
@@ -95,31 +95,31 @@ const RPCEdgeLanding: ThirdwebNextPage = () => {
/>
-
+
-
+
-
+
-
+
-
+
{
/>
}
>
-
+
-
+
-
+
{
Interact with your contracts from your app in the programming
language that you’re familiar with our{" "}
React
,{" "}
ReactNative
,{" "}
TypeScript
,{" "}
-
+
Unity
{" "}
SDKs.
diff --git a/apps/dashboard/src/pages/storage.tsx b/apps/dashboard/src/pages/storage.tsx
index da7316e8a7d..c94f3b547e3 100644
--- a/apps/dashboard/src/pages/storage.tsx
+++ b/apps/dashboard/src/pages/storage.tsx
@@ -1,3 +1,4 @@
+import { Card } from "@/components/ui/card";
import { Container, Flex } from "@chakra-ui/react";
import { LandingEndCTA } from "components/landing-pages/end-cta";
import { LandingGridSection } from "components/landing-pages/grid-section";
@@ -7,7 +8,6 @@ import { LandingIconSectionItem } from "components/landing-pages/icon-section-it
import { LandingLayout } from "components/landing-pages/layout";
import { getAbsoluteUrl } from "lib/vercel-utils";
import { PageId } from "page-id";
-import { Card } from "tw-components";
import type { ThirdwebNextPage } from "utils/types";
const TRACKING_CATEGORY = "storage-landing";
@@ -108,21 +108,21 @@ const InteractLanding: ThirdwebNextPage = () => {
/>
-
+
-
+
-
+
Date: Mon, 2 Dec 2024 13:33:43 +0000
Subject: [PATCH 11/15] [Dashboard] Remove chakra-ui from snapshot-upload
(#5543)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
part of DASH-388
---
## PR-Codex overview
This PR focuses on updating the return types in the `convertFiatToCrypto` and `convertCryptoToFiat` functions to return objects instead of plain numbers. Additionally, it modifies the `SnapshotUpload` component to replace `Link` elements with `a` tags and adjusts the layout for better styling.
### Detailed summary
- Changed return type of `convertFiatToCrypto` to `{ result: 0.0000057 }`.
- Changed return type of `convertCryptoToFiat` to `{ result: 3404.11 }`.
- Replaced `Link` components with `a` tags in `SnapshotUpload`.
- Adjusted layout and styling in `SnapshotUpload` for improved design.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
---
.../claim-conditions/snapshot-upload.tsx | 66 +++++++------------
.../thirdweb/src/pay/convert/cryptoToFiat.ts | 2 +-
.../thirdweb/src/pay/convert/fiatToCrypto.ts | 2 +-
3 files changed, 27 insertions(+), 43 deletions(-)
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx
index 342e711acfb..853ae7956a0 100644
--- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx
+++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_components/claim-conditions/snapshot-upload.tsx
@@ -9,7 +9,6 @@ import {
} from "@/components/ui/sheet";
import { ToolTipLabel } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
-import { Box, Flex, Link } from "@chakra-ui/react";
import { useCsvUpload } from "hooks/useCsvUpload";
import { CircleAlertIcon, DownloadIcon, UploadIcon } from "lucide-react";
import { type Dispatch, type SetStateAction, useRef } from "react";
@@ -159,13 +158,7 @@ export const SnapshotUpload: React.FC = ({
columns={columns}
/>
) : (
-
+
= ({
addresses and their
.
(amount each wallet is allowed to claim)
-
Example
snapshot
-
+
You may optionally add and
@@ -219,14 +212,14 @@ export const SnapshotUpload: React.FC = ({
This lets you override the currency and price you would
like to charge per wallet you specified
-
Example
snapshot
-
+
>
) : (
@@ -235,10 +228,14 @@ export const SnapshotUpload: React.FC
= ({
Files must contain one .csv file with a list of
addresses.
-
+
Example
snapshot
-
+
You may optionally add a{" "}
@@ -247,14 +244,14 @@ export const SnapshotUpload: React.FC = ({
claim) If not specified, the default value is the one
you have set on your claim phase.
-
Example
snapshot
-
+
You may optionally add and
@@ -266,14 +263,14 @@ export const SnapshotUpload: React.FC = ({
define a price override.
-
Example
snapshot
-
+
>
)}
@@ -287,25 +284,12 @@ export const SnapshotUpload: React.FC = ({
-
+
)}
-
-
+
+
{!isDisabled && (
-
+
= ({
Next
)}
-
+
)}
-
+
diff --git a/packages/thirdweb/src/pay/convert/cryptoToFiat.ts b/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
index cf98821b254..b046fedd3f7 100644
--- a/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
+++ b/packages/thirdweb/src/pay/convert/cryptoToFiat.ts
@@ -51,7 +51,7 @@ export type ConvertCryptoToFiatParams = {
* fromAmount: 1,
* });
*
- * // Result: 3404.11
+ * // Result: `{ result: 3404.11 }`
* ```
* @buyCrypto
* @returns a number representing the price (in selected fiat) of "x" token, with "x" being the `fromAmount`.
diff --git a/packages/thirdweb/src/pay/convert/fiatToCrypto.ts b/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
index 46f6af2cea1..7d6e6083c7f 100644
--- a/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
+++ b/packages/thirdweb/src/pay/convert/fiatToCrypto.ts
@@ -54,7 +54,7 @@ export type ConvertFiatToCryptoParams = {
* fromAmount: 0.02,
* });
* ```
- * Result: `0.0000057` (a number)
+ * Result: `{ result: 0.0000057 }`
* @buyCrypto
*/
export async function convertFiatToCrypto(
From e838713b2811da536b13ed64617c0fa8b5b3c956 Mon Sep 17 00:00:00 2001
From: gregfromstl
Date: Mon, 2 Dec 2024 14:10:20 +0000
Subject: [PATCH 12/15] refactor(sdk): serialize-transaction to use ox
---
.../actions/gasless/providers/biconomy.ts | 5 +-
.../actions/gasless/providers/engine.ts | 4 +-
.../actions/gasless/providers/openzeppelin.ts | 4 +-
.../gasless/send-gasless-transaction.ts | 4 +-
.../actions/sign-transaction.test.ts | 23 +++----
.../transaction/actions/sign-transaction.ts | 20 +++---
.../actions/to-serializable-transaction.ts | 4 +-
.../actions/zksync/getEip721Domain.ts | 4 +-
.../src/transaction/serialize-transaction.ts | 49 ++++++++++++---
.../utils/any-evm/keyless-transaction.test.ts | 63 +++++++++++++++++++
.../src/utils/any-evm/keyless-transaction.ts | 57 ++++++++++++-----
.../thirdweb/src/wallets/interfaces/wallet.ts | 3 +-
packages/thirdweb/src/wallets/private-key.ts | 16 +++--
.../thirdweb/src/wallets/smart/lib/bundler.ts | 7 ++-
14 files changed, 193 insertions(+), 70 deletions(-)
create mode 100644 packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts
diff --git a/packages/thirdweb/src/transaction/actions/gasless/providers/biconomy.ts b/packages/thirdweb/src/transaction/actions/gasless/providers/biconomy.ts
index adbf0e4a100..0c9442e4698 100644
--- a/packages/thirdweb/src/transaction/actions/gasless/providers/biconomy.ts
+++ b/packages/thirdweb/src/transaction/actions/gasless/providers/biconomy.ts
@@ -1,5 +1,5 @@
import type { Address } from "abitype";
-import { type TransactionSerializable, encodeAbiParameters } from "viem";
+import { encodeAbiParameters } from "viem";
import { ZERO_ADDRESS } from "../../../../constants/addresses.js";
import { getContract } from "../../../../contract/contract.js";
import { isHex } from "../../../../utils/encoding/helpers/is-hex.js";
@@ -8,6 +8,7 @@ import { stringify } from "../../../../utils/json.js";
import type { Account } from "../../../../wallets/interfaces/wallet.js";
import type { PreparedTransaction } from "../../../prepare-transaction.js";
import { readContract } from "../../../read-contract.js";
+import type { SerializableTransaction } from "../../../serialize-transaction.js";
import type { WaitForReceiptOptions } from "../../wait-for-tx-receipt.js";
/**
@@ -27,7 +28,7 @@ type SendBiconomyTransactionOptions = {
// TODO: update this to `Transaction<"prepared">` once the type is available to ensure only prepared transactions are accepted
// biome-ignore lint/suspicious/noExplicitAny: library function that accepts any prepared transaction type
transaction: PreparedTransaction;
- serializableTransaction: TransactionSerializable;
+ serializableTransaction: SerializableTransaction;
gasless: BiconomyOptions;
};
diff --git a/packages/thirdweb/src/transaction/actions/gasless/providers/engine.ts b/packages/thirdweb/src/transaction/actions/gasless/providers/engine.ts
index 56796a2e872..ab0f115741e 100644
--- a/packages/thirdweb/src/transaction/actions/gasless/providers/engine.ts
+++ b/packages/thirdweb/src/transaction/actions/gasless/providers/engine.ts
@@ -1,10 +1,10 @@
import type { Address } from "abitype";
-import type { TransactionSerializable } from "viem";
import { getContract } from "../../../../contract/contract.js";
import { stringify } from "../../../../utils/json.js";
import type { Account } from "../../../../wallets/interfaces/wallet.js";
import type { PreparedTransaction } from "../../../prepare-transaction.js";
import { readContract } from "../../../read-contract.js";
+import type { SerializableTransaction } from "../../../serialize-transaction.js";
import {
type WaitForReceiptOptions,
waitForReceipt,
@@ -28,7 +28,7 @@ type SendengineTransactionOptions = {
// TODO: update this to `Transaction<"prepared">` once the type is available to ensure only prepared transactions are accepted
// biome-ignore lint/suspicious/noExplicitAny: library function that accepts any prepared transaction type
transaction: PreparedTransaction;
- serializableTransaction: TransactionSerializable;
+ serializableTransaction: SerializableTransaction;
gasless: EngineOptions;
};
diff --git a/packages/thirdweb/src/transaction/actions/gasless/providers/openzeppelin.ts b/packages/thirdweb/src/transaction/actions/gasless/providers/openzeppelin.ts
index 3143e80a1ad..d643fd4f1f2 100644
--- a/packages/thirdweb/src/transaction/actions/gasless/providers/openzeppelin.ts
+++ b/packages/thirdweb/src/transaction/actions/gasless/providers/openzeppelin.ts
@@ -1,11 +1,11 @@
import type { Address } from "abitype";
-import type { TransactionSerializable } from "viem";
import { getContract } from "../../../../contract/contract.js";
import { isHex } from "../../../../utils/encoding/helpers/is-hex.js";
import { stringify } from "../../../../utils/json.js";
import type { Account } from "../../../../wallets/interfaces/wallet.js";
import type { PreparedTransaction } from "../../../prepare-transaction.js";
import { readContract } from "../../../read-contract.js";
+import type { SerializableTransaction } from "../../../serialize-transaction.js";
import type { WaitForReceiptOptions } from "../../wait-for-tx-receipt.js";
/**
@@ -26,7 +26,7 @@ type SendOpenZeppelinTransactionOptions = {
// TODO: update this to `Transaction<"prepared">` once the type is available to ensure only prepared transactions are accepted
// biome-ignore lint/suspicious/noExplicitAny: library function that accepts any prepared transaction type
transaction: PreparedTransaction;
- serializableTransaction: TransactionSerializable;
+ serializableTransaction: SerializableTransaction;
gasless: OpenZeppelinOptions;
};
diff --git a/packages/thirdweb/src/transaction/actions/gasless/send-gasless-transaction.ts b/packages/thirdweb/src/transaction/actions/gasless/send-gasless-transaction.ts
index f4f33371648..e43187db346 100644
--- a/packages/thirdweb/src/transaction/actions/gasless/send-gasless-transaction.ts
+++ b/packages/thirdweb/src/transaction/actions/gasless/send-gasless-transaction.ts
@@ -1,6 +1,6 @@
-import type { TransactionSerializable } from "viem";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import type { PreparedTransaction } from "../../prepare-transaction.js";
+import type { SerializableTransaction } from "../../serialize-transaction.js";
import { addTransactionToStore } from "../../transaction-store.js";
import type { WaitForReceiptOptions } from "../wait-for-tx-receipt.js";
import type { GaslessOptions } from "./types.js";
@@ -10,7 +10,7 @@ type SendGaslessTransactionOptions = {
// TODO: update this to `Transaction<"prepared">` once the type is available to ensure only prepared transactions are accepted
// biome-ignore lint/suspicious/noExplicitAny: library function that accepts any prepared transaction type
transaction: PreparedTransaction;
- serializableTransaction: TransactionSerializable;
+ serializableTransaction: SerializableTransaction;
gasless: GaslessOptions;
};
diff --git a/packages/thirdweb/src/transaction/actions/sign-transaction.test.ts b/packages/thirdweb/src/transaction/actions/sign-transaction.test.ts
index af4cce65976..257d96aeb73 100644
--- a/packages/thirdweb/src/transaction/actions/sign-transaction.test.ts
+++ b/packages/thirdweb/src/transaction/actions/sign-transaction.test.ts
@@ -1,27 +1,20 @@
-import {
- type TransactionSerializable,
- type TransactionSerializableBase,
- type TransactionSerializableEIP1559,
- type TransactionSerializableEIP2930,
- type TransactionSerializableLegacy,
- zeroAddress,
-} from "viem";
import { describe, expect, test } from "vitest";
import { ANVIL_PKEY_A } from "~test/test-wallets.js";
+import { ZERO_ADDRESS } from "../../constants/addresses.js";
import { fromGwei } from "../../utils/units.js";
import { signTransaction } from "./sign-transaction.js";
const BASE_TRANSACTION = {
gas: 21000n,
nonce: 785,
-} satisfies TransactionSerializableBase;
+} as const;
describe("eip1559", () => {
const BASE_EIP1559_TRANSACTION = {
...BASE_TRANSACTION,
chainId: 1,
type: "eip1559",
- } as const satisfies TransactionSerializableEIP1559;
+ } as const;
test("default", () => {
const signature = signTransaction({
@@ -69,7 +62,7 @@ describe("eip1559", () => {
...BASE_EIP1559_TRANSACTION,
accessList: [
{
- address: zeroAddress,
+ address: ZERO_ADDRESS,
storageKeys: [
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
@@ -105,7 +98,7 @@ describe("eip2930", () => {
...BASE_TRANSACTION,
chainId: 1,
type: "eip2930",
- } as const satisfies TransactionSerializable;
+ } as const;
test("default", () => {
const signature = signTransaction({
@@ -124,13 +117,13 @@ describe("eip2930", () => {
gasPrice: fromGwei("2"),
accessList: [
{
- address: zeroAddress,
+ address: ZERO_ADDRESS,
storageKeys: [
"0x0000000000000000000000000000000000000000000000000000000000000000",
],
},
],
- } as TransactionSerializableEIP2930,
+ },
privateKey: ANVIL_PKEY_A,
});
@@ -159,7 +152,7 @@ describe("legacy", () => {
...BASE_TRANSACTION,
gasPrice: fromGwei("2"),
type: "legacy",
- } as const satisfies TransactionSerializableLegacy;
+ } as const;
test("default", () => {
const signature = signTransaction({
diff --git a/packages/thirdweb/src/transaction/actions/sign-transaction.ts b/packages/thirdweb/src/transaction/actions/sign-transaction.ts
index 13605d02e33..efabb96d947 100644
--- a/packages/thirdweb/src/transaction/actions/sign-transaction.ts
+++ b/packages/thirdweb/src/transaction/actions/sign-transaction.ts
@@ -1,11 +1,13 @@
-import type { TransactionSerializable } from "viem";
+import * as ox__Hash from "ox/Hash";
+import * as ox__Secp256k1 from "ox/Secp256k1";
import type { Hex } from "../../utils/encoding/hex.js";
-import { keccak256 } from "../../utils/hashing/keccak256.js";
-import { sign } from "../../utils/signatures/sign.js";
-import { serializeTransaction } from "../serialize-transaction.js";
+import {
+ type SerializableTransaction,
+ serializeTransaction,
+} from "../serialize-transaction.js";
export type SignTransactionOptions = {
- transaction: TransactionSerializable;
+ transaction: SerializableTransaction;
privateKey: Hex;
// TODO: Add optional custom serializer here
};
@@ -32,14 +34,10 @@ export function signTransaction({
transaction,
privateKey,
}: SignTransactionOptions): Hex {
- if (transaction.type === "eip4844") {
- transaction = { ...transaction, sidecars: false };
- }
-
const serializedTransaction = serializeTransaction({ transaction });
- const signature = sign({
- hash: keccak256(serializedTransaction),
+ const signature = ox__Secp256k1.sign({
+ payload: ox__Hash.keccak256(serializedTransaction),
privateKey: privateKey,
});
return serializeTransaction({
diff --git a/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts b/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts
index 29e2ab5598a..073b11d28b4 100644
--- a/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts
+++ b/packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts
@@ -1,10 +1,10 @@
-import type { TransactionSerializable } from "viem";
import { getGasOverridesForTransaction } from "../../gas/fee-data.js";
import { getRpcClient } from "../../rpc/rpc.js";
import { getAddress } from "../../utils/address.js";
import { isZkSyncChain } from "../../utils/any-evm/zksync/isZkSyncChain.js";
import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js";
import type { PreparedTransaction } from "../prepare-transaction.js";
+import type { SerializableTransaction } from "../serialize-transaction.js";
import { encode } from "./encode.js";
import { estimateGas } from "./estimate-gas.js";
@@ -112,5 +112,5 @@ export async function toSerializableTransaction(
accessList,
value,
...feeData,
- } satisfies TransactionSerializable;
+ } satisfies SerializableTransaction;
}
diff --git a/packages/thirdweb/src/transaction/actions/zksync/getEip721Domain.ts b/packages/thirdweb/src/transaction/actions/zksync/getEip721Domain.ts
index 60b30c117de..bb548218315 100644
--- a/packages/thirdweb/src/transaction/actions/zksync/getEip721Domain.ts
+++ b/packages/thirdweb/src/transaction/actions/zksync/getEip721Domain.ts
@@ -1,4 +1,3 @@
-import type { TransactionSerializable } from "viem";
import { hashBytecode } from "viem/zksync";
import type { Address } from "../../../utils/address.js";
import { toHex } from "../../../utils/encoding/hex.js";
@@ -6,8 +5,9 @@ import type {
EIP712SerializedTransaction,
EIP712TransactionOptions,
} from "../../prepare-transaction.js";
+import type { SerializableTransaction } from "../../serialize-transaction.js";
-export type EIP721TransactionSerializable = TransactionSerializable & {
+export type EIP721TransactionSerializable = SerializableTransaction & {
from: Address;
} & EIP712TransactionOptions;
export const gasPerPubdataDefault = 50000n;
diff --git a/packages/thirdweb/src/transaction/serialize-transaction.ts b/packages/thirdweb/src/transaction/serialize-transaction.ts
index 686074d49f1..93a2e17b2a0 100644
--- a/packages/thirdweb/src/transaction/serialize-transaction.ts
+++ b/packages/thirdweb/src/transaction/serialize-transaction.ts
@@ -5,12 +5,12 @@ import * as ox__TransactionEnvelopeEip2930 from "ox/TransactionEnvelopeEip2930";
import * as ox__TransactionEnvelopeLegacy from "ox/TransactionEnvelopeLegacy";
import type { Hex } from "../utils/encoding/hex.js";
-type SerializableTransaction = {
+export type SerializableTransaction = {
type?: string | undefined;
- r?: Hex;
- s?: Hex;
- v?: bigint;
- yParity?: number;
+ r?: Hex | bigint;
+ s?: Hex | bigint;
+ v?: bigint | number;
+ yParity?: bigint | number;
accessList?:
| ox__TransactionEnvelopeEip2930.TransactionEnvelopeEip2930["accessList"]
| undefined;
@@ -22,12 +22,16 @@ type SerializableTransaction = {
to?: string | null | undefined; // Must allow null for backwards compatibility
nonce?: number | bigint | undefined;
value?: bigint | undefined;
+ gas?: bigint | undefined;
gasLimit?: bigint | undefined;
};
export type SerializeTransactionOptions = {
transaction: SerializableTransaction;
- signature?: ox__Signature.Signature | undefined;
+ signature?:
+ | ox__Signature.Signature
+ | ox__Signature.Legacy
+ | undefined;
};
/**
@@ -59,7 +63,26 @@ export function serializeTransaction(
// This is to maintain compatibility with our old interface (including the signature in the transaction object)
const signature = (() => {
- if (options.signature) return options.signature;
+ if (options.signature) {
+ if (
+ "v" in options.signature &&
+ typeof options.signature.v !== "undefined"
+ ) {
+ return ox__Signature.fromLegacy({
+ r: ox__Hex.toBigInt(options.signature.r),
+ s: ox__Hex.toBigInt(options.signature.s),
+ v: Number(options.signature.v),
+ });
+ }
+
+ return {
+ r: ox__Hex.toBigInt(options.signature.r),
+ s: ox__Hex.toBigInt(options.signature.s),
+ // We force the Signature type here because we filter for legacy type above
+ yParity: (options.signature as unknown as ox__Signature.Signature)
+ .yParity,
+ };
+ }
if (
typeof transaction.v === "undefined" &&
typeof transaction.yParity === "undefined"
@@ -72,13 +95,19 @@ export function serializeTransaction(
}
return {
- r: ox__Hex.toBigInt(transaction.r),
- s: ox__Hex.toBigInt(transaction.s),
+ r:
+ typeof transaction.r === "bigint"
+ ? transaction.r
+ : ox__Hex.toBigInt(transaction.r),
+ s:
+ typeof transaction.s === "bigint"
+ ? transaction.s
+ : ox__Hex.toBigInt(transaction.s),
yParity:
typeof transaction.v !== "undefined" &&
typeof transaction.yParity === "undefined"
? ox__Signature.vToYParity(Number(transaction.v))
- : (transaction.yParity as number),
+ : Number(transaction.yParity),
};
})();
diff --git a/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts b/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts
new file mode 100644
index 00000000000..dd2fdbb29b0
--- /dev/null
+++ b/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts
@@ -0,0 +1,63 @@
+import * as ox__Hash from "ox/Hash";
+import * as ox__Hex from "ox/Hex";
+import * as ox__Signature from "ox/Signature";
+import { recoverAddress } from "viem";
+import { describe, expect, it } from "vitest";
+import { serializeTransaction } from "../../transaction/serialize-transaction.js";
+import { getKeylessTransaction } from "./keyless-transaction.js";
+
+describe("getKeylessTransaction", () => {
+ const mockTransaction = {
+ to: "0x1234567890123456789012345678901234567890",
+ value: 1000n,
+ chainId: 1,
+ gasPrice: 10n,
+ };
+
+ const mockSignature = {
+ r: "0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
+ s: "0x60fdd29ff912ce880cd3edaf9f932dc61d3dae823ea77e0323f94adb9f6a72fe",
+ v: 27n,
+ } as const;
+
+ it("should return the correct signer address and serialized transaction", async () => {
+ const serializedTransaction = serializeTransaction({
+ transaction: mockTransaction,
+ });
+
+ const hash = ox__Hash.keccak256(serializedTransaction);
+ const expectedAddress = await recoverAddress({
+ hash,
+ signature: ox__Signature.toHex({
+ r: ox__Hex.toBigInt(mockSignature.r),
+ s: ox__Hex.toBigInt(mockSignature.s),
+ yParity: ox__Signature.vToYParity(Number(mockSignature.v)),
+ }),
+ });
+
+ const result = await getKeylessTransaction({
+ transaction: mockTransaction,
+ signature: mockSignature,
+ });
+
+ expect(result.signerAddress).toBe(expectedAddress);
+ expect(result.transaction).toBe(
+ serializeTransaction({
+ transaction: mockTransaction,
+ signature: mockSignature,
+ }),
+ );
+ });
+
+ it("should throw an error if the transaction is invalid", async () => {
+ const invalidTransaction = { ...mockTransaction, value: "invalid" };
+
+ await expect(
+ getKeylessTransaction({
+ // biome-ignore lint/suspicious/noExplicitAny: Testing invalid data
+ transaction: invalidTransaction as any,
+ signature: mockSignature,
+ }),
+ ).rejects.toThrow();
+ });
+});
diff --git a/packages/thirdweb/src/utils/any-evm/keyless-transaction.ts b/packages/thirdweb/src/utils/any-evm/keyless-transaction.ts
index fb94a48650b..717adbfd13f 100644
--- a/packages/thirdweb/src/utils/any-evm/keyless-transaction.ts
+++ b/packages/thirdweb/src/utils/any-evm/keyless-transaction.ts
@@ -1,15 +1,18 @@
+import * as ox__Hash from "ox/Hash";
+import * as ox__Hex from "ox/Hex";
+import * as ox__Signature from "ox/Signature";
+import { recoverAddress } from "viem";
import {
- type Signature,
- type TransactionSerializable,
- recoverAddress,
+ type SerializableTransaction,
serializeTransaction,
- signatureToHex,
-} from "viem";
-import { keccak256 } from "../hashing/keccak256.js";
+} from "../../transaction/serialize-transaction.js";
+import type { Hex } from "../encoding/hex.js";
type GetKeylessTransactionOptions = {
- transaction: TransactionSerializable;
- signature: Signature;
+ transaction: SerializableTransaction;
+ signature:
+ | ox__Signature.Signature
+ | ox__Signature.Legacy;
};
/**
@@ -23,20 +26,46 @@ export async function getKeylessTransaction(
options: GetKeylessTransactionOptions,
) {
// 1. Create serialized txn string
- const hash = keccak256(serializeTransaction(options.transaction));
+ const hash = ox__Hash.keccak256(
+ serializeTransaction({ transaction: options.transaction }),
+ );
+
+ const yParity = (() => {
+ if (
+ "yParity" in options.signature &&
+ typeof options.signature.yParity !== "undefined"
+ ) {
+ return options.signature.yParity;
+ }
+
+ if (
+ "v" in options.signature &&
+ typeof options.signature.v !== "undefined"
+ ) {
+ return ox__Signature.vToYParity(Number(options.signature.v));
+ }
+
+ throw new Error(
+ "Invalid recovered signature provided with transaction, missing v or yParity",
+ );
+ })();
// 2. Determine signer address from custom signature + txn
const address = await recoverAddress({
hash,
- signature: signatureToHex(options.signature),
+ signature: ox__Signature.toHex({
+ r: ox__Hex.toBigInt(options.signature.r),
+ s: ox__Hex.toBigInt(options.signature.s),
+ yParity,
+ }),
});
// 3. Create the signed serialized txn string.
// To be sent directly to the chain using a provider.
- const transaction = serializeTransaction(
- options.transaction,
- options.signature,
- );
+ const transaction = serializeTransaction({
+ transaction: options.transaction,
+ signature: options.signature,
+ });
return {
signerAddress: address,
diff --git a/packages/thirdweb/src/wallets/interfaces/wallet.ts b/packages/thirdweb/src/wallets/interfaces/wallet.ts
index 76d17e30be9..77630ce2a09 100644
--- a/packages/thirdweb/src/wallets/interfaces/wallet.ts
+++ b/packages/thirdweb/src/wallets/interfaces/wallet.ts
@@ -6,6 +6,7 @@ import type {
EIP712TransactionOptions,
PreparedTransaction,
} from "../../transaction/prepare-transaction.js";
+import type { SerializableTransaction } from "../../transaction/serialize-transaction.js";
import type { SendTransactionResult } from "../../transaction/types.js";
import type { WalletEmitter } from "../wallet-emitter.js";
import type {
@@ -15,7 +16,7 @@ import type {
WalletId,
} from "../wallet-types.js";
-export type SendTransactionOption = TransactionSerializable & {
+export type SendTransactionOption = SerializableTransaction & {
chainId: number;
eip712?: EIP712TransactionOptions;
};
diff --git a/packages/thirdweb/src/wallets/private-key.ts b/packages/thirdweb/src/wallets/private-key.ts
index 101c7326150..05e0ecc6e61 100644
--- a/packages/thirdweb/src/wallets/private-key.ts
+++ b/packages/thirdweb/src/wallets/private-key.ts
@@ -1,15 +1,16 @@
import { secp256k1 } from "@noble/curves/secp256k1";
import type * as ox__TypedData from "ox/TypedData";
-import type { SignableMessage, TransactionSerializable } from "viem";
import { publicKeyToAddress } from "viem/utils";
import { getCachedChain } from "../chains/utils.js";
import type { ThirdwebClient } from "../client/client.js";
import { eth_sendRawTransaction } from "../rpc/actions/eth_sendRawTransaction.js";
import { getRpcClient } from "../rpc/rpc.js";
import { signTransaction } from "../transaction/actions/sign-transaction.js";
+import type { SerializableTransaction } from "../transaction/serialize-transaction.js";
import { type Hex, toHex } from "../utils/encoding/hex.js";
import { signMessage } from "../utils/signatures/sign-message.js";
import { signTypedData } from "../utils/signatures/sign-typed-data.js";
+import type { Prettify } from "../utils/type-utils.js";
import type { Account } from "./interfaces/wallet.js";
export type PrivateKeyToAccountOptions = {
@@ -42,6 +43,13 @@ export type PrivateKeyToAccountOptions = {
privateKey: string;
};
+type Message = Prettify<
+ | string
+ | {
+ raw: Hex | Uint8Array;
+ }
+>;
+
/**
* Get an `Account` object from a private key.
* @param options - The options for `privateKeyToAccount`
@@ -70,7 +78,7 @@ export function privateKeyToAccount(
const account = {
address,
sendTransaction: async (
- tx: TransactionSerializable & { chainId: number },
+ tx: SerializableTransaction & { chainId: number },
) => {
const rpcRequest = getRpcClient({
client: client,
@@ -88,7 +96,7 @@ export function privateKeyToAccount(
transactionHash,
};
},
- signMessage: async ({ message }: { message: SignableMessage }) => {
+ signMessage: async ({ message }: { message: Message }) => {
return signMessage({
message,
privateKey,
@@ -105,7 +113,7 @@ export function privateKeyToAccount(
privateKey,
});
},
- signTransaction: async (tx: TransactionSerializable) => {
+ signTransaction: async (tx: SerializableTransaction) => {
return signTransaction({
transaction: tx,
privateKey,
diff --git a/packages/thirdweb/src/wallets/smart/lib/bundler.ts b/packages/thirdweb/src/wallets/smart/lib/bundler.ts
index a60f0bf895e..ff5e045c0ce 100644
--- a/packages/thirdweb/src/wallets/smart/lib/bundler.ts
+++ b/packages/thirdweb/src/wallets/smart/lib/bundler.ts
@@ -1,7 +1,8 @@
-import { type TransactionSerializable, decodeErrorResult } from "viem";
+import { decodeErrorResult } from "viem";
import { parseEventLogs } from "../../../event/actions/parse-logs.js";
import { userOperationRevertReasonEvent } from "../../../extensions/erc4337/__generated__/IEntryPoint/events/UserOperationRevertReason.js";
import { postOpRevertReasonEvent } from "../../../extensions/erc4337/__generated__/IEntryPoint_v07/events/PostOpRevertReason.js";
+import type { SerializableTransaction } from "../../../transaction/serialize-transaction.js";
import type { TransactionReceipt } from "../../../transaction/types.js";
import { type Hex, hexToBigInt } from "../../../utils/encoding/hex.js";
import { getClientFetch } from "../../../utils/fetch.js";
@@ -217,7 +218,7 @@ export async function getUserOpReceiptRaw(
*/
export async function getZkPaymasterData(args: {
options: BundlerOptions;
- transaction: TransactionSerializable;
+ transaction: SerializableTransaction;
}): Promise {
const res = await sendBundlerRequest({
options: args.options,
@@ -233,7 +234,7 @@ export async function getZkPaymasterData(args: {
export async function broadcastZkTransaction(args: {
options: BundlerOptions;
- transaction: TransactionSerializable;
+ transaction: SerializableTransaction;
signedTransaction: Hex;
}): Promise<{ transactionHash: Hex }> {
const res = await sendBundlerRequest({
From 9feaeedba23af8cffb6a26a8220566811635d26a Mon Sep 17 00:00:00 2001
From: gregfromstl
Date: Mon, 2 Dec 2024 14:16:57 +0000
Subject: [PATCH 13/15] fix build
---
packages/thirdweb/src/adapters/ethers5.ts | 5 +++--
packages/thirdweb/src/adapters/ethers6.ts | 5 +++--
packages/thirdweb/src/wallets/interfaces/wallet.ts | 4 ++--
3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/packages/thirdweb/src/adapters/ethers5.ts b/packages/thirdweb/src/adapters/ethers5.ts
index c4ca2d6e5cf..ee653207f72 100644
--- a/packages/thirdweb/src/adapters/ethers5.ts
+++ b/packages/thirdweb/src/adapters/ethers5.ts
@@ -2,7 +2,7 @@ import type { Abi } from "abitype";
import * as universalethers from "ethers";
import type * as ethers5 from "ethers5";
import type * as ethers6 from "ethers6";
-import type { AccessList, Hex, TransactionSerializable } from "viem";
+import type { AccessList, Hex } from "viem";
import type { Chain } from "../chains/types.js";
import { getRpcUrlForChain } from "../chains/utils.js";
import type { ThirdwebClient } from "../client/client.js";
@@ -10,6 +10,7 @@ import { type ThirdwebContract, getContract } from "../contract/contract.js";
import { toSerializableTransaction } from "../transaction/actions/to-serializable-transaction.js";
import { waitForReceipt } from "../transaction/actions/wait-for-tx-receipt.js";
import type { PreparedTransaction } from "../transaction/prepare-transaction.js";
+import type { SerializableTransaction } from "../transaction/serialize-transaction.js";
import { toHex } from "../utils/encoding/hex.js";
import type { Account } from "../wallets/interfaces/wallet.js";
@@ -588,7 +589,7 @@ export async function toEthersSigner(
* @internal
*/
function alignTxToEthers(
- tx: TransactionSerializable,
+ tx: SerializableTransaction,
): ethers5.ethers.utils.Deferrable {
const { to: viemTo, type: viemType, gas, ...rest } = tx;
// massage "to" to fit ethers
diff --git a/packages/thirdweb/src/adapters/ethers6.ts b/packages/thirdweb/src/adapters/ethers6.ts
index d552805ab60..de90941730f 100644
--- a/packages/thirdweb/src/adapters/ethers6.ts
+++ b/packages/thirdweb/src/adapters/ethers6.ts
@@ -2,13 +2,14 @@ import type { Abi } from "abitype";
import * as universalethers from "ethers";
import type * as ethers5 from "ethers5";
import type * as ethers6 from "ethers6";
-import type { AccessList, Hex, TransactionSerializable } from "viem";
+import type { AccessList, Hex } from "viem";
import type { Chain } from "../chains/types.js";
import { getRpcUrlForChain } from "../chains/utils.js";
import type { ThirdwebClient } from "../client/client.js";
import { type ThirdwebContract, getContract } from "../contract/contract.js";
import { toSerializableTransaction } from "../transaction/actions/to-serializable-transaction.js";
import type { PreparedTransaction } from "../transaction/prepare-transaction.js";
+import type { SerializableTransaction } from "../transaction/serialize-transaction.js";
import { toHex } from "../utils/encoding/hex.js";
import { resolvePromisedValue } from "../utils/promise/resolve-promised-value.js";
import type { Account } from "../wallets/interfaces/wallet.js";
@@ -493,7 +494,7 @@ export function toEthersSigner(
* @returns The aligned transaction object.
* @internal
*/
-function alignTxToEthers(tx: TransactionSerializable) {
+function alignTxToEthers(tx: SerializableTransaction) {
const { type: viemType, ...rest } = tx;
// massage "type" to fit ethers
diff --git a/packages/thirdweb/src/wallets/interfaces/wallet.ts b/packages/thirdweb/src/wallets/interfaces/wallet.ts
index 77630ce2a09..a5ac2b8bf11 100644
--- a/packages/thirdweb/src/wallets/interfaces/wallet.ts
+++ b/packages/thirdweb/src/wallets/interfaces/wallet.ts
@@ -1,6 +1,6 @@
import type { Address } from "abitype";
import type * as ox__TypedData from "ox/TypedData";
-import type { Hex, SignableMessage, TransactionSerializable } from "viem";
+import type { Hex, SignableMessage } from "viem";
import type { Chain } from "../../chains/types.js";
import type {
EIP712TransactionOptions,
@@ -218,7 +218,7 @@ export type Account = {
* }
* ```
*/
- signTransaction?: (tx: TransactionSerializable) => Promise;
+ signTransaction?: (tx: SerializableTransaction) => Promise;
/**
* Send the given array of transactions to the blockchain in a single batch
*
From 1f444ba6c118093b86415efd71001f1b149d9e5b Mon Sep 17 00:00:00 2001
From: gregfromstl
Date: Mon, 2 Dec 2024 14:20:38 +0000
Subject: [PATCH 14/15] fix viem test
---
packages/thirdweb/src/adapters/viem.test.ts | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/packages/thirdweb/src/adapters/viem.test.ts b/packages/thirdweb/src/adapters/viem.test.ts
index 935fbdcc16d..03356d2cbe3 100644
--- a/packages/thirdweb/src/adapters/viem.test.ts
+++ b/packages/thirdweb/src/adapters/viem.test.ts
@@ -1,4 +1,4 @@
-import { type Account as ViemAccount, zeroAddress } from "viem";
+import type { Account as ViemAccount } from "viem";
import { privateKeyToAccount as viemPrivateKeyToAccount } from "viem/accounts";
import { beforeAll, describe, expect, test } from "vitest";
import { USDT_ABI } from "~test/abis/usdt.js";
@@ -6,7 +6,7 @@ import {
USDT_CONTRACT_ADDRESS,
USDT_CONTRACT_WITH_ABI,
} from "~test/test-contracts.js";
-import { ANVIL_PKEY_A } from "~test/test-wallets.js";
+import { ANVIL_PKEY_A, TEST_ACCOUNT_B } from "~test/test-wallets.js";
import { typedData } from "~test/typed-data.js";
import { ANVIL_CHAIN, FORKED_ETHEREUM_CHAIN } from "../../test/src/chains.js";
@@ -87,6 +87,7 @@ describe("walletClient.toViem", () => {
if (!walletClient.account) {
throw new Error("Account not found");
}
+
const txHash = await walletClient.sendTransaction({
account: walletClient.account,
chain: {
@@ -101,8 +102,8 @@ describe("walletClient.toViem", () => {
decimals: ANVIL_CHAIN.nativeCurrency?.decimals || 18,
},
},
- to: zeroAddress,
- value: 0n,
+ to: TEST_ACCOUNT_B.address,
+ value: 10n,
});
expect(txHash).toBeDefined();
expect(txHash.slice(0, 2)).toBe("0x");
From f200de36a0e8b5b0c46e3b5f047d9191141c5f06 Mon Sep 17 00:00:00 2001
From: gregfromstl
Date: Mon, 2 Dec 2024 17:27:44 +0000
Subject: [PATCH 15/15] test(sdk): increase coverage
---
.../thirdweb/src/adapters/ethers5.test.ts | 92 +++++++++++++++++++
packages/thirdweb/src/adapters/ethers5.ts | 6 +-
.../utils/any-evm/keyless-transaction.test.ts | 28 ++++++
3 files changed, 123 insertions(+), 3 deletions(-)
diff --git a/packages/thirdweb/src/adapters/ethers5.test.ts b/packages/thirdweb/src/adapters/ethers5.test.ts
index c85da535acd..4239e5bc4f7 100644
--- a/packages/thirdweb/src/adapters/ethers5.test.ts
+++ b/packages/thirdweb/src/adapters/ethers5.test.ts
@@ -1,4 +1,5 @@
import * as ethers5 from "ethers5";
+import * as ethers6 from "ethers6";
import { describe, expect, it, test } from "vitest";
import { USDT_CONTRACT } from "~test/test-contracts.js";
import { ANVIL_CHAIN, FORKED_ETHEREUM_CHAIN } from "../../test/src/chains.js";
@@ -10,6 +11,7 @@ import { randomBytesBuffer } from "../utils/random.js";
import { privateKeyToAccount } from "../wallets/private-key.js";
import {
fromEthersContract,
+ fromEthersSigner,
toEthersContract,
toEthersProvider,
toEthersSigner,
@@ -149,4 +151,94 @@ describe("ethers5 adapter", () => {
const _decimals = await decimals({ contract: thirdwebContract });
expect(_decimals).toBe(6);
});
+
+ test("toEthersProvider should return a valid provider", async () => {
+ const provider = toEthersProvider(ethers5, TEST_CLIENT, ANVIL_CHAIN);
+ expect(provider).toBeDefined();
+ expect(provider.getBlockNumber).toBeDefined();
+
+ // Test if the provider can fetch the block number
+ const blockNumber = await provider.getBlockNumber();
+ expect(typeof blockNumber).toBe("number");
+ });
+
+ test("toEthersProvider should throw error with invalid ethers version", () => {
+ const invalidEthers = ethers6;
+ expect(() =>
+ // biome-ignore lint/suspicious/noExplicitAny: Testing invalid data
+ toEthersProvider(invalidEthers as any, TEST_CLIENT, ANVIL_CHAIN),
+ ).toThrow(
+ "You seem to be using ethers@6, please use the `ethers6Adapter()`",
+ );
+ });
+});
+
+describe("fromEthersSigner", () => {
+ it("should convert an ethers5 Signer to an Account", async () => {
+ const wallet = new ethers5.Wallet(ANVIL_PKEY_A);
+ const account = await fromEthersSigner(wallet);
+
+ expect(account).toBeDefined();
+ expect(account.address).toBe(await wallet.getAddress());
+ });
+
+ it("should sign a message", async () => {
+ const wallet = new ethers5.Wallet(ANVIL_PKEY_A);
+ const account = await fromEthersSigner(wallet);
+
+ const message = "Hello, world!";
+ const signature = await account.signMessage({ message });
+
+ expect(signature).toBe(await wallet.signMessage(message));
+ });
+
+ it("should sign a transaction", async () => {
+ const wallet = new ethers5.Wallet(
+ ANVIL_PKEY_A,
+ ethers5.getDefaultProvider(),
+ );
+ const account = await fromEthersSigner(wallet);
+
+ const transaction = {
+ to: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
+ value: 1n,
+ };
+
+ const signedTransaction = await account.signTransaction?.(transaction);
+
+ expect(signedTransaction).toBe(await wallet.signTransaction(transaction));
+ });
+
+ it("should sign typed data", async () => {
+ const wallet = new ethers5.Wallet(ANVIL_PKEY_A);
+ const account = await fromEthersSigner(wallet);
+
+ const domain = {
+ name: "Ether Mail",
+ version: "1",
+ chainId: 1,
+ verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
+ };
+
+ const types = {
+ Person: [
+ { name: "name", type: "string" },
+ { name: "wallet", type: "address" },
+ ],
+ };
+
+ const value = {
+ name: "Alice",
+ wallet: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
+ };
+
+ const signature = await account.signTypedData({
+ primaryType: "Person",
+ domain,
+ types,
+ message: value,
+ });
+
+ expect(signature).toBeDefined();
+ });
});
diff --git a/packages/thirdweb/src/adapters/ethers5.ts b/packages/thirdweb/src/adapters/ethers5.ts
index ee653207f72..f3b84cbecdc 100644
--- a/packages/thirdweb/src/adapters/ethers5.ts
+++ b/packages/thirdweb/src/adapters/ethers5.ts
@@ -40,7 +40,7 @@ function assertEthers5(
): asserts ethers is typeof ethers5 {
if (!isEthers5(ethers)) {
throw new Error(
- "You seem to be using ethers@6, please use the `ethers6Adapter()",
+ "You seem to be using ethers@6, please use the `ethers6Adapter()`",
);
}
}
@@ -280,6 +280,7 @@ export function toEthersProvider(
client: ThirdwebClient,
chain: Chain,
): ethers5.providers.Provider {
+ assertEthers5(ethers);
const url = getRpcUrlForChain({ chain, client });
const headers: HeadersInit = {
"Content-Type": "application/json",
@@ -624,8 +625,7 @@ function alignTxToEthers(
gasLimit: gas,
to,
type,
- accessList: tx.accessList as ethers5.utils.AccessListish | undefined,
- };
+ } as ethers5.ethers.utils.Deferrable;
}
async function alignTxFromEthers(
diff --git a/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts b/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts
index dd2fdbb29b0..c2fbf759196 100644
--- a/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts
+++ b/packages/thirdweb/src/utils/any-evm/keyless-transaction.test.ts
@@ -49,6 +49,34 @@ describe("getKeylessTransaction", () => {
);
});
+ it("should throw if yParity is explicitly undefined", async () => {
+ const invalidSignature = {
+ r: mockSignature.r,
+ s: mockSignature.s,
+ yParity: undefined,
+ };
+
+ await expect(
+ getKeylessTransaction({
+ transaction: mockTransaction,
+ // biome-ignore lint/suspicious/noExplicitAny: Testing invalid data
+ signature: invalidSignature as any,
+ }),
+ ).rejects.toThrow();
+ });
+
+ it("should throw if a signature is not recoverable", async () => {
+ const invalidSignature = { ...mockSignature, v: undefined };
+
+ await expect(
+ getKeylessTransaction({
+ transaction: mockTransaction,
+ // biome-ignore lint/suspicious/noExplicitAny: Testing invalid data
+ signature: invalidSignature as any,
+ }),
+ ).rejects.toThrow();
+ });
+
it("should throw an error if the transaction is invalid", async () => {
const invalidTransaction = { ...mockTransaction, value: "invalid" };