Skip to content
This repository was archived by the owner on Jul 10, 2025. It is now read-only.

Commit 869dcad

Browse files
authored
Merge pull request #31 from vechain/debug_namespace
implement debug_traceTransaction and debug_traceCall
2 parents 4d8cbf5 + 8768b1f commit 869dcad

File tree

9 files changed

+441
-8
lines changed

9 files changed

+441
-8
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const provider = thor.ethers.modifyProvider(
7777
)
7878
)
7979
```
80-
Obtaining a singner
80+
Obtaining a signer
8181
```ts
8282
const signer = provider.getSigner(address)
8383
```
@@ -179,10 +179,12 @@ Supported subscription type: `newHeads`, `logs`
179179
Equivalent to `eth_chainId`
180180
##### `web3_clientVersion`
181181
Returning string `thor`
182+
##### `debug_traceTransaction`
183+
##### `debug_traceCall`
182184

183185
## Implementation Notes
184186
1. Fields `blockHash` and `transactionHash` return the values of [`blockId`](https://docs.vechain.org/thor/learn/block.html#block) and [`transactionId`](https://docs.vechain.org/thor/learn/transaction-model.html#transaction-id) defined in the Thor protocol, respectively
185-
2. APIs `eth_estimateGas`, `eth_call` and `eth_getTransactionReceipt` only return information associated with the first [clause](https://docs.vechain.org/thor/learn/transaction-model.html#clauses) in a transaction
187+
2. APIs `eth_estimateGas`, `eth_call`, `eth_getTransactionReceipt`, `debug_traceTransaction` and `debug_traceCall` only return information associated with the first [clause](https://docs.vechain.org/thor/learn/transaction-model.html#clauses) in a transaction
186188
3. Unsupported returning fields (all set to zero):
187189
* `cumulativeGasUsed`
188190
* `difficulty`
@@ -195,6 +197,6 @@ Returning string `thor`
195197
## License
196198
This software is licensed under the
197199
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.html), also included
198-
in *LICENSE##### file in repository.
200+
in *LICENSE* file in repository.
199201
## References
200202
[1] [https://eth.wiki/json-rpc/API](https://eth.wiki/json-rpc/API).

src/common.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,8 @@ export const EthJsonRpcMethods = [
5656
'eth_subscribe',
5757
'eth_unsubscribe',
5858

59+
'debug_traceTransaction',
60+
'debug_traceCall',
61+
5962
'evm_mine'
6063
]

src/formatter.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export class Formatter {
4343
this._inputFormatters['eth_getLogs'] = this._getLogs;
4444
this._inputFormatters['eth_subscribe'] = this._subscribe;
4545
this._inputFormatters['eth_sendRawTransaction'] = this._sendRawTransaction;
46+
this._inputFormatters['debug_traceCall'] = this._traceCall;
4647
}
4748

4849
formatInput = (method: string, params?: any[]): any[] => {
@@ -155,6 +156,7 @@ export class Formatter {
155156
data: data,
156157
}],
157158
gas: o1.gas ? hexToNumber(o1.gas) : undefined,
159+
gasPrice: o1.gasPrice,
158160
caller: o1.from,
159161
}
160162

@@ -233,6 +235,32 @@ export class Formatter {
233235
return [out];
234236
}
235237

238+
private _traceCall = (params: any[]) => {
239+
// trace call needs net, bypass if net not set
240+
if (!this._ifSetNet) {
241+
return params;
242+
}
243+
244+
let [callObj, revision = 'latest', opt] = params;
245+
246+
revision = parseBlockNumber(revision);
247+
if (revision === null) {
248+
const msg = ErrMsg.ArgumentMissingOrInvalid('debug_traceCall', 'revision');
249+
throw new ProviderRpcError(ErrCode.InvalidParams, msg);
250+
}
251+
252+
const arg = {
253+
to: callObj.to || null,
254+
value: callObj.value || '0x0',
255+
data: callObj.data || '0x',
256+
gas: callObj.gas ? hexToNumber(callObj.gas) : undefined,
257+
gasPrice: callObj.gasPrice,
258+
caller: callObj.from,
259+
};
260+
261+
return [arg, revision, opt];
262+
}
263+
236264
private _subscribe = (params: any[]) => {
237265
const name: string = params[0];
238266
switch (name) {

src/provider.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ export class Provider extends EventEmitter implements IProvider {
7070
if (opt.net) {
7171
this.restful = new Restful(opt.net, this.connex.thor.genesis.id);
7272
this._methodMap['eth_sendRawTransaction'] = this._sendRawTransaction;
73+
this._methodMap['debug_traceTransaction'] = this._traceTransaction;
74+
this._methodMap['debug_traceCall'] = this._traceCall;
7375
}
7476

7577
if (opt.wallet) {
@@ -591,4 +593,77 @@ export class Provider extends EventEmitter implements IProvider {
591593
return Promise.reject(new ProviderRpcError(ErrCode.InternalError, getErrMsg(err)));
592594
}
593595
}
596+
597+
private _traceTransaction = async (params: any[]) => {
598+
/**
599+
* debug_traceTransaction(txHash, traceOptions)
600+
* traceOptions: {
601+
* tracer: '', // name of tracer or custom js tracer code
602+
* config: {} // struct logger config object
603+
* tracerConfig: {} // tracer specific config object
604+
* }
605+
*/
606+
try {
607+
const txId = params[0]
608+
const opts = params[1]
609+
const tx = await this.connex.thor.transaction(txId).get();
610+
if (!tx) { return Promise.reject(new ProviderRpcError(ErrCode.Default, 'Target not found')); }
611+
612+
const blk = (await this.connex.thor.block(tx.meta.blockID).get())!;
613+
const txIndex = blk.transactions.findIndex(elem => elem == txId);
614+
615+
616+
if (opts && opts.tracer) {
617+
return this.restful!.traceClause({
618+
target: `${tx.meta.blockID}/${txIndex}/0`,
619+
name: opts?.tracer,
620+
config: opts?.tracerConfig,
621+
})
622+
} else {
623+
// if tracerConfig.name not specified, it's struct logger
624+
// struct logger config is located at tracerConfig.config
625+
return this.restful!.traceClause({
626+
target: `${tx.meta.blockID}/${txIndex}/0`,
627+
name: opts?.tracer,
628+
config: opts?.config,
629+
})
630+
}
631+
} catch (err: any) {
632+
return Promise.reject(new ProviderRpcError(ErrCode.InternalError, getErrMsg(err)));
633+
}
634+
}
635+
636+
private _traceCall = async (params: any[]) => {
637+
/**
638+
* debug_traceCall(callArgs, blockHashOrNumber ,tracerOptions)
639+
* tracerOptions: {
640+
* tracer: '', // name of tracer or custom js tracer code
641+
* config: {} // struct logger config object
642+
* tracerConfig: {} // tracer specific config object
643+
* }
644+
*/
645+
try {
646+
const callArgs = params[0]
647+
const revision = params[1]
648+
const opts = params[2]
649+
650+
if (opts && opts.tracer) {
651+
return this.restful!.traceCall({
652+
... callArgs,
653+
name: opts?.tracer,
654+
config: opts?.tracerConfig,
655+
}, revision)
656+
} else {
657+
// if tracerConfig.name not specified, it's struct logger
658+
// struct logger config is located at tracerConfig.config
659+
return this.restful!.traceCall({
660+
... callArgs,
661+
name: opts.tracer,
662+
config: opts.config,
663+
}, revision)
664+
}
665+
} catch (err: any) {
666+
return Promise.reject(new ProviderRpcError(ErrCode.InternalError, getErrMsg(err)));
667+
}
668+
}
594669
}

src/restful.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
import { ErrCode } from './error';
4-
import { Net, ExplainArg } from './types';
4+
import { Net, ExplainArg, TraceClauseOption, TraceCallOption } from './types';
55
import { decodeRevertReason, getErrMsg } from './utils';
66
import { ProviderRpcError } from './eip1193';
77

@@ -136,4 +136,47 @@ export class Restful {
136136
return Promise.reject(new ProviderRpcError(ErrCode.InternalError, getErrMsg(err)));
137137
}
138138
}
139+
140+
traceClause = async (opts: TraceClauseOption) =>{
141+
try {
142+
const httpParams: Net.Params = {
143+
body: opts,
144+
validateResponseHeader: this._headerValidator
145+
}
146+
147+
const ret: object = await this._net.http(
148+
"POST",
149+
'debug/tracers',
150+
httpParams
151+
);
152+
153+
154+
return ret;
155+
} catch (err: any) {
156+
return Promise.reject(new ProviderRpcError(ErrCode.InternalError, getErrMsg(err)));
157+
}
158+
}
159+
160+
traceCall = async (opts: TraceCallOption, revision?: string) => {
161+
try {
162+
const httpParams: Net.Params = {
163+
body: opts,
164+
validateResponseHeader: this._headerValidator
165+
}
166+
if (revision) {
167+
httpParams.query = { "revision": revision };
168+
}
169+
170+
const ret: object = await this._net.http(
171+
"POST",
172+
'debug/tracers/call',
173+
httpParams
174+
);
175+
176+
177+
return ret;
178+
} catch (err: any) {
179+
return Promise.reject(new ProviderRpcError(ErrCode.InternalError, getErrMsg(err)));
180+
}
181+
}
139182
}

src/types.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export type TxObj = {
181181
value?: string;
182182
data?: string;
183183
gas?: string;
184-
184+
gasPrice?: string;
185185
input?: string; // Added to support requests from web3.js
186186
}
187187

@@ -200,4 +200,22 @@ export type ConvertedFilterOpts = {
200200
export type DelegateOpt = {
201201
url: string;
202202
signer?: string;
203+
}
204+
205+
export interface TracerOption {
206+
name: string;
207+
config: object;
208+
}
209+
210+
export interface TraceClauseOption extends TracerOption {
211+
target: string;
212+
}
213+
214+
export interface TraceCallOption extends TracerOption{
215+
to: string | null
216+
value: string
217+
data: string
218+
caller?: string;
219+
gas?: number;
220+
gasPrice?: string;
203221
}

test/docker-compose-test.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: "3"
22

33
services:
44
thor-solo:
5-
image: vechain/thor:v2.0.0
5+
image: vechain/thor:v2.1.0
66
container_name: thor-solo
77
# Install curl for our healthcheck, then start thor solo
88
entrypoint:
@@ -11,6 +11,7 @@ services:
1111
"-c",
1212
"apk update && apk upgrade && apk add curl && thor solo --on-demand --data-dir /data/thor --api-addr 0.0.0.0:8669 --api-cors '*' --api-backtrace-limit -1 --verbosity 4"
1313
]
14+
user: root
1415
ports:
1516
- "8669:8669"
1617
- "11235:11235"

0 commit comments

Comments
 (0)