Skip to content

Commit

Permalink
feat: Enforce maxFeePerGas as a gas ceiling and add timeouts (#692)
Browse files Browse the repository at this point in the history
* wip

* move all gas settings to overrides

* remove gasPrice override

* add logging

* fix formatting, expand range
  • Loading branch information
arcoraven authored Oct 2, 2024
1 parent 26397db commit 5ca4d29
Show file tree
Hide file tree
Showing 23 changed files with 390 additions and 474 deletions.
52 changes: 25 additions & 27 deletions src/db/transactions/queueTx.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { DeployTransaction, Transaction } from "@thirdweb-dev/sdk";
import { ERC4337EthersSigner } from "@thirdweb-dev/wallets/dist/declarations/src/evm/connectors/smart-wallet/lib/erc4337-signer";
import { Address, ZERO_ADDRESS } from "thirdweb";
import type { ERC4337EthersSigner } from "@thirdweb-dev/wallets/dist/declarations/src/evm/connectors/smart-wallet/lib/erc4337-signer";
import { ZERO_ADDRESS, type Address } from "thirdweb";
import type { ContractExtension } from "../../schema/extension";
import { parseTransactionOverrides } from "../../server/utils/transactionOverrides";
import { maybeBigInt, normalizeAddress } from "../../utils/primitiveTypes";
import { insertTransaction } from "../../utils/transaction/insertTransaction";
import type { InsertedTransaction } from "../../utils/transaction/types";

interface QueueTxParams {
// we should move away from Transaction type (v4 SDK)
Expand Down Expand Up @@ -49,10 +51,8 @@ export const queueTx = async ({
functionName,
functionArgs,
extension,
gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(txOverrides?.maxPriorityFeePerGas),
};
...parseTransactionOverrides(txOverrides),
} satisfies Partial<InsertedTransaction>;

// TODO: We need a much safer way of detecting if the transaction should be a user operation
const isUserOp = !!(tx.getSigner as ERC4337EthersSigner).erc4337provider;
Expand All @@ -76,27 +76,25 @@ export const queueTx = async ({
idempotencyKey,
shouldSimulate: simulateTx,
});
} else {
const isPublishedContractDeploy =
tx.getTarget() === ZERO_ADDRESS &&
functionName === "deploy" &&
extension === "deploy-published";
}

const queueId = await insertTransaction({
insertedTransaction: {
...baseTransaction,
isUserOp: false,
deployedContractAddress,
deployedContractType,
from: normalizeAddress(await tx.getSignerAddress()),
to: normalizeAddress(
isPublishedContractDeploy ? undefined : tx.getTarget(),
),
},
idempotencyKey,
shouldSimulate: simulateTx,
});
const isPublishedContractDeploy =
tx.getTarget() === ZERO_ADDRESS &&
functionName === "deploy" &&
extension === "deploy-published";

return queueId;
}
return await insertTransaction({
insertedTransaction: {
...baseTransaction,
isUserOp: false,
deployedContractAddress,
deployedContractType,
from: normalizeAddress(await tx.getSignerAddress()),
to: normalizeAddress(
isPublishedContractDeploy ? undefined : tx.getTarget(),
),
},
idempotencyKey,
shouldSimulate: simulateTx,
});
};
21 changes: 6 additions & 15 deletions src/server/routes/backend-wallet/sendTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Static, Type } from "@sinclair/typebox";
import { FastifyInstance } from "fastify";
import { Type, type Static } from "@sinclair/typebox";
import type { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import { Address, Hex } from "thirdweb";
import { maybeBigInt } from "../../../utils/primitiveTypes";
import type { Address, Hex } from "thirdweb";
import { insertTransaction } from "../../../utils/transaction/insertTransaction";
import { AddressSchema } from "../../schemas/address";
import {
Expand All @@ -17,6 +16,7 @@ import {
walletWithAAHeaderSchema,
} from "../../schemas/wallet";
import { getChainIdFromChain } from "../../utils/chain";
import { parseTransactionOverrides } from "../../utils/transactionOverrides";

const requestBodySchema = Type.Object({
toAddress: Type.Optional(AddressSchema),
Expand Down Expand Up @@ -93,11 +93,7 @@ export async function sendTransaction(fastify: FastifyInstance) {
accountFactoryAddress,
"x-account-factory-address",
),
gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(
txOverrides?.maxPriorityFeePerGas,
),
...parseTransactionOverrides(txOverrides),
},
shouldSimulate: simulateTx,
idempotencyKey,
Expand All @@ -111,12 +107,7 @@ export async function sendTransaction(fastify: FastifyInstance) {
to: toAddress as Address | undefined,
data: data as Hex,
value: BigInt(value),

gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(
txOverrides?.maxPriorityFeePerGas,
),
...parseTransactionOverrides(txOverrides),
},
shouldSimulate: simulateTx,
idempotencyKey,
Expand Down
15 changes: 5 additions & 10 deletions src/server/routes/backend-wallet/sendTransactionBatch.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Static, Type } from "@sinclair/typebox";
import { FastifyInstance } from "fastify";
import { Type, type Static } from "@sinclair/typebox";
import type { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import { Address, Hex } from "thirdweb";
import { maybeBigInt } from "../../../utils/primitiveTypes";
import type { Address, Hex } from "thirdweb";
import { insertTransaction } from "../../../utils/transaction/insertTransaction";
import { AddressSchema } from "../../schemas/address";
import { standardResponseSchema } from "../../schemas/sharedApiSchemas";
Expand All @@ -12,6 +11,7 @@ import {
walletHeaderSchema,
} from "../../schemas/wallet";
import { getChainIdFromChain } from "../../utils/chain";
import { parseTransactionOverrides } from "../../utils/transactionOverrides";

const requestBodySchema = Type.Array(
Type.Object({
Expand Down Expand Up @@ -74,12 +74,7 @@ export async function sendTransactionBatch(fastify: FastifyInstance) {
to: toAddress as Address | undefined,
data: data as Hex,
value: BigInt(value),

gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(
txOverrides?.maxPriorityFeePerGas,
),
...parseTransactionOverrides(txOverrides),
},
});
queueIds.push(queueId);
Expand Down
12 changes: 4 additions & 8 deletions src/server/routes/backend-wallet/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { transfer as transferERC20 } from "thirdweb/extensions/erc20";
import { isContractDeployed, resolvePromisedValue } from "thirdweb/utils";
import { getChain } from "../../../utils/chain";
import { maybeBigInt, normalizeAddress } from "../../../utils/primitiveTypes";
import { normalizeAddress } from "../../../utils/primitiveTypes";
import { thirdwebClient } from "../../../utils/sdk";
import { insertTransaction } from "../../../utils/transaction/insertTransaction";
import type { InsertedTransaction } from "../../../utils/transaction/types";
Expand All @@ -29,6 +29,7 @@ import {
walletWithAddressParamSchema,
} from "../../schemas/wallet";
import { getChainIdFromChain } from "../../utils/chain";
import { parseTransactionOverrides } from "../../utils/transactionOverrides";

const requestSchema = Type.Omit(walletWithAddressParamSchema, [
"walletAddress",
Expand Down Expand Up @@ -93,11 +94,6 @@ export async function transfer(fastify: FastifyInstance) {
// Resolve inputs.
const currencyAddress = normalizeAddress(_currencyAddress);
const chainId = await getChainIdFromChain(chain);
const gasOverrides = {
gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(txOverrides?.maxPriorityFeePerGas),
};

let insertedTransaction: InsertedTransaction;
if (
Expand All @@ -113,7 +109,7 @@ export async function transfer(fastify: FastifyInstance) {
value: toWei(amount),
extension: "none",
functionName: "transfer",
...gasOverrides,
...parseTransactionOverrides(txOverrides),
};
} else {
const contract = getContract({
Expand Down Expand Up @@ -147,7 +143,7 @@ export async function transfer(fastify: FastifyInstance) {
extension: "erc20",
functionName: "transfer",
functionArgs: [to, amount, currencyAddress],
...gasOverrides,
...parseTransactionOverrides(txOverrides),
};
}

Expand Down
10 changes: 5 additions & 5 deletions src/server/routes/backend-wallet/withdraw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import { getWalletBalance } from "thirdweb/wallets";
import { getAccount } from "../../../utils/account";
import { getChain } from "../../../utils/chain";
import { logger } from "../../../utils/logger";
import { getChecksumAddress, maybeBigInt } from "../../../utils/primitiveTypes";
import { getChecksumAddress } from "../../../utils/primitiveTypes";
import { thirdwebClient } from "../../../utils/sdk";
import type { PopulatedTransaction } from "../../../utils/transaction/types";
import { createCustomError } from "../../middleware/error";
import { AddressSchema, TransactionHashSchema } from "../../schemas/address";
import { TokenAmountStringSchema } from "../../schemas/number";
Expand All @@ -27,6 +28,7 @@ import {
walletWithAddressParamSchema,
} from "../../schemas/wallet";
import { getChainIdFromChain } from "../../utils/chain";
import { parseTransactionOverrides } from "../../utils/transactionOverrides";

const ParamsSchema = Type.Omit(walletWithAddressParamSchema, ["walletAddress"]);

Expand Down Expand Up @@ -88,9 +90,7 @@ export async function withdraw(fastify: FastifyInstance) {
data: "0x",
// Dummy value, replaced below.
value: 1n,
gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(txOverrides?.maxPriorityFeePerGas),
...parseTransactionOverrides(txOverrides).overrides,
},
});

Expand Down Expand Up @@ -130,7 +130,7 @@ export async function withdraw(fastify: FastifyInstance) {

const getWithdrawValue = async (
from: Address,
populatedTransaction: Awaited<ReturnType<typeof toSerializableTransaction>>,
populatedTransaction: PopulatedTransaction,
): Promise<bigint> => {
const chain = await getChain(populatedTransaction.chainId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { resolvePromisedValue } from "thirdweb/utils";
import { queueTx } from "../../../../../../db/transactions/queueTx";
import { getContract } from "../../../../../../utils/cache/getContract";
import { getContractV5 } from "../../../../../../utils/cache/getContractv5";
import { maybeBigInt } from "../../../../../../utils/primitiveTypes";
import { insertTransaction } from "../../../../../../utils/transaction/insertTransaction";
import { thirdwebSdkVersionSchema } from "../../../../../schemas/httpHeaders/thirdwebSdkVersion";
import { signature721OutputSchema } from "../../../../../schemas/nft";
Expand All @@ -23,6 +22,7 @@ import {
import { txOverridesWithValueSchema } from "../../../../../schemas/txOverrides";
import { walletWithAAHeaderSchema } from "../../../../../schemas/wallet";
import { getChainIdFromChain } from "../../../../../utils/chain";
import { parseTransactionOverrides } from "../../../../../utils/transactionOverrides";

// INPUTS
const requestSchema = contractParamSchema;
Expand Down Expand Up @@ -122,11 +122,9 @@ export async function erc721SignatureMint(fastify: FastifyInstance) {
from: fromAddress as Address,
to: contractAddress as Address | undefined,
data: (await resolvePromisedValue(transaction.data)) as Hex,
value: maybeBigInt(txOverrides?.value),
gas: maybeBigInt(txOverrides?.gas),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(txOverrides?.maxPriorityFeePerGas),
...parseTransactionOverrides(txOverrides),
};

if (accountAddress) {
queueId = await insertTransaction({
insertedTransaction: {
Expand Down
10 changes: 4 additions & 6 deletions src/server/routes/contract/write/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Type, type Static } from "@sinclair/typebox";
import type { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import { prepareContractCall, resolveMethod } from "thirdweb";
import type { AbiFunction } from "thirdweb/utils";
import { getContractV5 } from "../../../../utils/cache/getContractv5";
import { maybeBigInt } from "../../../../utils/primitiveTypes";
import { queueTransaction } from "../../../../utils/transaction/queueTransation";
import { createCustomError } from "../../../middleware/error";
import { abiSchema } from "../../../schemas/contract";
Expand All @@ -19,6 +19,7 @@ import {
walletWithAAHeaderSchema,
} from "../../../schemas/wallet";
import { getChainIdFromChain } from "../../../utils/chain";
import { parseTransactionOverrides } from "../../../utils/transactionOverrides";

// INPUT
const writeRequestBodySchema = Type.Object({
Expand Down Expand Up @@ -78,7 +79,7 @@ export async function writeToContract(fastify: FastifyInstance) {
// 2. functionName passed as function name + passed in ABI
// 3. functionName passed as function name + inferred ABI (fetched at encode time)
// this is all handled inside the `resolveMethod` function
let method;
let method: AbiFunction;
try {
method = await resolveMethod(functionName)(contract);
} catch (e: any) {
Expand All @@ -88,10 +89,7 @@ export async function writeToContract(fastify: FastifyInstance) {
contract,
method,
params: args,
gas: maybeBigInt(txOverrides?.gas),
value: maybeBigInt(txOverrides?.value),
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(txOverrides?.maxPriorityFeePerGas),
...parseTransactionOverrides(txOverrides),
});

const queueId = await queueTransaction({
Expand Down
9 changes: 6 additions & 3 deletions src/server/routes/transaction/cancel.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { Static, Type } from "@sinclair/typebox";
import { FastifyInstance } from "fastify";
import { Type, type Static } from "@sinclair/typebox";
import type { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import { TransactionDB } from "../../../db/transactions/db";
import { getBlockNumberish } from "../../../utils/block";
import { getConfig } from "../../../utils/cache/getConfig";
import { getChain } from "../../../utils/chain";
import { msSince } from "../../../utils/date";
import { sendCancellationTransaction } from "../../../utils/transaction/cancelTransaction";
import { CancelledTransaction } from "../../../utils/transaction/types";
import type { CancelledTransaction } from "../../../utils/transaction/types";
import { enqueueTransactionWebhook } from "../../../utils/transaction/webhook";
import { reportUsage } from "../../../utils/usage";
import { SendTransactionQueue } from "../../../worker/queues/sendTransactionQueue";
Expand Down Expand Up @@ -98,10 +98,13 @@ export async function cancelTransaction(fastify: FastifyInstance) {
...transaction,
status: "cancelled",
cancelledAt: new Date(),

// Dummy data since the transaction was never sent.
sentAt: new Date(),
sentAtBlock: await getBlockNumberish(transaction.chainId),

isUserOp: false,
gas: 0n,
nonce: -1,
sentTransactionHashes: [],
};
Expand Down
3 changes: 2 additions & 1 deletion src/server/routes/transaction/syncRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ export async function syncRetryTransaction(fastify: FastifyInstance) {
client: thirdwebClient,
chain: await getChain(chainId),
...transaction,
// Explicitly reuse the same nonce the transaction had previously acquired.
nonce: transaction.nonce,
maxFeePerGas: maybeBigInt(maxFeePerGas),
maxPriorityFeePerGas: maybeBigInt(maxPriorityFeePerGas),
nonce: transaction.nonce,
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/server/schemas/number.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export const TokenAmountStringSchema = Type.RegExp(/^\d+(\.\d+)?$/, {

export const WeiAmountStringSchema = Type.RegExp(/^\d+$/, {
description: 'An amount in wei (no decimals). Example: "100000000"',
examples: ["0"],
examples: ["50000000000"],
});
Loading

0 comments on commit 5ca4d29

Please sign in to comment.