code.vegaprotocol.io/vega@v0.79.0/core/datasource/external/ethcall/call.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package ethcall
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  
    25  	dscommon "code.vegaprotocol.io/vega/core/datasource/common"
    26  	ethcallcommon "code.vegaprotocol.io/vega/core/datasource/external/ethcall/common"
    27  
    28  	"github.com/ethereum/go-ethereum"
    29  	"github.com/ethereum/go-ethereum/accounts/abi"
    30  	"github.com/ethereum/go-ethereum/common"
    31  )
    32  
    33  type Call struct {
    34  	spec    ethcallcommon.Spec
    35  	address common.Address
    36  	method  string
    37  	args    []byte
    38  	abi     abi.ABI
    39  	abiJSON []byte
    40  	filters dscommon.Filters
    41  	chainID uint64
    42  }
    43  
    44  func NewCall(spec ethcallcommon.Spec) (Call, error) {
    45  	abiJSON, err := CanonicalizeJSON(spec.AbiJson)
    46  	if err != nil {
    47  		return Call{}, errors.Join(
    48  			ethcallcommon.ErrInvalidEthereumAbi,
    49  			fmt.Errorf("unable to canonicalize abi JSON: %w", err))
    50  	}
    51  
    52  	reader := bytes.NewReader(abiJSON)
    53  	abi, err := abi.JSON(reader)
    54  	if err != nil {
    55  		return Call{}, errors.Join(
    56  			ethcallcommon.ErrInvalidEthereumAbi,
    57  			fmt.Errorf("unable to parse abi JSON: %w", err))
    58  	}
    59  
    60  	args, err := JsonArgsToAny(spec.Method, spec.ArgsJson, spec.AbiJson)
    61  	if err != nil {
    62  		return Call{}, errors.Join(
    63  			ethcallcommon.ErrInvalidCallArgs,
    64  			fmt.Errorf("unable to deserialize args: %w", err))
    65  	}
    66  
    67  	packedArgs, err := abi.Pack(spec.Method, args...)
    68  	if err != nil {
    69  		return Call{}, errors.Join(
    70  			ethcallcommon.ErrInvalidCallArgs,
    71  			fmt.Errorf("failed to pack inputs: %w", err))
    72  	}
    73  
    74  	filters, err := dscommon.NewFilters(spec.Filters, true)
    75  	if err != nil {
    76  		return Call{}, errors.Join(
    77  			ethcallcommon.ErrInvalidFilters,
    78  			fmt.Errorf("failed to create filters: %w", err))
    79  	}
    80  
    81  	return Call{
    82  		address: common.HexToAddress(spec.Address),
    83  		method:  spec.Method,
    84  		args:    packedArgs,
    85  		abi:     abi,
    86  		abiJSON: abiJSON,
    87  		spec:    spec,
    88  		filters: filters,
    89  		chainID: spec.SourceChainID,
    90  	}, nil
    91  }
    92  
    93  func (c Call) Call(ctx context.Context, ethClient EthReaderCaller, blockNumber uint64) (Result, error) {
    94  	// TODO: timeout?
    95  	msg := ethereum.CallMsg{
    96  		To:   &c.address,
    97  		Data: c.args,
    98  	}
    99  
   100  	n := big.NewInt(0).SetUint64(blockNumber)
   101  	bytes, err := ethClient.CallContract(ctx, msg, n)
   102  	if err != nil {
   103  		return Result{}, fmt.Errorf("failed to call contract: %w", err)
   104  	}
   105  
   106  	return newResult(c, bytes)
   107  }
   108  
   109  func (c Call) Spec() ethcallcommon.Spec {
   110  	return c.spec
   111  }
   112  
   113  func (c Call) triggered(prevEthBlock blockish, currentEthBlock blockish) bool {
   114  	switch trigger := c.spec.Trigger.(type) {
   115  	case ethcallcommon.TimeTrigger:
   116  		// Before initial?
   117  		if currentEthBlock.Time() < trigger.Initial {
   118  			return false
   119  		}
   120  
   121  		// Crossing initial boundary?
   122  		if prevEthBlock.Time() < trigger.Initial && currentEthBlock.Time() >= trigger.Initial {
   123  			return true
   124  		}
   125  
   126  		// After until?
   127  		if trigger.Until != 0 && currentEthBlock.Time() > trigger.Until {
   128  			return false
   129  		}
   130  
   131  		if trigger.Every == 0 {
   132  			return false
   133  		}
   134  		// Somewhere in the middle..
   135  		prevTriggerCount := (prevEthBlock.Time() - trigger.Initial) / trigger.Every
   136  		currentTriggerCount := (currentEthBlock.Time() - trigger.Initial) / trigger.Every
   137  		return currentTriggerCount > prevTriggerCount
   138  	}
   139  	return false
   140  }
   141  
   142  func (c Call) initialTime() uint64 {
   143  	switch trigger := c.spec.Trigger.(type) {
   144  	case ethcallcommon.TimeTrigger:
   145  		return trigger.Initial
   146  	}
   147  	return 0
   148  }