Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deployment/ccip/sequence/deploy_ccip.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ func deployCCIPSequence(b operations.Bundle, dp *dep.DependencyProvider, in Depl
PendingOwner: address.NewAddressNone(),
},
AuthorizedCaller: &routerAddress,
Behavior: receiver.Accept,
Behavior: receiver.BehaviorAccept,
}

outputAddr, err = utils.InvokeDeployContractOperation(b, dp, in.ChainSelector, tonCompiledContracts[state.TonReceiver], receiverStorage, nil, in.CCIPConfig.ReceiverParams.Coin, in.CCIPConfig.ReceiverParams.ContractsSemver)
Expand Down
68 changes: 68 additions & 0 deletions docs/.misc/dev-guides/explorer/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# TON Explorer Architecture

This document describes the explorer command architecture in `pkg/ton/codec/debug/explorer`.

## Overview

The explorer flow is organized into five stages:

1. **CLI input normalization**
2. **Network/API connection setup**
3. **Transaction and trace discovery**
4. **Trace enrichment (actors/contracts)**
5. **Rendering/output**

## Module layout

- `explorer.go`: command wiring, `client` lifecycle, trace orchestration, actor discovery.
- `cli_args.go`: positional argument and URL/hash parsing integration (`parseCLIInput`).
- `utils.go`: explorer URL parsing (`ParseURL`).
- `format.go`: visualization format validation (`parseFormat`).
- `network_connect.go`: TON connection bootstrap (`connect`).
- `network_mylocalton.go`: Docker inspection helpers for `mylocalton`.
- `tx_lookup.go`: tx hash decoding, toncenter metadata lookups, tx search/fallback logic.
- `browser.go`: OS-specific browser opening for sequence URL mode.

## Request lifecycle

`GenerateExplorerCmd` parses args and flags, creates a `client` with `Connect`, and runs `PrintTrace`.

`PrintTrace` performs:

1. Resolve root tx hash from toncenter when supported (`mainnet`/`testnet`).
2. Resolve sender address:
- from user input when provided,
- from toncenter when hash-only mode is used on supported networks.
3. Locate transaction from account history (paged liteclient scan), with toncenter metadata fallback when available.
4. Convert transaction to trace root (`tracetracking.MapToReceivedMessage`) and wait for full trace (`WaitForTrace`).
5. Query contract actors via `typeAndVersion` getter.
6. Render either tree or sequence output.
7. For sequence URL mode, open Mermaid URL in browser.

## Toncenter behavior

Toncenter is treated as an optional dependency by network:

- `mainnet`/`testnet`: toncenter is used for trace-root and tx metadata resolution.
- `mylocalton` or custom config URL networks: toncenter fallback is unavailable.
- Hash-only mode requires explicit source address in these environments.

## Extension points

For maintainability, keep future changes aligned with existing seams:

- Input parsing changes in `cli_args.go`/`utils.go`.
- New visualization output options in `format.go` + rendering branch in `PrintTrace`.
- Network-specific bootstrap logic in `network_connect.go`.
- External metadata providers in `tx_lookup.go`.
- Browser side-effects in `browser.go`.

## Compatibility contract

Current trace CLI contract is:

- `explorer trace <url>`
- `explorer trace <tx-hash> <address>`
- `explorer trace <tx-hash>` (works when address can be resolved via toncenter)

`--address` and `--tx` flags were removed because they were unused and misleading.
6 changes: 4 additions & 2 deletions docs/.misc/dev-guides/explorer/development.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# TON Explorer Development Guide

For adding support to more contracts, you need to register your decoder in [`defaultDecoders`](../../../../pkg/ton/debug/pretty_print.go). Decoders implement [`ContractDecoder`](../../../../pkg/ton/debug/lib/lib.go) interface:
For explorer architecture and module boundaries, read [TON Explorer Architecture](./architecture.md).

For adding support to more contracts, you need to register your decoder in [`defaultDecoders`](../../../../pkg/ton/codec/debug/pretty_print.go). Decoders implement [`ContractDecoder`](../../../../pkg/ton/codec/debug/lib/lib.go) interface:

```go
type ContractDecoder interface {
Expand Down Expand Up @@ -30,7 +32,7 @@ type BodyInfo interface {
}
```

