Skip to content

Commit 862db40

Browse files
d4mrjoaquim-verges
andauthored
[SDK] Feature: Add support for forcing legacy transactions in chain configuration (#6180)
Signed-off-by: Prithvish Baidya <[email protected]> Co-authored-by: Joaquim Verges <[email protected]>
1 parent 39cb2a2 commit 862db40

File tree

5 files changed

+97
-55
lines changed

5 files changed

+97
-55
lines changed

packages/thirdweb/src/adapters/ethers5.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import type { ThirdwebClient } from "../client/client.js";
99
import { type ThirdwebContract, getContract } from "../contract/contract.js";
1010
import { toSerializableTransaction } from "../transaction/actions/to-serializable-transaction.js";
1111
import { waitForReceipt } from "../transaction/actions/wait-for-tx-receipt.js";
12-
import type { PreparedTransaction } from "../transaction/prepare-transaction.js";
12+
import {
13+
type PreparedTransaction,
14+
TransactionTypeMap,
15+
} from "../transaction/prepare-transaction.js";
1316
import type { SerializableTransaction } from "../transaction/serialize-transaction.js";
1417
import { toHex } from "../utils/encoding/hex.js";
1518
import type { Account } from "../wallets/interfaces/wallet.js";
@@ -483,6 +486,7 @@ export async function toEthersSigner(
483486

484487
const response: ethers5.ethers.providers.TransactionResponse = {
485488
...serialized,
489+
type: serialized.type ? TransactionTypeMap[serialized.type] : undefined,
486490
nonce: Number(serialized.nonce ?? 0),
487491
from: account.address,
488492
maxFeePerGas: serialized.maxFeePerGas

packages/thirdweb/src/chains/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { FeeType } from "../gas/fee-data.js";
2+
13
/**
24
* @chain
35
*/
@@ -26,6 +28,7 @@ export type ChainOptions = {
2628
increaseZeroByteCount?: boolean;
2729
};
2830
faucets?: Array<string>;
31+
feeType?: FeeType;
2932
};
3033

3134
/**

packages/thirdweb/src/gas/fee-data.ts

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ export async function getGasOverridesForTransaction(
5252
transaction: PreparedTransaction,
5353
): Promise<FeeDataParams> {
5454
// first check for explicit values
55-
const [maxFeePerGas, maxPriorityFeePerGas, gasPrice] = await Promise.all([
56-
resolvePromisedValue(transaction.maxFeePerGas),
57-
resolvePromisedValue(transaction.maxPriorityFeePerGas),
58-
resolvePromisedValue(transaction.gasPrice),
59-
]);
55+
const [maxFeePerGas, maxPriorityFeePerGas, gasPrice, type] =
56+
await Promise.all([
57+
resolvePromisedValue(transaction.maxFeePerGas),
58+
resolvePromisedValue(transaction.maxPriorityFeePerGas),
59+
resolvePromisedValue(transaction.gasPrice),
60+
resolvePromisedValue(transaction.type),
61+
]);
6062

6163
// Exit early if the user explicitly provided enough options
6264
if (maxFeePerGas !== undefined && maxPriorityFeePerGas !== undefined) {
@@ -65,6 +67,7 @@ export async function getGasOverridesForTransaction(
6567
maxPriorityFeePerGas,
6668
};
6769
}
70+
6871
if (typeof gasPrice === "bigint") {
6972
return { gasPrice };
7073
}
@@ -73,6 +76,7 @@ export async function getGasOverridesForTransaction(
7376
const defaultGasOverrides = await getDefaultGasOverrides(
7477
transaction.client,
7578
transaction.chain,
79+
type === "legacy" ? "legacy" : "eip1559", // 7702, 2930, and eip1559 all qualify as "eip1559" fee type
7680
);
7781

7882
if (transaction.chain.experimental?.increaseZeroByteCount) {
@@ -103,6 +107,8 @@ export async function getGasOverridesForTransaction(
103107
};
104108
}
105109

110+
export type FeeType = "legacy" | "eip1559";
111+
106112
/**
107113
* Retrieves the default gas overrides for a given client and chain ID.
108114
* If the fee data contains both maxFeePerGas and maxPriorityFeePerGas, it returns an object with those values.
@@ -115,20 +121,27 @@ export async function getGasOverridesForTransaction(
115121
export async function getDefaultGasOverrides(
116122
client: ThirdwebClient,
117123
chain: Chain,
124+
feeType?: FeeType,
118125
) {
119-
// if chain is in the force gas price list, always use gas price
120-
if (!FORCE_GAS_PRICE_CHAIN_IDS.includes(chain.id)) {
121-
const feeData = await getDynamicFeeData(client, chain);
122-
if (
123-
feeData.maxFeePerGas !== null &&
124-
feeData.maxPriorityFeePerGas !== null
125-
) {
126-
return {
127-
maxFeePerGas: feeData.maxFeePerGas,
128-
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
129-
};
130-
}
126+
// give priority to the transaction fee type over the chain fee type
127+
const resolvedFeeType = feeType ?? chain.feeType;
128+
// if chain is configured to force legacy transactions or is in the legacy chain list
129+
if (
130+
resolvedFeeType === "legacy" ||
131+
FORCE_GAS_PRICE_CHAIN_IDS.includes(chain.id)
132+
) {
133+
return {
134+
gasPrice: await getGasPrice({ client, chain, percentMultiplier: 10 }),
135+
};
136+
}
137+
const feeData = await getDynamicFeeData(client, chain);
138+
if (feeData.maxFeePerGas !== null && feeData.maxPriorityFeePerGas !== null) {
139+
return {
140+
maxFeePerGas: feeData.maxFeePerGas,
141+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
142+
};
131143
}
144+
// TODO: resolvedFeeType here could be "EIP1559", but we could not get EIP1559 fee data. should we throw?
132145
return {
133146
gasPrice: await getGasPrice({ client, chain, percentMultiplier: 10 }),
134147
};
@@ -156,7 +169,7 @@ async function getDynamicFeeData(
156169
eth_maxPriorityFeePerGas(rpcRequest).catch(() => null),
157170
]);
158171

159-
const baseBlockFee = block?.baseFeePerGas ?? 0n;
172+
const baseBlockFee = block?.baseFeePerGas;
160173

161174
const chainId = chain.id;
162175
// flag chain testnet & flag chain
@@ -174,7 +187,7 @@ async function getDynamicFeeData(
174187
maxPriorityFeePerGas_ = maxPriorityFeePerGas;
175188
}
176189

177-
if (maxPriorityFeePerGas_ == null) {
190+
if (maxPriorityFeePerGas_ == null || baseBlockFee == null) {
178191
// chain does not support eip-1559, return null for both
179192
return { maxFeePerGas: null, maxPriorityFeePerGas: null };
180193
}

packages/thirdweb/src/transaction/actions/to-serializable-transaction.ts

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -75,42 +75,52 @@ export async function toSerializableTransaction(
7575
const rpcRequest = getRpcClient(options.transaction);
7676
const chainId = options.transaction.chain.id;
7777
const from = options.from;
78-
let [data, nonce, gas, feeData, to, accessList, value, authorizationList] =
79-
await Promise.all([
80-
encode(options.transaction),
81-
(async () => {
82-
// if the user has specified a nonce, use that
83-
const resolvedNonce = await resolvePromisedValue(
84-
options.transaction.nonce,
85-
);
86-
if (resolvedNonce !== undefined) {
87-
return resolvedNonce;
88-
}
78+
let [
79+
data,
80+
nonce,
81+
gas,
82+
feeData,
83+
to,
84+
accessList,
85+
value,
86+
authorizationList,
87+
type,
88+
] = await Promise.all([
89+
encode(options.transaction),
90+
(async () => {
91+
// if the user has specified a nonce, use that
92+
const resolvedNonce = await resolvePromisedValue(
93+
options.transaction.nonce,
94+
);
95+
if (resolvedNonce !== undefined) {
96+
return resolvedNonce;
97+
}
8998

90-
return from // otherwise get the next nonce (import the method to do so)
91-
? await import("../../rpc/actions/eth_getTransactionCount.js").then(
92-
({ eth_getTransactionCount }) =>
93-
eth_getTransactionCount(rpcRequest, {
94-
address:
95-
typeof from === "string"
96-
? getAddress(from)
97-
: getAddress(from.address),
98-
blockTag: "pending",
99-
}),
100-
)
101-
: undefined;
102-
})(),
103-
// takes the same options as the sendTransaction function thankfully!
104-
estimateGas({
105-
...options,
106-
from: options.from,
107-
}),
108-
getGasOverridesForTransaction(options.transaction),
109-
resolvePromisedValue(options.transaction.to),
110-
resolvePromisedValue(options.transaction.accessList),
111-
resolvePromisedValue(options.transaction.value),
112-
resolvePromisedValue(options.transaction.authorizationList),
113-
]);
99+
return from // otherwise get the next nonce (import the method to do so)
100+
? await import("../../rpc/actions/eth_getTransactionCount.js").then(
101+
({ eth_getTransactionCount }) =>
102+
eth_getTransactionCount(rpcRequest, {
103+
address:
104+
typeof from === "string"
105+
? getAddress(from)
106+
: getAddress(from.address),
107+
blockTag: "pending",
108+
}),
109+
)
110+
: undefined;
111+
})(),
112+
// takes the same options as the sendTransaction function thankfully!
113+
estimateGas({
114+
...options,
115+
from: options.from,
116+
}),
117+
getGasOverridesForTransaction(options.transaction),
118+
resolvePromisedValue(options.transaction.to),
119+
resolvePromisedValue(options.transaction.accessList),
120+
resolvePromisedValue(options.transaction.value),
121+
resolvePromisedValue(options.transaction.authorizationList),
122+
resolvePromisedValue(options.transaction.type),
123+
]);
114124

115125
const extraGas = await resolvePromisedValue(options.transaction.extraGas);
116126
if (extraGas) {
@@ -126,6 +136,7 @@ export async function toSerializableTransaction(
126136
accessList,
127137
value,
128138
authorizationList,
139+
type,
129140
...feeData,
130141
} satisfies SerializableTransaction;
131142
}

packages/thirdweb/src/transaction/prepare-transaction.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type StaticPrepareTransactionOptions = {
1717
maxFeePerGas?: bigint | undefined;
1818
maxPriorityFeePerGas?: bigint | undefined;
1919
maxFeePerBlobGas?: bigint | undefined;
20+
type?: undefined | TransactionType;
2021
nonce?: number | undefined;
2122
extraGas?: bigint | undefined;
2223
// eip7702
@@ -34,6 +35,16 @@ export type StaticPrepareTransactionOptions = {
3435
};
3536
};
3637

38+
type TransactionType = "legacy" | "eip1559" | "eip2930" | "eip4844" | "eip7702";
39+
40+
export const TransactionTypeMap: Record<TransactionType, number> = {
41+
legacy: 0,
42+
eip1559: 1,
43+
eip2930: 2,
44+
eip4844: 3,
45+
eip7702: 4,
46+
};
47+
3748
export type EIP712TransactionOptions = {
3849
// constant or user input
3950
gasPerPubdata?: bigint | undefined;

0 commit comments

Comments
 (0)