Skip to content

Commit

Permalink
refactor abi handling for write transactions (#80)
Browse files Browse the repository at this point in the history
* use strong types on transaction write abis

* add changeset
  • Loading branch information
alecananian authored Aug 8, 2024
1 parent 94cba4c commit 9e56447
Show file tree
Hide file tree
Showing 11 changed files with 307 additions and 211 deletions.
5 changes: 5 additions & 0 deletions .changeset/twelve-masks-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@treasure-dev/tdk-core": patch
---

Added strict typing on Magicswap argument utils
1 change: 1 addition & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@thirdweb-dev/engine": "^0.0.12",
"@treasure-dev/tdk-core": "*",
"@wagmi/core": "^2.9.1",
"abitype": "^1.0.5",
"dotenv": "^16.3.1",
"fastify": "^4.24.3",
"thirdweb": "^5.15.0",
Expand Down
107 changes: 43 additions & 64 deletions apps/api/src/routes/magicswap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as Sentry from "@sentry/node";
import {
fetchPool,
fetchPools,
Expand Down Expand Up @@ -43,6 +42,7 @@ import {
TdkError,
parseEngineErrorMessage,
} from "../utils/error";
import { writeTransaction } from "../utils/transaction";

export const magicswapRoutes =
({ env, wagmiConfig, engine }: TdkApiContext): FastifyPluginAsync =>
Expand Down Expand Up @@ -229,28 +229,21 @@ export const magicswapRoutes =
}

try {
Sentry.setExtra(
"transaction",
JSON.stringify(swapArguments, null, 2),
);
const { result } = await engine.contract.write(
chainId.toString(),
swapArguments.address,
const result = await writeTransaction({
engine,
chainId,
contractAddress: swapArguments.address,
backendWallet,
{
abi: magicSwapV2RouterABI,
functionName: swapArguments.functionName,
args: swapArguments.args as string[],
txOverrides: swapArguments.value
? {
value: swapArguments.value.toString(),
}
: undefined,
},
false,
undefined,
userAddress,
);
smartAccountAddress: userAddress,
abi: magicSwapV2RouterABI,
functionName: swapArguments.functionName,
args: swapArguments.args,
txOverrides: swapArguments.value
? {
value: swapArguments.value.toString(),
}
: undefined,
});
reply.send(result);
} catch (err) {
throw new TdkError({
Expand Down Expand Up @@ -334,28 +327,21 @@ export const magicswapRoutes =
}

try {
Sentry.setExtra(
"transaction",
JSON.stringify(addLiquidityArgs, null, 2),
);
const { result } = await engine.contract.write(
chainId.toString(),
addLiquidityArgs.address,
const result = await writeTransaction({
engine,
chainId,
contractAddress: addLiquidityArgs.address,
backendWallet,
{
abi: magicSwapV2RouterABI,
functionName: addLiquidityArgs.functionName,
args: addLiquidityArgs.args as string[],
txOverrides: addLiquidityArgs.value
? {
value: addLiquidityArgs.value.toString(),
}
: undefined,
},
false,
undefined,
userAddress,
);
smartAccountAddress: userAddress,
abi: magicSwapV2RouterABI,
functionName: addLiquidityArgs.functionName,
args: addLiquidityArgs.args,
txOverrides: addLiquidityArgs.value
? {
value: addLiquidityArgs.value.toString(),
}
: undefined,
});
reply.send(result);
} catch (err) {
throw new TdkError({
Expand Down Expand Up @@ -439,28 +425,21 @@ export const magicswapRoutes =
}

try {
Sentry.setExtra(
"transaction",
JSON.stringify(removeLiquidityArgs, null, 2),
);
const { result } = await engine.contract.write(
chainId.toString(),
removeLiquidityArgs.address,
const result = await writeTransaction({
engine,
chainId,
contractAddress: removeLiquidityArgs.address,
backendWallet,
{
abi: magicSwapV2RouterABI,
functionName: removeLiquidityArgs.functionName,
args: removeLiquidityArgs.args as string[],
txOverrides: removeLiquidityArgs.value
? {
value: removeLiquidityArgs.value.toString(),
}
: undefined,
},
false,
undefined,
userAddress,
);
smartAccountAddress: userAddress,
abi: magicSwapV2RouterABI,
functionName: removeLiquidityArgs.functionName,
args: removeLiquidityArgs.args,
txOverrides: removeLiquidityArgs.value
? {
value: removeLiquidityArgs.value.toString(),
}
: undefined,
});
reply.send(result);
} catch (err) {
throw new TdkError({
Expand Down
84 changes: 44 additions & 40 deletions apps/api/src/schema/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,46 @@ const abiSchema = Type.Object({
stateMutability: Type.Optional(Type.String()),
});

const txOverridesSchema = Type.Object({
value: Type.Optional(
Type.String({
examples: ["10000000000"],
description: "Amount of native currency to send",
}),
),
gas: Type.Optional(
Type.String({
examples: ["530000"],
description: "Gas limit for the transaction",
}),
),
maxFeePerGas: Type.Optional(
Type.String({
examples: ["1000000000"],
description: "Maximum fee per gas",
}),
),
maxPriorityFeePerGas: Type.Optional(
Type.String({
examples: ["1000000000"],
description: "Maximum priority fee per gas",
}),
),
});

const txArgsSchema = Type.Array(
Type.Union([
Type.String({
description: "The arguments to call on the function",
examples: [[EXAMPLE_WALLET_ADDRESS, "1000000000000000000"]],
}),
Type.Tuple([Type.String(), Type.String()]),
Type.Object({}),
Type.Array(Type.Any()),
Type.Any(),
]),
);

export const createTransactionBodySchema = Type.Object({
address: Type.String({
description: "The address of the contract to call",
Expand All @@ -40,46 +80,8 @@ export const createTransactionBodySchema = Type.Object({
description: "The function to call on the contract",
examples: ["transfer"],
}),
args: Type.Array(
Type.Union([
Type.String({
description: "The arguments to call on the function",
examples: [[EXAMPLE_WALLET_ADDRESS, "1000000000000000000"]],
}),
Type.Tuple([Type.String(), Type.String()]),
Type.Object({}),
Type.Array(Type.Any()),
Type.Any(),
]),
),
txOverrides: Type.Optional(
Type.Object({
value: Type.Optional(
Type.String({
examples: ["10000000000"],
description: "Amount of native currency to send",
}),
),
gas: Type.Optional(
Type.String({
examples: ["530000"],
description: "Gas limit for the transaction",
}),
),
maxFeePerGas: Type.Optional(
Type.String({
examples: ["1000000000"],
description: "Maximum fee per gas",
}),
),
maxPriorityFeePerGas: Type.Optional(
Type.String({
examples: ["1000000000"],
description: "Maximum priority fee per gas",
}),
),
}),
),
args: txArgsSchema,
txOverrides: Type.Optional(txOverridesSchema),
backendWallet: Type.Optional(Type.String()),
});

Expand Down Expand Up @@ -119,6 +121,8 @@ export const readTransactionReplySchema = Type.Object({
errorMessage: nullableStringSchema,
});

export type TransactionArguments = Static<typeof txArgsSchema>;
export type TransactionOverrides = Static<typeof txOverridesSchema>;
export type CreateTransactionBody = Static<typeof createTransactionBodySchema>;
export type CreateTransactionReply = Static<
typeof createTransactionReplySchema
Expand Down
84 changes: 84 additions & 0 deletions apps/api/src/utils/transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import * as Sentry from "@sentry/node";
import type { Engine } from "@thirdweb-dev/engine";
import type {
Abi,
AbiParametersToPrimitiveTypes,
ExtractAbiFunction,
ExtractAbiFunctionNames,
} from "abitype";

import type { TransactionArguments, TransactionOverrides } from "../schema";

export const writeTransaction = async <
TAbi extends Abi,
TFunctionName extends ExtractAbiFunctionNames<
TAbi,
"nonpayable" | "payable"
> = string,
>({
engine,
chainId,
contractAddress,
backendWallet,
smartAccountAddress,
functionName,
args,
txOverrides,
simulateTransaction = false,
idempotencyKey,
...rest
}: {
engine: Engine;
chainId: number;
contractAddress: string;
backendWallet: string;
smartAccountAddress: string;
txOverrides?: TransactionOverrides;
simulateTransaction?: boolean;
idempotencyKey?: string;
} & (
| {
abi: TAbi;
functionName: TFunctionName;
args: AbiParametersToPrimitiveTypes<
ExtractAbiFunction<TAbi, TFunctionName>["inputs"],
"inputs"
>;
}
| {
functionName: string;
args: TransactionArguments | Readonly<TransactionArguments>;
}
)) => {
const abi = "abi" in rest ? rest.abi : undefined;
Sentry.setExtra(
"transaction",
JSON.stringify(
{
contractAddress,
functionName,
abi: !!abi,
args,
},
null,
2,
),
);
const { result } = await engine.contract.write(
chainId.toString(),
contractAddress,
backendWallet,
{
// @ts-ignore: stronger type-checking than Engine
abi,
functionName,
// @ts-ignore: stronger type-checking than Engine
args,
txOverrides,
},
simulateTransaction,
idempotencyKey,
smartAccountAddress,
);
return result;
};
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/core/src/abis/magicSwapV2RouterAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,4 +797,4 @@ export const magicSwapV2RouterABI = [
stateMutability: "nonpayable",
type: "function",
},
];
] as const;
Loading

0 comments on commit 9e56447

Please sign in to comment.