Skip to content

Commit d0b372e

Browse files
committed
unused nonces
1 parent 690c417 commit d0b372e

File tree

18 files changed

+149
-227
lines changed

18 files changed

+149
-227
lines changed

src/db/wallets/walletNonce.ts

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,11 @@ import { getChain } from "../../utils/chain";
33
import { redis } from "../../utils/redis/redis";
44
import { thirdwebClient } from "../../utils/sdk";
55

6-
/**
7-
* !IMPORTANT
8-
*
9-
* The nonce stored is the LAST USED NONCE.
10-
* To get the next available nonce, use `incrWalletNonce()`.
11-
*/
12-
6+
// Data type: String
137
const lastUsedNonceKey = (chainId: number, walletAddress: Address) =>
148
`nonce:${chainId}:${walletAddress}`;
159

10+
// Data type: Array of String
1611
const unusedNoncesKey = (chainId: number, walletAddress: Address) =>
1712
`nonce-unused:${chainId}:${walletAddress}`;
1813

@@ -23,13 +18,10 @@ const unusedNoncesKey = (chainId: number, walletAddress: Address) =>
2318
* @param walletAddress
2419
* @returns number - The next unused nonce value for this wallet.
2520
*/
26-
export const getAndUpdateNonce = async (
27-
chainId: number,
28-
walletAddress: Address,
29-
) => {
21+
export const acquireNonce = async (chainId: number, walletAddress: Address) => {
3022
// Acquire an unused nonce, if any.
31-
let nonce = await getAndUpdateLowestUnusedNonce(chainId, walletAddress);
32-
if (nonce) {
23+
let nonce = await _acquireUnusedNonce(chainId, walletAddress);
24+
if (nonce !== null) {
3325
return nonce;
3426
}
3527

@@ -51,32 +43,29 @@ export const getAndUpdateNonce = async (
5143
* @param walletAddress
5244
* @param nonce
5345
*/
54-
export const addUnusedNonce = async (
46+
export const releaseNonce = async (
5547
chainId: number,
5648
walletAddress: Address,
5749
nonce: number,
5850
) => {
51+
if (isNaN(nonce) || nonce < 3) {
52+
throw new Error(`**** NONCE IS BAD WHY???: ${nonce}`);
53+
}
54+
5955
const key = unusedNoncesKey(chainId, walletAddress);
60-
await redis.zadd(key, nonce, nonce);
56+
await redis.rpush(key, nonce);
6157
};
6258

6359
/**
64-
* Acquires the lowest unused nonce.
60+
* Acquires an unused nonce.
6561
* @param chainId
6662
* @param walletAddress
6763
* @returns
6864
*/
69-
export const getAndUpdateLowestUnusedNonce = async (
70-
chainId: number,
71-
walletAddress: Address,
72-
) => {
65+
const _acquireUnusedNonce = async (chainId: number, walletAddress: Address) => {
7366
const key = unusedNoncesKey(chainId, walletAddress);
74-
const res = await redis.zpopmin(key);
75-
if (res.length > 0) {
76-
// res will be [value, score] where the score is the nonce.
77-
return parseInt(res[1]);
78-
}
79-
return null;
67+
const res = await redis.lpop(key);
68+
return res ? parseInt(res) : null;
8069
};
8170

8271
const _syncNonce = async (
@@ -106,7 +95,7 @@ const _syncNonce = async (
10695
* @param walletAddress
10796
* @returns number - The last used nonce value for this wallet.
10897
*/
109-
export const getNonce = async (chainId: number, walletAddress: Address) => {
98+
export const inspectNonce = async (chainId: number, walletAddress: Address) => {
11099
const key = lastUsedNonceKey(chainId, walletAddress);
111100
const nonce = await redis.get(key);
112101
return nonce ? parseInt(nonce) : 0;
@@ -116,7 +105,10 @@ export const getNonce = async (chainId: number, walletAddress: Address) => {
116105
* Delete all wallet nonces. Useful when the get out of sync.
117106
*/
118107
export const deleteAllNonces = async () => {
119-
const keys = await redis.keys("nonce:*");
108+
const keys = [
109+
...(await redis.keys("nonce:*")),
110+
...(await redis.keys("nonce-unused:*")),
111+
];
120112
if (keys.length > 0) {
121113
await redis.del(keys);
122114
}

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,9 @@ import {
66
currencyValueSchema,
77
standardResponseSchema,
88
} from "../../schemas/sharedApiSchemas";
9-
import { walletParamSchema } from "../../schemas/wallet";
9+
import { walletWithAddressParamSchema } from "../../schemas/wallet";
1010
import { getChainIdFromChain } from "../../utils/chain";
1111

12-
// INPUTS
13-
const requestSchema = walletParamSchema;
14-
15-
// OUTPUT
1612
const responseSchema = Type.Object({
1713
result: Type.Object({
1814
walletAddress: Type.String(),
@@ -33,7 +29,7 @@ responseSchema.example = {
3329

3430
export async function getBalance(fastify: FastifyInstance) {
3531
fastify.route<{
36-
Params: Static<typeof requestSchema>;
32+
Params: Static<typeof walletWithAddressParamSchema>;
3733
Reply: Static<typeof responseSchema>;
3834
}>({
3935
method: "GET",
@@ -43,7 +39,7 @@ export async function getBalance(fastify: FastifyInstance) {
4339
description: "Get the native balance for a backend wallet.",
4440
tags: ["Backend Wallet"],
4541
operationId: "getBalance",
46-
params: requestSchema,
42+
params: walletWithAddressParamSchema,
4743
response: {
4844
...standardResponseSchema,
4945
[StatusCodes.OK]: responseSchema,

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { Static, Type } from "@sinclair/typebox";
22
import { FastifyInstance } from "fastify";
33
import { StatusCodes } from "http-status-codes";
44
import { Address } from "thirdweb";
5-
import { getNonce } from "../../../db/wallets/walletNonce";
5+
import { inspectNonce } from "../../../db/wallets/walletNonce";
66
import { standardResponseSchema } from "../../schemas/sharedApiSchemas";
7-
import { walletParamSchema } from "../../schemas/wallet";
7+
import { walletWithAddressParamSchema } from "../../schemas/wallet";
88
import { getChainIdFromChain } from "../../utils/chain";
99

10-
const requestSchema = walletParamSchema;
10+
const requestSchema = walletWithAddressParamSchema;
1111

1212
const responseSchema = Type.Object({
1313
result: Type.Object({
@@ -43,7 +43,7 @@ export const getBackendWalletNonce = async (fastify: FastifyInstance) => {
4343
handler: async (req, reply) => {
4444
const { chain, walletAddress } = req.params;
4545
const chainId = await getChainIdFromChain(chain);
46-
const nonce = await getNonce(chainId, walletAddress as Address);
46+
const nonce = await inspectNonce(chainId, walletAddress as Address);
4747

4848
reply.status(StatusCodes.OK).send({
4949
result: {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
TransactionSchema,
88
toTransactionSchema,
99
} from "../../schemas/transaction";
10-
import { walletParamSchema } from "../../schemas/wallet";
10+
import { walletWithAddressParamSchema } from "../../schemas/wallet";
1111
import { getChainIdFromChain } from "../../utils/chain";
1212

13-
const ParamsSchema = walletParamSchema;
13+
const ParamsSchema = walletWithAddressParamSchema;
1414

1515
const responseBodySchema = Type.Object({
1616
result: Type.Object({

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import {
1010
transactionWritesResponseSchema,
1111
} from "../../schemas/sharedApiSchemas";
1212
import { txOverridesSchema } from "../../schemas/txOverrides";
13-
import { walletWithAAHeaderSchema } from "../../schemas/wallet";
13+
import {
14+
walletChainParamSchema,
15+
walletWithAAHeaderSchema,
16+
} from "../../schemas/wallet";
1417
import { getChainIdFromChain } from "../../utils/chain";
1518

16-
const ParamsSchema = Type.Object({
17-
chain: Type.String(),
18-
});
19-
2019
const requestBodySchema = Type.Object({
2120
toAddress: Type.Optional(
2221
Type.String({
@@ -45,7 +44,7 @@ requestBodySchema.examples = [
4544

4645
export async function sendTransaction(fastify: FastifyInstance) {
4746
fastify.route<{
48-
Params: Static<typeof ParamsSchema>;
47+
Params: Static<typeof walletChainParamSchema>;
4948
Body: Static<typeof requestBodySchema>;
5049
Reply: Static<typeof transactionWritesResponseSchema>;
5150
Querystring: Static<typeof requestQuerystringSchema>;
@@ -57,7 +56,7 @@ export async function sendTransaction(fastify: FastifyInstance) {
5756
description: "Send a transaction with transaction parameters",
5857
tags: ["Backend Wallet"],
5958
operationId: "sendTransaction",
60-
params: ParamsSchema,
59+
params: walletChainParamSchema,
6160
body: requestBodySchema,
6261
headers: walletWithAAHeaderSchema,
6362
querystring: requestQuerystringSchema,
@@ -76,8 +75,6 @@ export async function sendTransaction(fastify: FastifyInstance) {
7675
"x-account-address": accountAddress,
7776
} = request.headers as Static<typeof walletWithAAHeaderSchema>;
7877

79-
// Standardize
80-
8178
const chainId = await getChainIdFromChain(chain);
8279

8380
let queueId: string;

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

Lines changed: 45 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { Static, Type } from "@sinclair/typebox";
22
import { FastifyInstance } from "fastify";
33
import { StatusCodes } from "http-status-codes";
4-
import { createCustomError } from "../../middleware/error";
4+
import { Address, Hex } from "thirdweb";
5+
import { maybeBigInt } from "../../../utils/primitiveTypes";
6+
import { insertTransaction } from "../../../utils/transaction/insertTransaction";
57
import { standardResponseSchema } from "../../schemas/sharedApiSchemas";
68
import { txOverridesWithValueSchema } from "../../schemas/txOverrides";
7-
import { walletHeaderSchema } from "../../schemas/wallet";
8-
9-
const ParamsSchema = Type.Object({
10-
chain: Type.String(),
11-
});
9+
import {
10+
walletChainParamSchema,
11+
walletHeaderSchema,
12+
} from "../../schemas/wallet";
13+
import { getChainIdFromChain } from "../../utils/chain";
1214

1315
const requestBodySchema = Type.Array(
1416
Type.Object({
@@ -29,14 +31,13 @@ const requestBodySchema = Type.Array(
2931

3032
const responseBodySchema = Type.Object({
3133
result: Type.Object({
32-
groupId: Type.String(),
3334
queueIds: Type.Array(Type.String()),
3435
}),
3536
});
3637

3738
export async function sendTransactionBatch(fastify: FastifyInstance) {
3839
fastify.route<{
39-
Params: Static<typeof ParamsSchema>;
40+
Params: Static<typeof walletChainParamSchema>;
4041
Body: Static<typeof requestBodySchema>;
4142
Reply: Static<typeof responseBodySchema>;
4243
}>({
@@ -48,22 +49,50 @@ export async function sendTransactionBatch(fastify: FastifyInstance) {
4849
"Send a batch of raw transactions with transaction parameters",
4950
tags: ["Backend Wallet"],
5051
operationId: "sendTransactionBatch",
51-
params: ParamsSchema,
52+
params: walletChainParamSchema,
5253
body: requestBodySchema,
5354
headers: walletHeaderSchema,
5455
response: {
5556
...standardResponseSchema,
5657
[StatusCodes.OK]: responseBodySchema,
5758
},
58-
hide: true,
59-
deprecated: true,
6059
},
6160
handler: async (request, reply) => {
62-
throw createCustomError(
63-
"This endpoint is deprecated",
64-
StatusCodes.GONE,
65-
"ENDPOINT_DEPRECATED",
66-
);
61+
const { chain } = request.params;
62+
const { "x-backend-wallet-address": fromAddress } =
63+
request.headers as Static<typeof walletHeaderSchema>;
64+
const chainId = await getChainIdFromChain(chain);
65+
66+
const transactionRequests = request.body;
67+
68+
const queueIds: string[] = [];
69+
for (const transactionRequest of transactionRequests) {
70+
const { toAddress, data, value, txOverrides } = transactionRequest;
71+
72+
const queueId = await insertTransaction({
73+
insertedTransaction: {
74+
isUserOp: false,
75+
chainId,
76+
from: fromAddress as Address,
77+
to: toAddress as Address | undefined,
78+
data: data as Hex,
79+
value: BigInt(value),
80+
81+
gas: maybeBigInt(txOverrides?.gas),
82+
maxFeePerGas: maybeBigInt(txOverrides?.maxFeePerGas),
83+
maxPriorityFeePerGas: maybeBigInt(
84+
txOverrides?.maxPriorityFeePerGas,
85+
),
86+
},
87+
});
88+
queueIds.push(queueId);
89+
}
90+
91+
reply.status(StatusCodes.OK).send({
92+
result: {
93+
queueIds,
94+
},
95+
});
6796
},
6897
});
6998
}

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,12 @@ import {
1010
simulateResponseSchema,
1111
standardResponseSchema,
1212
} from "../../schemas/sharedApiSchemas";
13-
import { walletWithAAHeaderSchema } from "../../schemas/wallet";
13+
import {
14+
walletChainParamSchema,
15+
walletWithAAHeaderSchema,
16+
} from "../../schemas/wallet";
1417
import { getChainIdFromChain } from "../../utils/chain";
1518

16-
// INPUT
17-
const ParamsSchema = Type.Object({
18-
chain: Type.String({
19-
examples: ["80002"],
20-
description: "Chain ID",
21-
}),
22-
});
23-
2419
const simulateRequestBodySchema = Type.Object({
2520
toAddress: Type.String({
2621
description: "The contract address",
@@ -58,10 +53,9 @@ const simulateRequestBodySchema = Type.Object({
5853
),
5954
});
6055

61-
// LOGIC
6256
export async function simulateTransaction(fastify: FastifyInstance) {
6357
fastify.route<{
64-
Params: Static<typeof ParamsSchema>;
58+
Params: Static<typeof walletChainParamSchema>;
6559
Body: Static<typeof simulateRequestBodySchema>;
6660
Reply: Static<typeof simulateResponseSchema>;
6761
}>({
@@ -72,7 +66,7 @@ export async function simulateTransaction(fastify: FastifyInstance) {
7266
description: "Simulate a transaction with transaction parameters",
7367
tags: ["Backend Wallet"],
7468
operationId: "simulateTransaction",
75-
params: ParamsSchema,
69+
params: walletChainParamSchema,
7670
body: simulateRequestBodySchema,
7771
headers: walletWithAAHeaderSchema,
7872
response: {

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@ import {
2323
transactionWritesResponseSchema,
2424
} from "../../schemas/sharedApiSchemas";
2525
import { txOverridesWithValueSchema } from "../../schemas/txOverrides";
26-
import { walletHeaderSchema, walletParamSchema } from "../../schemas/wallet";
26+
import {
27+
walletHeaderSchema,
28+
walletWithAddressParamSchema,
29+
} from "../../schemas/wallet";
2730
import { getChainIdFromChain } from "../../utils/chain";
2831

2932
// INPUTS
30-
const requestSchema = Type.Omit(walletParamSchema, ["walletAddress"]);
33+
const requestSchema = Type.Omit(walletWithAddressParamSchema, [
34+
"walletAddress",
35+
]);
3136
const requestBodySchema = Type.Object({
3237
to: Type.String({
3338
description: "Address of the wallet to transfer to",

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ import {
1616
requestQuerystringSchema,
1717
standardResponseSchema,
1818
} from "../../schemas/sharedApiSchemas";
19-
import { walletHeaderSchema, walletParamSchema } from "../../schemas/wallet";
19+
import {
20+
walletHeaderSchema,
21+
walletWithAddressParamSchema,
22+
} from "../../schemas/wallet";
2023
import { getChainIdFromChain } from "../../utils/chain";
2124

22-
const ParamsSchema = Type.Omit(walletParamSchema, ["walletAddress"]);
25+
const ParamsSchema = Type.Omit(walletWithAddressParamSchema, ["walletAddress"]);
2326

2427
const requestBodySchema = Type.Object({
2528
toAddress: Type.String({

0 commit comments

Comments
 (0)