Skip to content

Commit

Permalink
feat(tracer): add next false behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Rubilmax committed Oct 24, 2024
1 parent b712b34 commit 3c2008e
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 52 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ await client.traceCall({

client.transport.tracer.all = true; // If you want to trace all submitted transactions, failing or not.
client.transport.tracer.next = true; // If you want to trace the next submitted transaction.
client.transport.tracer.next = false; // If you DON'T want to trace the next submitted transaction.
client.transport.tracer.failed = false; // If you don't want to append traces to failed transactions.

```
Expand Down
4 changes: 2 additions & 2 deletions src/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { RpcCallTrace, RpcLogTrace } from "./actions/traceCall.js";

// The requested module 'colors/safe.js' is a CommonJS module, which may not support all module.exports as named exports.
// CommonJS modules can always be imported via the default export, for example using:
const { bold, cyan, grey, red, white, yellow, green, dim, black, magenta } = colors;
const { bold, cyan, grey, red, white, yellow, green, dim, magenta } = colors;

export type TraceFormatConfig = {
gas: boolean;
Expand Down Expand Up @@ -177,5 +177,5 @@ export async function formatFullTrace(trace: RpcCallTrace, config?: Partial<Trac
}
}

return formatCallTrace(trace, config);
return white(formatCallTrace(trace, config));
}
97 changes: 47 additions & 50 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { type TraceFormatConfig, formatFullTrace } from "./format.js";

export type TracerConfig = TraceFormatConfig & {
/**
* Whether to trace all transactions. Default to `false`.
* Whether to trace all transactions. Defaults to `false`.
*/
all: boolean;
/**
* Whether to trace the next submitted transaction. Default to `false`.
* Whether to trace the next submitted transaction. Defaults to `undefined`.
*/
next: boolean;
next?: boolean;
/**
* Whether to trace all failed transactions. Default to `true`.
* Whether to trace all failed transactions. Defaults to `true`.
*/
failed: boolean;
};
Expand All @@ -34,7 +34,7 @@ export type TracedTransport<transport extends Transport = Transport> = transport
*/
export function traced<transport extends Transport>(
transport: transport,
{ all = false, next = false, failed = true, gas = false }: Partial<TracerConfig> = {},
{ all = false, next, failed = true, gas = false }: Partial<TracerConfig> = {},
): TracedTransport<transport> {
// @ts-ignore: complex overload
return (...config) => {
Expand All @@ -48,65 +48,62 @@ export function traced<transport extends Transport>(
return {
...instance,
async request(args, options) {
const { method, params } = args;
switch (args.method) {
case "eth_estimateGas":
case "eth_sendTransaction": {
const { params } = args;
const { tracer } = instance.value!;

const traceCall = async () => {
const trace = await instance.request<TraceCallRpcSchema>(
{
method: "debug_traceCall",
params: [
// @ts-ignore: params[0] is the rpc transaction request
params[0],
// @ts-ignore: params[1] is either undefined or the block identifier
params[1] || "latest",
const traceCall = async () => {
const trace = await instance.request<TraceCallRpcSchema>(
{
// @ts-ignore: params[2] may contain state and block overrides
...params[2],
tracer: "callTracer",
tracerConfig: {
onlyTopCall: false,
withLog: true,
},
method: "debug_traceCall",
params: [
// @ts-ignore: params[0] is the rpc transaction request
params[0],
// @ts-ignore: params[1] is either undefined or the block identifier
params[1] || "latest",
{
// @ts-ignore: params[2] may contain state and block overrides
...params[2],
tracer: "callTracer",
tracerConfig: {
onlyTopCall: false,
withLog: true,
},
},
],
},
],
},
{ retryCount: 0 },
);

return await formatFullTrace(trace, instance.value!.tracer);
};
{ retryCount: 0 },
);

switch (method) {
case "eth_estimateGas":
case "eth_sendTransaction": {
if (instance.value?.tracer.all || instance.value?.tracer.next) {
instance.value.tracer.next = false;
return await formatFullTrace(trace, tracer);
};

if (tracer.next || (tracer.next == null && tracer.all)) {
try {
console.log(await traceCall());
} catch (error) {
console.warn(`Failed to trace transaction: ${error}`);
}
}

break;
}
}

return instance.request(args, options).catch(async (error) => {
switch (method) {
case "eth_estimateGas":
case "eth_sendTransaction": {
if (instance.value?.tracer.failed) {
throw new RawContractError({ message: `\n${await traceCall()}` });
}
return instance
.request(args, options)
.catch(async (error) => {
if (tracer.next || (tracer.next == null && tracer.failed)) {
throw new RawContractError({ message: `\n${await traceCall()}` });
}

break;
}
throw error;
})
.finally(() => {
tracer.next = undefined;
});
}

throw error;
});
default:
return instance.request(args, options);
}
},
};
};
Expand Down
50 changes: 50 additions & 0 deletions test/traceCall.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,4 +156,54 @@ describe("traceCall", () => {

consoleSpy.mockRestore();
});

test("should not trace next tx when next disabled", async ({ expect, client }) => {
client.transport.tracer.next = false;

await expect(
client.writeContract({
address: usdc,
abi: erc20Abi,
functionName: "transfer",
args: [client.account.address, parseUnits("100", 6)],
}),
).rejects.toMatchInlineSnapshot(`
[ContractFunctionExecutionError: The contract function "transfer" reverted with the following reason:
ERC20: transfer amount exceeds balance
Contract Call:
address: 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
function: transfer(address recipient, uint256 amount)
args: (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 100000000)
sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Docs: https://viem.sh/docs/contract/writeContract
Version: [email protected]]
`);

await expect(
client.writeContract({
address: usdc,
abi: erc20Abi,
functionName: "transfer",
args: [client.account.address, parseUnits("100", 6)],
}),
).rejects.toMatchInlineSnapshot(`
[ContractFunctionExecutionError: The contract function "transfer" reverted with the following reason:
0 ↳ FROM 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
0 ↳ CALL (0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48).transfer(0xf39Fd6…0xf3, 100000000) -> ERC20: transfer amount exceeds balance
1 ↳ DELEGATECALL (0x43506849d7c04f9138d1a2050bbf3a0c054402dd).transfer(0xf39Fd6…0xf3, 100000000) -> ERC20: transfer amount exceeds balance
Contract Call:
address: 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
function: transfer(address recipient, uint256 amount)
args: (0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, 100000000)
sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Docs: https://viem.sh/docs/contract/writeContract
Version: [email protected]]
`);
});
});

0 comments on commit 3c2008e

Please sign in to comment.