package evmole
import (
"encoding/json"
"fmt"
)
type Contract struct {
Functions []Function `json:"functions,omitempty"`
Storage []StorageRecord `json:"storage,omitempty"`
Disassembled []Instruction `json:"disassembled,omitempty"`
BasicBlocks []BasicBlock `json:"basic_blocks,omitempty"`
ControlFlowGraph *ControlFlowGraph `json:"control_flow_graph,omitempty"`
}
type Instruction struct {
Offset int
Opcode string
}
func (i *Instruction) UnmarshalJSON(data []byte) error {
var arr []json.RawMessage
if err := json.Unmarshal(data, &arr); err != nil {
return err
}
if len(arr) != 2 {
return fmt.Errorf("expected array of 2 elements, got %d", len(arr))
}
if err := json.Unmarshal(arr[0], &i.Offset); err != nil {
return err
}
return json.Unmarshal(arr[1], &i.Opcode)
}
func (i Instruction) MarshalJSON() ([]byte, error) {
return json.Marshal([2]any{i.Offset, i.Opcode})
}
type BasicBlock struct {
Start int
End int
}
func (b *BasicBlock) UnmarshalJSON(data []byte) error {
var arr []int
if err := json.Unmarshal(data, &arr); err != nil {
return err
}
if len(arr) != 2 {
return fmt.Errorf("expected array of 2 elements, got %d", len(arr))
}
b.Start = arr[0]
b.End = arr[1]
return nil
}
func (b BasicBlock) MarshalJSON() ([]byte, error) {
return json.Marshal([2]int{b.Start, b.End})
}
type Function struct {
Selector string `json:"selector"`
BytecodeOffset int `json:"bytecode_offset"`
Arguments *string `json:"arguments,omitempty"`
StateMutability *string `json:"state_mutability,omitempty"`
}
type StorageRecord struct {
Slot string `json:"slot"`
Offset int `json:"offset"`
Type string `json:"type"`
Reads []string `json:"reads"`
Writes []string `json:"writes"`
}
type ControlFlowGraph struct {
Blocks []Block `json:"blocks"`
}
type Block struct {
ID int `json:"id"`
Start int `json:"start"`
End int `json:"end"`
Type BlockType `json:"-"`
}
func (b *Block) UnmarshalJSON(data []byte) error {
type blockAlias struct {
ID int `json:"id"`
Start int `json:"start"`
End int `json:"end"`
Type string `json:"type"`
Data json.RawMessage `json:"data,omitempty"`
}
var alias blockAlias
if err := json.Unmarshal(data, &alias); err != nil {
return err
}
b.ID = alias.ID
b.Start = alias.Start
b.End = alias.End
switch alias.Type {
case "Terminate":
var t TerminateData
if err := json.Unmarshal(alias.Data, &t); err != nil {
return err
}
b.Type = BlockType{Kind: BlockKindTerminate, Terminate: &t}
case "Jump":
var j JumpData
if err := json.Unmarshal(alias.Data, &j); err != nil {
return err
}
b.Type = BlockType{Kind: BlockKindJump, Jump: &j}
case "Jumpi":
var j JumpiData
if err := json.Unmarshal(alias.Data, &j); err != nil {
return err
}
b.Type = BlockType{Kind: BlockKindJumpi, Jumpi: &j}
case "DynamicJump":
var dj DynamicJumpData
if err := json.Unmarshal(alias.Data, &dj); err != nil {
return err
}
b.Type = BlockType{Kind: BlockKindDynamicJump, DynamicJump: &dj}
case "DynamicJumpi":
var dj DynamicJumpiData
if err := json.Unmarshal(alias.Data, &dj); err != nil {
return err
}
b.Type = BlockType{Kind: BlockKindDynamicJumpi, DynamicJumpi: &dj}
default:
return fmt.Errorf("unknown block type: %s", alias.Type)
}
return nil
}
func (b Block) MarshalJSON() ([]byte, error) {
type blockJSON struct {
Start int `json:"start"`
End int `json:"end"`
Type string `json:"type"`
Data any `json:"data"`
}
var data any
switch b.Type.Kind {
case BlockKindTerminate:
data = b.Type.Terminate
case BlockKindJump:
data = b.Type.Jump
case BlockKindJumpi:
data = b.Type.Jumpi
case BlockKindDynamicJump:
data = b.Type.DynamicJump
case BlockKindDynamicJumpi:
data = b.Type.DynamicJumpi
}
return json.Marshal(blockJSON{
Start: b.Start,
End: b.End,
Type: b.Type.Kind.String(),
Data: data,
})
}
type BlockKind int
const (
BlockKindTerminate BlockKind = iota
BlockKindJump
BlockKindJumpi
BlockKindDynamicJump
BlockKindDynamicJumpi
)
func (k BlockKind) String() string {
switch k {
case BlockKindTerminate:
return "Terminate"
case BlockKindJump:
return "Jump"
case BlockKindJumpi:
return "Jumpi"
case BlockKindDynamicJump:
return "DynamicJump"
case BlockKindDynamicJumpi:
return "DynamicJumpi"
default:
return "Unknown"
}
}
type BlockType struct {
Kind BlockKind
Terminate *TerminateData
Jump *JumpData
Jumpi *JumpiData
DynamicJump *DynamicJumpData
DynamicJumpi *DynamicJumpiData
}
type TerminateData struct {
Success bool `json:"success"`
}
type JumpData struct {
To int `json:"to"`
}
type JumpiData struct {
TrueTo int `json:"true_to"`
FalseTo int `json:"false_to"`
}
type DynamicJumpData struct {
To []DynamicJump `json:"to"`
}
type DynamicJumpiData struct {
TrueTo []DynamicJump `json:"true_to"`
FalseTo int `json:"false_to"`
}
type DynamicJump struct {
Path []int `json:"path"`
To *int `json:"to,omitempty"`
}