gother

package module
v0.0.0-...-68c0e59 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2023 License: MIT Imports: 19 Imported by: 0

README

gother

gother is the library supporting interaction with smart contract, fetch contract's logs from blocks,...

Content:

Installation

  1. You first need Go installed (version 1.18+ is required):
$ go get -u github.com/func25/gother
  1. Import it in your code:
import "github.com/func25/gother"

Get started

Dial to node

You need to dial the RPC node first:

gother.DialCtx(context.Background(), "https://data-seed-prebsc-1-s3.binance.org:8545/")

// or gother.Dial("https://data-seed-prebsc-1-s3.binance.org:8545/")
Worker workflow

In some cases, we need to crawl contract logs from the block; you can collect with the support of a scanner and lazier.

Scanner: scan blocks to get the logs
// scan 100 blocks from block 2160030
scan := gother.NewScanner(100, 21600030)

logs, scannedBlock, err := scan.Scan(ctx)
if err != nil {
  return err
}

// print the transaction hash of logs
for _, l := range logs {
  fmt.Println(l.TxHash)
}

fmt.Printf("I have scanned to block %d\n", scannedBlock)

But how can you scan logs emitted from a specific smart contract?

// scan 100 blocks from block 2160030 and get the logs 
// which emitted from contract: 0xbA01E92eA9B940745f89785fC9cED4DDc17Da450
scan := gother.NewScanner(100, 21600030, common.HexToAddress("0xbA01E92eA9B940745f89785fC9cED4DDc17Da450"))
Agent & Lazier: scan but in the loop

This lib supports you define an agent to do the loop: scan -> wait -> scan -> wait -> scan..., and the agent must meet the interface IAgent

// the target interface 
type IAgent interface {
	FromBlock(ctx context.Context) (uint64, error)       // get the next block which want to scan from
	ProcessLog(ctx context.Context, log types.Log) error // process the logs that agent collects
	UpdateBlock(ctx context.Context, block uint64) error // update the scanned block after scanning
}

// create an agent
type Agent struct {
  Block uint64
}

// Scanner will scan from this block
func (s *Agent) FromBlock(ctx context.Context) (uint64, error) {
	return s.Block + 1, nil
}

// Process the crawled log
func (s *Agent) ProcessLog(ctx context.Context, log types.Log) error {
	if log.Removed {
		return nil
	}

	fmt.Println(log.TxHash.Hex())

	return nil
}

// Save the scanned block after scanning
func (s *Agent) UpdateBlock(ctx context.Context, block uint64) error {
	s.Block = block
	return nil
}

After creating the agent, wrap it with the lazier and do scanning

// create an agent
agent := Agent{Block: 21600130}

// wrap the agent with lazier and wait duration of lazier is 3 seconds
lazier := gother.Lazier[*Agent]{Agent: &agent, Duration: time.Second * 3}

// scan 100 blocks each time, the `from` of scanner will be replaced with FromBlock(ctx) of agent
lazier.Scan(*gother.NewScanner(100, 0))

for {} // block the thread

Documentation

Index

Constants

View Source
const (
	SIG_PREFIX = "\x19Ethereum Signed Message:\n"
	SIG_V      = 27
)

Variables

View Source
var Util gotherUtil

Functions

func HeaderLatest

func HeaderLatest(ctx context.Context) (*types.Header, error)

func HeaderNumber

func HeaderNumber(ctx context.Context, number *big.Int) (*types.Header, error)

func JsonABI

func JsonABI(reader io.Reader) abi.ABI

JsonABI parses json abi to abi.ABI, panic if error

func Keccak256Hash

func Keccak256Hash(data ...[]byte) (hash string)

func Keccak256Sign

func Keccak256Sign(prv string, data ...[]byte) (signature string, err error)

Keccak256Sign will hash data to 32 bytes (= keccak256) then signing it, return hexa-signature which is 132 bytes (0x...)

func Keccak256SignBytes

func Keccak256SignBytes(prv string, data ...[]byte) (signature []byte, err error)

Keccak256SignBytes will hash data to 32 bytes (= keccak256) then signing it return bytes-signature which is 65 bytes (32 bytes r, 32 bytes s, 1 byte v)

func RecoverETHSig

func RecoverETHSig(msg, sig []byte) (*ecdsa.PublicKey, error)

RecoverHexSig will recover public key from [65]byte signature

func RecoverHexSig

func RecoverHexSig(msg []byte, hexSig string) (*ecdsa.PublicKey, error)

RecoverHexSig will recover public key from hex signature

func Sign

func Sign(prv string, data []byte) (str string, err error)

Sign signs the data with prefix `\x19Ethereum Signed Message:\n${len(data)}`

func Uint

func Uint(mul int, data []byte) []byte

Types

type Account

type Account struct {
	*GotherClient
	// contains filtered or unexported fields
}

func NewAccount

func NewAccount(privateKey string) *Account

func (Account) Address

func (c Account) Address() (string, error)

