-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathwrite.ts
144 lines (137 loc) · 5.17 KB
/
write.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { type Static, Type } from "@sinclair/typebox";
import type { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import type { AbiParameters } from "ox";
import { prepareContractCall, resolveMethod } from "thirdweb";
import { type AbiFunction, parseAbiParams } from "thirdweb/utils";
import { getContractV5 } from "../../../../shared/utils/cache/get-contractv5";
import { prettifyError } from "../../../../shared/utils/error";
import { queueTransaction } from "../../../../shared/utils/transaction/queue-transation";
import { createCustomError } from "../../../middleware/error";
import { abiArraySchema } from "../../../schemas/contract";
import {
contractParamSchema,
requestQuerystringSchema,
transactionWritesResponseSchema,
} from "../../../schemas/shared-api-schemas";
import { txOverridesWithValueSchema } from "../../../schemas/tx-overrides";
import {
maybeAddress,
requiredAddress,
walletWithAAHeaderSchema,
} from "../../../schemas/wallet";
import { sanitizeAbi, sanitizeFunctionName } from "../../../utils/abi";
import { getChainIdFromChain } from "../../../utils/chain";
import { parseTransactionOverrides } from "../../../utils/transaction-overrides";
// INPUT
const writeRequestBodySchema = Type.Object({
functionName: Type.String({
description: `The function to call on the contract. It is highly recommended to provide a full function signature, such as "function mintTo(address to, uint256 amount)", to avoid ambiguity and to skip ABI resolution.`,
examples: ["function mintTo(address to, uint256 amount)"],
}),
args: Type.Array(Type.Any(), {
description:
"An array of arguments to provide the function. Supports: numbers, strings, arrays, objects. Do not provide: BigNumber, bigint, Date objects",
examples: [
[
1730380951,
"0x09530565aC1Ce08C3621f5B24Fca6d9a76574620",
["a", "b", "c"],
],
],
}),
...txOverridesWithValueSchema.properties,
abi: Type.Optional(abiArraySchema),
});
// LOGIC
export async function writeToContract(fastify: FastifyInstance) {
fastify.route<{
Body: Static<typeof writeRequestBodySchema>;
Params: Static<typeof contractParamSchema>;
Reply: Static<typeof transactionWritesResponseSchema>;
Querystring: Static<typeof requestQuerystringSchema>;
}>({
method: "POST",
url: "/contract/:chain/:contractAddress/write",
schema: {
summary: "Write to contract",
description: "Call a write function on a contract.",
tags: ["Contract"],
operationId: "write",
params: contractParamSchema,
headers: walletWithAAHeaderSchema,
querystring: requestQuerystringSchema,
body: writeRequestBodySchema,
response: {
[StatusCodes.OK]: transactionWritesResponseSchema,
},
},
handler: async (request, reply) => {
const { chain, contractAddress } = request.params;
const { simulateTx } = request.query;
const { functionName, args, txOverrides, abi } = request.body;
const {
"x-backend-wallet-address": fromAddress,
"x-account-address": accountAddress,
"x-idempotency-key": idempotencyKey,
"x-account-factory-address": accountFactoryAddress,
"x-account-salt": accountSalt,
"x-transaction-mode": transactionMode,
} = request.headers as Static<typeof walletWithAAHeaderSchema>;
const chainId = await getChainIdFromChain(chain);
const contract = await getContractV5({
chainId,
contractAddress,
abi: sanitizeAbi(abi),
});
// 3 possible ways to get function from abi:
// 1. functionName passed as solidity signature
// 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: AbiFunction;
let params: Array<string | bigint | boolean | object>;
try {
const functionNameOrSignature = sanitizeFunctionName(functionName);
method = await resolveMethod(functionNameOrSignature)(contract);
params = parseAbiParams(
method.inputs.map((i: AbiParameters.Parameter) => i.type),
args,
);
} catch (e) {
throw createCustomError(
prettifyError(e),
StatusCodes.BAD_REQUEST,
"BAD_REQUEST",
);
}
const transaction = prepareContractCall({
contract,
method,
params,
...parseTransactionOverrides(txOverrides),
});
const queueId = await queueTransaction({
functionName,
transaction,
fromAddress: requiredAddress(fromAddress, "x-backend-wallet-address"),
toAddress: maybeAddress(contractAddress, "to"),
accountAddress: maybeAddress(accountAddress, "x-account-address"),
accountFactoryAddress: maybeAddress(
accountFactoryAddress,
"x-account-factory-address",
),
accountSalt,
txOverrides,
idempotencyKey,
transactionMode,
shouldSimulate: simulateTx,
});
reply.status(StatusCodes.OK).send({
result: {
queueId,
},
});
},
});
}