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 }