func (Account) Balance

func (c Account) Balance(ctx context.Context) (*big.Int, error)

func (Account) Keccak256Sign

func (c Account) Keccak256Sign(data ...[]byte) (signature string, err error)

func (Account) Keccak256SignBytes

func (c Account) Keccak256SignBytes(data ...[]byte) (signature []byte, err error)

func (Account) NewSmcTx

func (c Account) NewSmcTx(ctx context.Context, tx SmcTxData) (*bind.TransactOpts, error)

NewSmcTx creates a transaction calling smart contract with suggest gas price and 110% gas limit

func (Account) NewTx

func (c Account) NewTx(ctx context.Context, data TxData) (*types.Transaction, error)

func (Account) PublicKey

func (c Account) PublicKey() (string, error)

func (Account) SendTx

func (c Account) SendTx(ctx context.Context, data TxData) (*types.Transaction, error)

sign transaction and send return signed transaction

func (*Account) SetClient

func (a *Account) SetClient(client *GotherClient) *Account

func (*Account) SetPrivate

func (a *Account) SetPrivate(privateKey string) *Account

func (Account) Sign

func (c Account) Sign(data []byte) (str string, err error)

type GotherClient

type GotherClient struct {
	*ethclient.Client
}
var Client *GotherClient = &GotherClient{}

func Dial

func Dial(url string) (*GotherClient, error)

DialCtx will connect to `url` and set the default client to client(url) if default client is nil

func DialCtx

func DialCtx(ctx context.Context, url string) (*GotherClient, error)

DialCtx will connect to `url` and set the default client to client(url) if default client is nil

func SetClient

func SetClient(c *ethclient.Client) *GotherClient

SetClient is used in case you already have ethclient.Client and want to use features of gother

func (GotherClient) Balance

func (c GotherClient) Balance(ctx context.Context, address string) (*big.Int, error)

func (GotherClient) HeaderLatest

func (a GotherClient) HeaderLatest(ctx context.Context) (*types.Header, error)

func (GotherClient) HeaderNumber

func (a GotherClient) HeaderNumber(ctx context.Context, number *big.Int) (*types.Header, error)

func (GotherClient) IsSmartContract

func (c GotherClient) IsSmartContract(ctx context.Context, addr string) (bool, error)

type IAgent

type IAgent interface {
	FromBlock() (uint64, error)                          // get the next block which want to scan from
	ProcessLogs(from, to uint64, logs []types.Log) error // process the logs that agent collects
}

type Lazier

type Lazier[T IAgent] struct {
	Agent    T
	Duration time.Duration
}

Lazier is too lazy, he/she ignores errors when processing logs and updating block

func (Lazier[T]) Scan

func (sol Lazier[T]) Scan(s Scanner) chan struct{}

Lazier will scan the blocks to get the logs with 3 steps with IAgent:

  • FromBlock -> ProcessLog -> UpdateBlock

Read IAgent interface for more information

type Scanner

type Scanner struct {
	ScanNum uint64 // how many blocks scanning each time

	From      uint64           // scan from?
	Addresses []common.Address // smart contract address
	// contains filtered or unexported fields
}

func NewScanner

func NewScanner(scanNum uint64, from uint64, addresses ...common.Address) *Scanner

NewScanner create a scanner which will scan `scanNum` blocks each time, it scans from `from` and only scans log from smart contracts which has address in `addresses` array

func (Scanner) Clone

func (s Scanner) Clone() *Scanner

func (*Scanner) InjAddresses

func (sc *Scanner) InjAddresses(addresses ...common.Address) *Scanner

InjAddresses changes the addresses of smart contracts that will be scanned

func (*Scanner) InjClient

func (sc *Scanner) InjClient(client *GotherClient) *Scanner

InjClient set the client for scanner

func (*Scanner) InjOffset

func (sc *Scanner) InjOffset(offset uint64) *Scanner

InjOffset sets the offset from latest block, this will scan all the blocks except last `offset` blocks because we need to scan the most stable blocks, latest blocks may be forked or replaced...

func (*Scanner) Scan

func (sc *Scanner) Scan(ctx context.Context) (logs []types.Log, scannedBlock uint64, err error)

Scan scans all the logs from `scanner.from` to `scanner.from + scanner.ScanNum` and retrieves logs to process return the current block after scanning (currentBlock = min(scanner.from + scanner.ScanNum, latestBlockNum - offset))

type Smc

type Smc struct {
	ABI     abi.ABI
	Address string `validate:"required"`
}

type SmcTxData

type SmcTxData struct {
	Smc   Smc
	Value *big.Int
	Data  []byte
}

type TxData

type TxData struct {
	To    string
	Value *big.Int
}

type Wallet

type Wallet struct {
	Private      string
	PrivateECDSA *ecdsa.PrivateKey
	Public       string
	PublicECDSA  *ecdsa.PublicKey
	Address      string
}

func NewWallet

func NewWallet() (*Wallet, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL