From 90f5fde29763f7a1a33b6a13ef529bed4bbc4a07 Mon Sep 17 00:00:00 2001 From: Phillip Ho Date: Fri, 1 Nov 2024 12:50:57 +0800 Subject: [PATCH] fix: Support missing 'function' prefix in functionName --- src/server/routes/contract/write/write.ts | 19 ++++++++++--- src/server/utils/abi.ts | 8 +++++- test/e2e/tests/{ => routes}/write.test.ts | 34 +++++++++++++++++++---- 3 files changed, 50 insertions(+), 11 deletions(-) rename test/e2e/tests/{ => routes}/write.test.ts (83%) diff --git a/src/server/routes/contract/write/write.ts b/src/server/routes/contract/write/write.ts index f58990c30..b91c5b94b 100644 --- a/src/server/routes/contract/write/write.ts +++ b/src/server/routes/contract/write/write.ts @@ -19,17 +19,26 @@ import { requiredAddress, walletWithAAHeaderSchema, } from "../../../schemas/wallet"; -import { sanitizeAbi } from "../../../utils/abi"; +import { sanitizeAbi, sanitizeFunctionName } from "../../../utils/abi"; import { getChainIdFromChain } from "../../../utils/chain"; import { parseTransactionOverrides } from "../../../utils/transactionOverrides"; // INPUT const writeRequestBodySchema = Type.Object({ functionName: Type.String({ - description: "The function to call on the contract", + 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: "The arguments to call on the function", + 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), @@ -84,7 +93,8 @@ export async function writeToContract(fastify: FastifyInstance) { // this is all handled inside the `resolveMethod` function let method: AbiFunction; try { - method = await resolveMethod(functionName)(contract); + const functionNameOrSignature = sanitizeFunctionName(functionName); + method = await resolveMethod(functionNameOrSignature)(contract); } catch (e) { throw createCustomError( prettifyError(e), @@ -92,6 +102,7 @@ export async function writeToContract(fastify: FastifyInstance) { "BAD_REQUEST", ); } + const transaction = prepareContractCall({ contract, method, diff --git a/src/server/utils/abi.ts b/src/server/utils/abi.ts index 2edd27be2..cedb50414 100644 --- a/src/server/utils/abi.ts +++ b/src/server/utils/abi.ts @@ -2,7 +2,10 @@ import type { Abi } from "thirdweb/utils"; import type { AbiSchemaType } from "../schemas/contract"; export function sanitizeAbi(abi: AbiSchemaType | undefined): Abi | undefined { - if (!abi) return undefined; + if (!abi) { + return undefined; + } + return abi.map((item) => { if (item.type === "function") { return { @@ -15,3 +18,6 @@ export function sanitizeAbi(abi: AbiSchemaType | undefined): Abi | undefined { return item; }) as Abi; } + +export const sanitizeFunctionName = (val: string) => + val.includes("(") && !val.startsWith("function ") ? `function ${val}` : val; diff --git a/test/e2e/tests/write.test.ts b/test/e2e/tests/routes/write.test.ts similarity index 83% rename from test/e2e/tests/write.test.ts rename to test/e2e/tests/routes/write.test.ts index bfe72b146..06aa36cea 100644 --- a/test/e2e/tests/write.test.ts +++ b/test/e2e/tests/routes/write.test.ts @@ -2,13 +2,13 @@ import { beforeAll, describe, expect, test } from "bun:test"; import assert from "node:assert"; import type { Address } from "thirdweb"; import { zeroAddress } from "viem"; -import type { ApiError } from "../../../sdk/dist/thirdweb-dev-engine.cjs"; -import { CONFIG } from "../config"; -import type { setupEngine } from "../utils/engine"; -import { pollTransactionStatus } from "../utils/transactions"; -import { setup } from "./setup"; +import type { ApiError } from "../../../../sdk/dist/thirdweb-dev-engine.cjs.js"; +import { CONFIG } from "../../config"; +import type { setupEngine } from "../../utils/engine"; +import { pollTransactionStatus } from "../../utils/transactions"; +import { setup } from "../setup"; -describe("Write Tests", () => { +describe("/contract/write route", () => { let tokenContractAddress: string; let engine: ReturnType; let backendWallet: Address; @@ -91,6 +91,28 @@ describe("Write Tests", () => { expect(writeTransactionStatus.minedAt).toBeDefined(); }); + test("Write to a contract with function signature without prefix", async () => { + const writeRes = await engine.contract.write( + CONFIG.CHAIN.id.toString(), + tokenContractAddress, + backendWallet, + { + functionName: "setContractURI(string uri)", + args: ["https://signature-test.com"], + }, + ); + + expect(writeRes.result.queueId).toBeDefined(); + + const writeTransactionStatus = await pollTransactionStatus( + engine, + writeRes.result.queueId, + true, + ); + + expect(writeTransactionStatus.minedAt).toBeDefined(); + }); + test("Write to a contract with function abi", async () => { const writeRes = await engine.contract.write( CONFIG.CHAIN.id.toString(),