Skip to content

Commit 4851842

Browse files
authored
transfer route AA fixes + experimental mine worker polling interval env vars (#897)
* transfer route fixes + experimental mine worker polling interval env vars * lint
1 parent fa4369a commit 4851842

File tree

3 files changed

+73
-30
lines changed

3 files changed

+73
-30
lines changed

src/server/routes/backend-wallet/transfer.ts

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ import {
99
type Address,
1010
} from "thirdweb";
1111
import { transfer as transferERC20 } from "thirdweb/extensions/erc20";
12-
import { isContractDeployed, resolvePromisedValue } from "thirdweb/utils";
12+
import { isContractDeployed } from "thirdweb/utils";
1313
import { getChain } from "../../../shared/utils/chain";
14-
import { normalizeAddress } from "../../../shared/utils/primitive-types";
14+
import {
15+
getChecksumAddress,
16+
normalizeAddress,
17+
} from "../../../shared/utils/primitive-types";
1518
import { thirdwebClient } from "../../../shared/utils/sdk";
1619
import { insertTransaction } from "../../../shared/utils/transaction/insert-transaction";
20+
import { queueTransaction } from "../../../shared/utils/transaction/queue-transation";
1721
import type { InsertedTransaction } from "../../../shared/utils/transaction/types";
1822
import { createCustomError } from "../../middleware/error";
1923
import { AddressSchema } from "../../schemas/address";
@@ -25,7 +29,7 @@ import {
2529
} from "../../schemas/shared-api-schemas";
2630
import { txOverridesWithValueSchema } from "../../schemas/tx-overrides";
2731
import {
28-
walletHeaderSchema,
32+
walletWithAAHeaderSchema,
2933
walletWithAddressParamSchema,
3034
} from "../../schemas/wallet";
3135
import { getChainIdFromChain } from "../../utils/chain";
@@ -70,7 +74,7 @@ export async function transfer(fastify: FastifyInstance) {
7074
operationId: "transfer",
7175
params: requestSchema,
7276
body: requestBodySchema,
73-
headers: walletHeaderSchema,
77+
headers: walletWithAAHeaderSchema,
7478
querystring: requestQuerystringSchema,
7579
response: {
7680
...standardResponseSchema,
@@ -88,31 +92,50 @@ export async function transfer(fastify: FastifyInstance) {
8892
const {
8993
"x-backend-wallet-address": walletAddress,
9094
"x-idempotency-key": idempotencyKey,
95+
"x-account-address": accountAddress,
96+
"x-account-factory-address": accountFactoryAddress,
97+
"x-account-salt": accountSalt,
9198
"x-transaction-mode": transactionMode,
92-
} = request.headers as Static<typeof walletHeaderSchema>;
99+
} = request.headers as Static<typeof walletWithAAHeaderSchema>;
93100
const { simulateTx: shouldSimulate } = request.query;
94101

95102
// Resolve inputs.
96103
const currencyAddress = normalizeAddress(_currencyAddress);
97104
const chainId = await getChainIdFromChain(chain);
98105

99-
let insertedTransaction: InsertedTransaction;
106+
let queueId: string;
100107
if (
101108
currencyAddress === ZERO_ADDRESS ||
102109
currencyAddress === NATIVE_TOKEN_ADDRESS
103110
) {
104-
insertedTransaction = {
105-
isUserOp: false,
111+
// Native token transfer - use insertTransaction directly
112+
const insertedTransaction: InsertedTransaction = {
106113
chainId,
107114
from: walletAddress as Address,
108115
to: to as Address,
109116
data: "0x",
110117
value: toWei(amount),
111-
extension: "none",
112-
functionName: "transfer",
113118
transactionMode,
114119
...parseTransactionOverrides(txOverrides),
120+
...(accountAddress
121+
? {
122+
isUserOp: true,
123+
accountAddress: getChecksumAddress(accountAddress),
124+
signerAddress: getChecksumAddress(walletAddress),
125+
target: getChecksumAddress(to),
126+
accountFactoryAddress: getChecksumAddress(
127+
accountFactoryAddress,
128+
),
129+
accountSalt,
130+
}
131+
: { isUserOp: false }),
115132
};
133+
134+
queueId = await insertTransaction({
135+
insertedTransaction,
136+
idempotencyKey,
137+
shouldSimulate,
138+
});
116139
} else {
117140
const contract = getContract({
118141
client: thirdwebClient,
@@ -131,31 +154,25 @@ export async function transfer(fastify: FastifyInstance) {
131154
);
132155
}
133156

157+
// ERC20 token transfer - use queueTransaction with PreparedTransaction
134158
const transaction = transferERC20({ contract, to, amount });
135159

136-
insertedTransaction = {
137-
isUserOp: false,
138-
chainId,
139-
from: walletAddress as Address,
140-
to: (await resolvePromisedValue(transaction.to)) as
141-
| Address
142-
| undefined,
143-
data: await resolvePromisedValue(transaction.data),
144-
value: 0n,
145-
extension: "erc20",
160+
queueId = await queueTransaction({
161+
transaction,
162+
fromAddress: getChecksumAddress(walletAddress),
163+
toAddress: getChecksumAddress(to),
164+
accountAddress: getChecksumAddress(accountAddress),
165+
accountFactoryAddress: getChecksumAddress(accountFactoryAddress),
166+
accountSalt,
167+
txOverrides,
168+
idempotencyKey,
169+
shouldSimulate,
146170
functionName: "transfer",
147-
functionArgs: [to, amount, currencyAddress],
171+
extension: "erc20",
148172
transactionMode,
149-
...parseTransactionOverrides(txOverrides),
150-
};
173+
});
151174
}
152175

153-
const queueId = await insertTransaction({
154-
insertedTransaction,
155-
idempotencyKey,
156-
shouldSimulate,
157-
});
158-
159176
reply.status(StatusCodes.OK).send({
160177
result: {
161178
queueId,

src/shared/utils/env.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ export const env = createEnv({
103103
.default(30 * 60),
104104
// Sets the max gas price for a transaction attempt. Most RPCs reject transactions above a certain gas price. Default: 10^18 wei.
105105
EXPERIMENTAL__MAX_GAS_PRICE_WEI: z.coerce.number().default(10 ** 18),
106+
EXPERIMENTAL__MINE_WORKER_BASE_POLL_INTERVAL_SECONDS: z.coerce
107+
.number()
108+
.default(2),
109+
EXPERIMENTAL__MINE_WORKER_MAX_POLL_INTERVAL_SECONDS: z.coerce
110+
.number()
111+
.default(20),
112+
EXPERIMENTAL__MINE_WORKER_POLL_INTERVAL_SCALING_FACTOR: z.coerce
113+
.number()
114+
.gt(0.0, "scaling factor must be greater than 0")
115+
.default(1.0),
106116
},
107117
clientPrefix: "NEVER_USED",
108118
client: {},
@@ -151,6 +161,12 @@ export const env = createEnv({
151161
CUSTOM_HMAC_AUTH_CLIENT_ID: process.env.CUSTOM_HMAC_AUTH_CLIENT_ID,
152162
CUSTOM_HMAC_AUTH_CLIENT_SECRET: process.env.CUSTOM_HMAC_AUTH_CLIENT_SECRET,
153163
ACCOUNT_CACHE_SIZE: process.env.ACCOUNT_CAHCE_SIZE,
164+
EXPERIMENTAL__MINE_WORKER_BASE_POLL_INTERVAL_SECONDS:
165+
process.env.EXPERIMENTAL__MINE_WORKER_BASE_POLL_INTERVAL_SECONDS,
166+
EXPERIMENTAL__MINE_WORKER_MAX_POLL_INTERVAL_SECONDS:
167+
process.env.EXPERIMENTAL__MINE_WORKER_MAX_POLL_INTERVAL_SECONDS,
168+
EXPERIMENTAL__MINE_WORKER_POLL_INTERVAL_SCALING_FACTOR:
169+
process.env.EXPERIMENTAL__MINE_WORKER_POLL_INTERVAL_SCALING_FACTOR,
154170
},
155171
onValidationError: (error: ZodError) => {
156172
console.error(

src/worker/tasks/mine-transaction-worker.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,13 @@ const _notifyIfLowBalance = async (transaction: MinedTransaction) => {
309309
}
310310
};
311311

312+
const SCALING_FACTOR =
313+
env.EXPERIMENTAL__MINE_WORKER_POLL_INTERVAL_SCALING_FACTOR;
314+
const MAX_POLL_INTERVAL_MS =
315+
env.EXPERIMENTAL__MINE_WORKER_MAX_POLL_INTERVAL_SECONDS * 1000;
316+
const BASE_POLL_INTERVAL_MS =
317+
env.EXPERIMENTAL__MINE_WORKER_BASE_POLL_INTERVAL_SECONDS * 1000;
318+
312319
// Must be explicitly called for the worker to run on this host.
313320
export const initMineTransactionWorker = () => {
314321
const _worker = new Worker(MineTransactionQueue.q.name, handler, {
@@ -317,7 +324,10 @@ export const initMineTransactionWorker = () => {
317324
settings: {
318325
backoffStrategy: (attemptsMade: number) => {
319326
// Retries at 2s, 4s, 6s, ..., 18s, 20s, 20s, 20s, ...
320-
return Math.min(attemptsMade * 2_000, 20_000);
327+
return Math.min(
328+
attemptsMade * BASE_POLL_INTERVAL_MS * SCALING_FACTOR, // 2_000 default * 1.0 = 2_000
329+
MAX_POLL_INTERVAL_MS, // 20_000 default
330+
);
321331
},
322332
},
323333
});

0 commit comments

Comments
 (0)