Your decoder should go in `pkg/ton/debug/decoders/<domain>` package. If it is a ccip contract, then in `pkg/ton/debug/decoders/ccip`. E.g. `pkg/ton/debug/decoders/ccip/feequoter/feequoter.go`.
Your decoder should go in `pkg/ton/codec/debug/decoders/<domain>` package. If it is a ccip contract, then in `pkg/ton/codec/debug/decoders/ccip`. E.g. `pkg/ton/codec/debug/decoders/ccip/feequoter/feequoter.go`.

I suggest not placing any business logic in the decoder. Instead, create a separate package for that, e.g. `pkg/ccip/bindings/feequoter/codec.go` and use it from the decoder.

Expand Down
100 changes: 90 additions & 10 deletions docs/.misc/dev-guides/explorer/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

Command-line tool for analyzing TON blockchain transactions and traces.

Read [TON Explorer Architecture](./architecture.md) for internal module layout and execution flow.

## Usage

Three ways to run:
Four ways to run:

1. **URL**: `./explorer <tonscan-url>`
2. **Hash + Address**: `./explorer <tx-hash> <address>`
3. **Hash only**: `./explorer <tx-hash>` (testnet/mainnet only)
1. **URL**: `./explorer trace <tonscan-url>`
2. **Hash + Address**: `./explorer trace <tx-hash> <address>`
3. **Hash only**: `./explorer trace <tx-hash>` (testnet/mainnet only unless sender address is provided separately)
4. **Getter call**: `./explorer get <address> [getter_name] [args...]`

## Run with Nix

Expand All @@ -17,7 +20,7 @@ The `explorer` binary is packaged with `chainlink-ton-extras` pkg bundle.
We can start a dev shell including specific pkg contents and execute a bash cmd:

```bash
nix shell .#chainlink-ton-extras -c explorer https://testnet.tonscan.org/tx/<tx-hash>
nix shell .#chainlink-ton-extras -c explorer trace https://testnet.tonscan.org/tx/<tx-hash>
```

## Build
Expand All @@ -31,16 +34,89 @@ go build

```bash
# URL (recommended)
./explorer https://testnet.tonscan.org/tx/<tx-hash>
./explorer http://localhost:8080/transaction?account=<account_addr>&hash=<tx-hash>
./explorer trace https://testnet.tonscan.org/tx/<tx-hash>
./explorer trace http://localhost:8080/transaction?account=<account_addr>&hash=<tx-hash>

# Hash + address
./explorer <tx-hash> <address> [--net testnet|mainnet|mylocalton|http://custom-domain/global.config.json]
./explorer trace <tx-hash> <address> [--net testnet|mainnet|mylocalton|http://custom-domain/global.config.json]

# Hash only (auto-resolves address via toncenter on testnet/mainnet)
./explorer trace <tx-hash> [--net testnet|mainnet|mylocalton|http://custom-domain/global.config.json]

# Getter call
./explorer get <address> [getter_name] [args...] [--arg name=value] [--net testnet|mainnet|mylocalton|http://custom-domain/global.config.json] [--contract-type <type>]

# Example
./explorer get EQA-CUZI_USus4w0_Erf-wTj5uhaAR7XldEimU0w0WAJGGod dynamicConfig
```

## Getter command

`explorer get` supports no-args and argument-based getters and prints decoded JSON output.

When `getter_name` is omitted in an interactive terminal, explorer opens a numbered selector prompt (`0` to cancel).
When a selected getter requires arguments and values are missing, explorer prompts for those values.

The command tries to infer the contract type by calling `typeAndVersion` on the target address.
If inference is unavailable/fails, pass `--contract-type` explicitly.

Examples:

