Skip to content

Commit 8e14148

Browse files
committed
Merge remote-tracking branch 'origin/main' into ph/removeMigrateWorker
2 parents 28b05bc + 85b2aff commit 8e14148

File tree

7 files changed

+199
-49
lines changed

7 files changed

+199
-49
lines changed

src/server/routes/contract/extensions/erc1155/write/mintTo.ts

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { Type, type Static } from "@sinclair/typebox";
22
import type { FastifyInstance } from "fastify";
33
import { StatusCodes } from "http-status-codes";
4-
import { queueTx } from "../../../../../../db/transactions/queueTx";
5-
import { getContract } from "../../../../../../utils/cache/getContract";
4+
import { getContract } from "thirdweb";
5+
import { mintTo } from "thirdweb/extensions/erc1155";
6+
import type { NFTInput } from "thirdweb/utils";
7+
import { getChain } from "../../../../../../utils/chain";
8+
import { thirdwebClient } from "../../../../../../utils/sdk";
9+
import { queueTransaction } from "../../../../../../utils/transaction/queueTransation";
610
import { AddressSchema } from "../../../../../schemas/address";
711
import { nftAndSupplySchema } from "../../../../../schemas/nft";
812
import {
@@ -12,10 +16,13 @@ import {
1216
transactionWritesResponseSchema,
1317
} from "../../../../../schemas/sharedApiSchemas";
1418
import { txOverridesWithValueSchema } from "../../../../../schemas/txOverrides";
15-
import { walletWithAAHeaderSchema } from "../../../../../schemas/wallet";
19+
import {
20+
maybeAddress,
21+
requiredAddress,
22+
walletWithAAHeaderSchema,
23+
} from "../../../../../schemas/wallet";
1624
import { getChainIdFromChain } from "../../../../../utils/chain";
1725

18-
// INPUTS
1926
const requestSchema = erc1155ContractParamSchema;
2027
const requestBodySchema = Type.Object({
2128
receiver: {
@@ -66,34 +73,62 @@ export async function erc1155mintTo(fastify: FastifyInstance) {
6673
},
6774
},
6875
handler: async (request, reply) => {
69-
const { chain, contractAddress } = request.params;
76+
const { chain: _chain, contractAddress } = request.params;
7077
const { simulateTx } = request.query;
7178
const { receiver, metadataWithSupply, txOverrides } = request.body;
7279
const {
73-
"x-backend-wallet-address": walletAddress,
80+
"x-backend-wallet-address": fromAddress,
7481
"x-account-address": accountAddress,
7582
"x-idempotency-key": idempotencyKey,
83+
"x-account-factory-address": accountFactoryAddress,
84+
"x-account-salt": accountSalt,
7685
} = request.headers as Static<typeof walletWithAAHeaderSchema>;
7786

78-
const chainId = await getChainIdFromChain(chain);
79-
const contract = await getContract({
80-
chainId,
81-
contractAddress,
82-
walletAddress,
83-
accountAddress,
87+
const chainId = await getChainIdFromChain(_chain);
88+
const chain = await getChain(chainId);
89+
90+
const contract = getContract({
91+
chain,
92+
client: thirdwebClient,
93+
address: contractAddress,
8494
});
85-
const tx = await contract.erc1155.mintTo.prepare(
86-
receiver,
87-
metadataWithSupply,
88-
);
8995

90-
const queueId = await queueTx({
91-
tx,
92-
chainId,
93-
simulateTx,
94-
extension: "erc1155",
95-
idempotencyKey,
96+
// Backward compatibility: Transform the request body's v4 shape to v5.
97+
const { metadata, supply } = metadataWithSupply;
98+
const nft: NFTInput | string =
99+
typeof metadata === "string"
100+
? metadata
101+
: {
102+
name: metadata.name?.toString() ?? undefined,
103+
description: metadata.description ?? undefined,
104+
image: metadata.image ?? undefined,
105+
animation_url: metadata.animation_url ?? undefined,
106+
external_url: metadata.external_url ?? undefined,
107+
background_color: metadata.background_color ?? undefined,
108+
properties: metadata.properties,
109+
};
110+
const transaction = mintTo({
111+
contract,
112+
to: receiver,
113+
nft,
114+
supply: BigInt(supply),
115+
});
116+
117+
const queueId = await queueTransaction({
118+
transaction,
119+
fromAddress: requiredAddress(fromAddress, "x-backend-wallet-address"),
120+
toAddress: maybeAddress(contractAddress, "to"),
121+
accountAddress: maybeAddress(accountAddress, "x-account-address"),
122+
accountFactoryAddress: maybeAddress(
123+
accountFactoryAddress,
124+
"x-account-factory-address",
125+
),
126+
accountSalt,
96127
txOverrides,
128+
idempotencyKey,
129+
extension: "erc1155",
130+
functionName: "mintTo",
131+
shouldSimulate: simulateTx,
97132
});
98133

99134
reply.status(StatusCodes.OK).send({

src/server/routes/contract/extensions/erc20/write/mintTo.ts

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { Type, type Static } from "@sinclair/typebox";
22
import type { FastifyInstance } from "fastify";
33
import { StatusCodes } from "http-status-codes";
4-
import { queueTx } from "../../../../../../db/transactions/queueTx";
5-
import { getContract } from "../../../../../../utils/cache/getContract";
4+
import { getContract } from "thirdweb";
5+
import { mintTo } from "thirdweb/extensions/erc20";
6+
import { getChain } from "../../../../../../utils/chain";
7+
import { thirdwebClient } from "../../../../../../utils/sdk";
8+
import { queueTransaction } from "../../../../../../utils/transaction/queueTransation";
69
import { AddressSchema } from "../../../../../schemas/address";
710
import {
811
erc20ContractParamSchema,
@@ -11,7 +14,11 @@ import {
1114
transactionWritesResponseSchema,
1215
} from "../../../../../schemas/sharedApiSchemas";
1316
import { txOverridesWithValueSchema } from "../../../../../schemas/txOverrides";
14-
import { walletWithAAHeaderSchema } from "../../../../../schemas/wallet";
17+
import {
18+
maybeAddress,
19+
requiredAddress,
20+
walletWithAAHeaderSchema,
21+
} from "../../../../../schemas/wallet";
1522
import { getChainIdFromChain } from "../../../../../utils/chain";
1623

1724
// INPUTS
@@ -59,31 +66,46 @@ export async function erc20mintTo(fastify: FastifyInstance) {
5966
},
6067
},
6168
handler: async (request, reply) => {
62-
const { chain, contractAddress } = request.params;
69+
const { chain: _chain, contractAddress } = request.params;
6370
const { simulateTx } = request.query;
6471
const { toAddress, amount, txOverrides } = request.body;
6572
const {
66-
"x-backend-wallet-address": walletAddress,
73+
"x-backend-wallet-address": fromAddress,
6774
"x-account-address": accountAddress,
6875
"x-idempotency-key": idempotencyKey,
76+
"x-account-factory-address": accountFactoryAddress,
77+
"x-account-salt": accountSalt,
6978
} = request.headers as Static<typeof walletWithAAHeaderSchema>;
7079

71-
const chainId = await getChainIdFromChain(chain);
72-
const contract = await getContract({
73-
chainId,
74-
contractAddress,
75-
walletAddress,
76-
accountAddress,
80+
const chainId = await getChainIdFromChain(_chain);
81+
const chain = await getChain(chainId);
82+
83+
const contract = getContract({
84+
chain,
85+
client: thirdwebClient,
86+
address: contractAddress,
87+
});
88+
const transaction = mintTo({
89+
contract,
90+
to: toAddress,
91+
amount,
7792
});
78-
const tx = await contract.erc20.mintTo.prepare(toAddress, amount);
7993

80-
const queueId = await queueTx({
81-
tx,
82-
chainId,
83-
simulateTx,
84-
extension: "erc20",
85-
idempotencyKey,
94+
const queueId = await queueTransaction({
95+
transaction,
96+
fromAddress: requiredAddress(fromAddress, "x-backend-wallet-address"),
97+
toAddress: maybeAddress(contractAddress, "to"),
98+
accountAddress: maybeAddress(accountAddress, "x-account-address"),
99+
accountFactoryAddress: maybeAddress(
100+
accountFactoryAddress,
101+
"x-account-factory-address",
102+
),
103+
accountSalt,
86104
txOverrides,
105+
idempotencyKey,
106+
extension: "erc20",
107+
functionName: "mintTo",
108+
shouldSimulate: simulateTx,
87109
});
88110

89111
reply.status(StatusCodes.OK).send({

src/server/routes/contract/extensions/erc721/write/mintTo.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export async function erc721mintTo(fastify: FastifyInstance) {
120120
accountSalt,
121121
txOverrides,
122122
idempotencyKey,
123+
extension: "erc721",
124+
functionName: "mintTo",
123125
shouldSimulate: simulateTx,
124126
});
125127

src/tests/math.test.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vitest";
2-
import { getPercentile } from "../utils/math";
2+
import { bigMath, getPercentile } from "../utils/math";
33

44
describe("getPercentile", () => {
55
it("should correctly calculate the p50 (median) of a sorted array", () => {
@@ -27,3 +27,81 @@ describe("getPercentile", () => {
2727
expect(getPercentile(numbers, 50)).toBe(0);
2828
});
2929
});
30+
31+
describe("bigMath", () => {
32+
describe("min", () => {
33+
it("should return the smaller of two positive numbers", () => {
34+
const a = 5n;
35+
const b = 10n;
36+
expect(bigMath.min(a, b)).toBe(5n);
37+
});
38+
39+
it("should return the smaller of two negative numbers", () => {
40+
const a = -10n;
41+
const b = -5n;
42+
expect(bigMath.min(a, b)).toBe(-10n);
43+
});
44+
45+
it("should handle equal numbers", () => {
46+
const a = 5n;
47+
const b = 5n;
48+
expect(bigMath.min(a, b)).toBe(5n);
49+
});
50+
51+
it("should handle zero and positive number", () => {
52+
const a = 0n;
53+
const b = 5n;
54+
expect(bigMath.min(a, b)).toBe(0n);
55+
});
56+
57+
it("should handle zero and negative number", () => {
58+
const a = 0n;
59+
const b = -5n;
60+
expect(bigMath.min(a, b)).toBe(-5n);
61+
});
62+
63+
it("should handle very large numbers", () => {
64+
const a = BigInt(Number.MAX_SAFE_INTEGER) * 2n;
65+
const b = BigInt(Number.MAX_SAFE_INTEGER);
66+
expect(bigMath.min(a, b)).toBe(b);
67+
});
68+
});
69+
70+
describe("max", () => {
71+
it("should return the larger of two positive numbers", () => {
72+
const a = 5n;
73+
const b = 10n;
74+
expect(bigMath.max(a, b)).toBe(10n);
75+
});
76+
77+
it("should return the larger of two negative numbers", () => {
78+
const a = -10n;
79+
const b = -5n;
80+
expect(bigMath.max(a, b)).toBe(-5n);
81+
});
82+
83+
it("should handle equal numbers", () => {
84+
const a = 5n;
85+
const b = 5n;
86+
expect(bigMath.max(a, b)).toBe(5n);
87+
});
88+
89+
it("should handle zero and positive number", () => {
90+
const a = 0n;
91+
const b = 5n;
92+
expect(bigMath.max(a, b)).toBe(5n);
93+
});
94+
95+
it("should handle zero and negative number", () => {
96+
const a = 0n;
97+
const b = -5n;
98+
expect(bigMath.max(a, b)).toBe(0n);
99+
});
100+
101+
it("should handle very large numbers", () => {
102+
const a = BigInt(Number.MAX_SAFE_INTEGER) * 2n;
103+
const b = BigInt(Number.MAX_SAFE_INTEGER);
104+
expect(bigMath.max(a, b)).toBe(a);
105+
});
106+
});
107+
});

src/utils/env.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,12 @@ export const env = createEnv({
102102
/**
103103
* Experimental env vars. These may be renamed or removed in future non-major releases.
104104
*/
105-
// Sets how long the mine worker waits for a transaction receipt before considering the transaction dropped (default: 30 minutes).
105+
// Sets how long the mine worker waits for a transaction receipt before considering the transaction dropped. Default: 30 minutes
106106
EXPERIMENTAL__MINE_WORKER_TIMEOUT_SECONDS: z.coerce
107107
.number()
108108
.default(30 * 60),
109+
// Sets the max gas price for a transaction attempt. Most RPCs reject transactions above a certain gas price. Default: 10^18 wei.
110+
EXPERIMENTAL__MAX_GAS_PRICE_WEI: z.coerce.number().default(10 ** 18),
109111
},
110112
clientPrefix: "NEVER_USED",
111113
client: {},
@@ -144,6 +146,8 @@ export const env = createEnv({
144146
NONCE_MAP_COUNT: process.env.NONCE_MAP_COUNT,
145147
EXPERIMENTAL__MINE_WORKER_TIMEOUT_SECONDS:
146148
process.env.EXPERIMENTAL__MINE_WORKER_TIMEOUT_SECONDS,
149+
EXPERIMENTAL__MAX_GAS_PRICE_WEI:
150+
process.env.EXPERIMENTAL__MAX_GAS_PRICE_WEI,
147151
METRICS_PORT: process.env.METRICS_PORT,
148152
METRICS_ENABLED: process.env.METRICS_ENABLED,
149153
},

src/utils/math.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,8 @@ export const getPercentile = (arr: number[], percentile: number): number => {
77
const index = Math.floor((percentile / 100) * (arr.length - 1));
88
return arr[index];
99
};
10+
11+
export const BigIntMath = {
12+
min: (a: bigint, b: bigint) => (a < b ? a : b),
13+
max: (a: bigint, b: bigint) => (a > b ? a : b),
14+
};

src/worker/tasks/sendTransactionWorker.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
isReplacementGasFeeTooLow,
4040
wrapError,
4141
} from "../../utils/error";
42+
import { BigIntMath } from "../../utils/math";
4243
import { getChecksumAddress } from "../../utils/primitiveTypes";
4344
import { recordMetrics } from "../../utils/prometheus";
4445
import { redis } from "../../utils/redis/redis";
@@ -565,34 +566,37 @@ const _minutesFromNow = (minutes: number) =>
565566
* @param populatedTransaction The transaction with estimated gas from RPC.
566567
* @param resendCount The resend attempt #. Example: 2 = the transaction was initially sent, then resent once. This is the second resend attempt.
567568
*/
568-
export const _updateGasFees = (
569+
export function _updateGasFees(
569570
populatedTransaction: PopulatedTransaction,
570571
resendCount: number,
571572
overrides: SentTransaction["overrides"],
572-
): PopulatedTransaction => {
573+
): PopulatedTransaction {
573574
if (resendCount === 0) {
574575
return populatedTransaction;
575576
}
576577

577-
const multiplier = BigInt(Math.min(10, resendCount * 2));
578-
578+
const multiplier = BigIntMath.min(10n, BigInt(resendCount) * 2n);
579579
const updated = { ...populatedTransaction };
580580

581581
// Update gas fees (unless they were explicitly overridden).
582+
// Do not exceed MAX_GAS_PRICE_WEI.
583+
const MAX_GAS_PRICE_WEI = BigInt(env.EXPERIMENTAL__MAX_GAS_PRICE_WEI);
582584

583585
if (updated.gasPrice && !overrides?.gasPrice) {
584-
updated.gasPrice *= multiplier;
586+
const newGasPrice = updated.gasPrice * multiplier;
587+
updated.gasPrice = BigIntMath.min(newGasPrice, MAX_GAS_PRICE_WEI);
585588
}
586589
if (updated.maxPriorityFeePerGas && !overrides?.maxPriorityFeePerGas) {
587590
updated.maxPriorityFeePerGas *= multiplier;
588591
}
589592
if (updated.maxFeePerGas && !overrides?.maxFeePerGas) {
590-
updated.maxFeePerGas =
593+
const newMaxFeePerGas =
591594
updated.maxFeePerGas * 2n + (updated.maxPriorityFeePerGas ?? 0n);
595+
updated.maxFeePerGas = BigIntMath.min(newMaxFeePerGas, MAX_GAS_PRICE_WEI);
592596
}
593597

594598
return updated;
595-
};
599+
}
596600

597601
// Must be explicitly called for the worker to run on this host.
598602
export const initSendTransactionWorker = () => {

0 commit comments

Comments
 (0)