# Hash only (auto-resolves address)
./explorer <tx-hash> [--net testnet|mainnet|mylocalton|http://custom-domain/global.config.json]
```bash
# auto-detect contract type
./explorer get <address> owner

# explicit contract type
./explorer get <address> owner --contract-type link.chain.ton.ccip.OnRamp

# positional args
./explorer get <router-address> onRamp 16015286601757825753

# named args
./explorer get <timelock-address> getRoleMember --arg role=1 --arg index=0
```

## Autocomplete setup

The explorer uses Cobra shell completion, including dynamic getter completion for:

`explorer get <address> <TAB>`

### zsh (current shell only)

```bash
source <(./explorer completion zsh)
```

### zsh (persistent)

```bash
mkdir -p ~/.zfunc
./explorer completion zsh > ~/.zfunc/_explorer
```

Add this to `~/.zshrc`:

```bash
fpath=(~/.zfunc $fpath)
autoload -Uz compinit
compinit
```

Reload:

```bash
source ~/.zshrc
```

If you run the local binary directly from this repository, add an alias in `~/.zshrc`:

```bash
alias explorer="/Users/patricio.passarino/Code/ton/chainlink-ton-explorer/explorer"
```

Then you can tab-complete getter names for a contract address.

## Networks

Choose the network with `-n`/`--net` flag:
Expand Down Expand Up @@ -70,8 +146,12 @@ Display message trace as a tree structure with `--visualization tree`.
```bash
--verbose # Show debugging information
--page-size 10 --max-pages 10 # Control transaction search pagination
--contract-type <type> # (get command) optional contract type override
--arg name=value # (get command) named getter argument (repeatable)
```

Note: `--address` and `--tx` flags are not supported; use positional arguments.

## Environment injection

The same cli is exposed in [chainlink-deployments's repo](https://github.com/smartcontractkit/chainlink-deployments/tree/main/domains/ccip/cmd) which injects contract metadata from the DataStore.
Expand Down
3 changes: 3 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

- [Nix - Getting Started](.misc/dev-guides/nix/getting-started.md)
- [Nix - Builds](.misc/dev-guides/nix/builds.md)
- [Explorer - Usage](.misc/dev-guides/explorer/usage.md)
- [Explorer - Architecture](.misc/dev-guides/explorer/architecture.md)
- [Explorer - Development](.misc/dev-guides/explorer/development.md)

## CCIP Product E2E Tests

Expand Down
21 changes: 14 additions & 7 deletions pkg/bindings/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ var TypeToGetterMap = map[string]GetterMap{
"rmn_pendingOwner": router.GetRMNPendingOwner,
"onRamp": router.GetOnRamp,
"destChainSelectors": router.GetDestChainSelectors,
"verifyNotCursed": router.GetVerifyNotCursed,
"cursedSubjects": router.GetCursedSubjects,
},
TypeOnRamp: {
"owner": onramp.GetOwner,
Expand All @@ -46,17 +48,22 @@ var TypeToGetterMap = map[string]GetterMap{
"sourceChainSelectors": offramp.GetSourceChainSelectors,
},
TypeFeeQuoter: {
"owner": feequoter.GetOwner,
"pendingOwner": feequoter.GetPendingOwner,
"destChainConfig": feequoter.GetDestChainConfig,
"destinationChainGasPrice": feequoter.GetDestinationChainGasPrice,
"tokenPrice": feequoter.GetTokenPrice,
"staticConfig": feequoter.GetStaticConfig,
"destChainSelectors": feequoter.GetDestChainSelectors,
"owner": feequoter.GetOwner,
"pendingOwner": feequoter.GetPendingOwner,
"destChainConfig": feequoter.GetDestChainConfig,
"destinationChainGasPrice": feequoter.GetDestinationChainGasPrice,
"tokenPrice": feequoter.GetTokenPrice,
"staticConfig": feequoter.GetStaticConfig,
"destChainSelectors": feequoter.GetDestChainSelectors,
"feeTokens": feequoter.GetFeeTokens,
"premiumMultiplierWeiPerEth": feequoter.GetPremiumMultiplierWeiPerEth,
"tokenTransferFeeConfig": feequoter.GetTokenTransferFeeConfig,
},
// Common contract getters (applies to all CCIP contracts)
"link.chain.ton.ccip.Common": {
"typeAndVersion": common.GetTypeAndVersion,
"facilityId": common.GetFacilityId,
"errorCode": common.GetErrorCode,
},
// Ownable2Step pattern (inherited by many contracts)
TypeOwnable: {
Expand Down
33 changes: 23 additions & 10 deletions pkg/bindings/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import (
"github.com/smartcontractkit/chainlink-ton/pkg/bindings/mcms/mcms"
"github.com/smartcontractkit/chainlink-ton/pkg/bindings/mcms/timelock"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/ccipsendexecutor"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/merkleroot"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/ownable2step"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/receiveexecutor"
"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/receiver"
"github.com/smartcontractkit/chainlink-ton/pkg/ton/tvm"

"github.com/smartcontractkit/chainlink-ton/pkg/ccip/bindings/feequoter"
Expand Down Expand Up @@ -40,11 +43,16 @@ const (
TypeTimelock = PkgMCMS + ".Timelock"

// CCIP
TypeRouter = PkgCCIP + ".Router"
TypeOnRamp = PkgCCIP + ".OnRamp"
TypeOffRamp = PkgCCIP + ".OffRamp"
TypeFeeQuoter = PkgCCIP + ".FeeQuoter"
TypeSendExecutor = PkgCCIP + ".CCIPSendExecutor"
TypeRouter = PkgCCIP + ".Router"
TypeOnRamp = PkgCCIP + ".OnRamp"
TypeOffRamp = PkgCCIP + ".OffRamp"
TypeFeeQuoter = PkgCCIP + ".FeeQuoter"
TypeSendExecutor = PkgCCIP + ".CCIPSendExecutor"
TypeReceiveExecutor = PkgCCIP + ".ReceiveExecutor"
TypeMerkleRoot = PkgCCIP + ".MerkleRoot"

// Test CCIP
TypeReceiver = PkgCCIP + ".Receiver"

// Jetton
TypeJettonWallet = PkgJetton + ".contracts.jetton-wallet"
Expand All @@ -64,11 +72,16 @@ var Registry = tvm.ContractTLBRegistry{
TypeTimelock: timelock.TLBs,

// CCIP contract types
TypeRouter: router.TLBs,
TypeOnRamp: onramp.TLBs,
TypeOffRamp: offramp.TLBs,
TypeFeeQuoter: feequoter.TLBs,
TypeSendExecutor: ccipsendexecutor.TLBs,
TypeRouter: router.TLBs,
TypeOnRamp: onramp.TLBs,
TypeOffRamp: offramp.TLBs,
TypeFeeQuoter: feequoter.TLBs,
TypeSendExecutor: ccipsendexecutor.TLBs,
TypeReceiveExecutor: receiveexecutor.TLBs,
TypeMerkleRoot: merkleroot.TLBs,

// Test contract types
TypeReceiver: receiver.TLBs,

// Jetton contract types
TypeJettonWallet: wallet.TLBs,
Expand Down
4 changes: 3 additions & 1 deletion pkg/ccip/bindings/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import (
)

const (
versionGetter = "typeAndVersion"
versionGetter = "typeAndVersion"
facilityIdGetter = "facilityId"
errorCodeGetter = "errorCode"
)

// TVM limits for cell chains, enforced at different stages:
Expand Down
24 changes: 24 additions & 0 deletions pkg/ccip/bindings/common/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,27 @@ var GetTypeAndVersion = tvm.NewNoArgsGetter(tvm.NoArgsOpts[TypeAndVersion]{
}, nil
}),
})

// GetFacilityId gets the facility ID of the FeeQuoter contract
var GetFacilityId = tvm.NewNoArgsGetter(tvm.NoArgsOpts[uint16]{
Name: facilityIdGetter,
Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (uint16, error) {
v, err := r.Int(0)
if err != nil {
return 0, err
}
return uint16(v.Uint64()), nil
}),
})

// GetErrorCode gets the contract-specific error code for a given local error code
var GetErrorCode = tvm.Getter[uint16, uint16]{
Name: errorCodeGetter,
Decoder: tvm.NewResultDecoder(func(r *ton.ExecutionResult) (uint16, error) {
v, err := r.Int(0)
if err != nil {
return 0, err
}
return uint16(v.Uint64()), nil
}),
}
13 changes: 8 additions & 5 deletions pkg/ccip/bindings/feequoter/fee_quoter.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,14 @@ const (

// Registry method names
const (
DestChainsGetter = "destChainSelectors"
tokenPriceGetter = "tokenPrice"
staticConfigGetter = "staticConfig"
destChainConfigGetter = "destChainConfig"
destinationChainGasPriceGetter = "destinationChainGasPrice"
DestChainsGetter = "destChainSelectors"
tokenPriceGetter = "tokenPrice"
staticConfigGetter = "staticConfig"
destChainConfigGetter = "destChainConfig"
destinationChainGasPriceGetter = "destinationChainGasPrice"
FeeTokensGetter = "feeTokens"
premiumMultiplierWeiPerEthGetter = "premiumMultiplierWeiPerEth"
tokenTransferFeeConfigGetter = "tokenTransferFeeConfig"
)

type Storage struct {
Expand Down
Loading
Loading