-
Notifications
You must be signed in to change notification settings - Fork 797
/
precompiles.go
119 lines (99 loc) · 3.75 KB
/
precompiles.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package common
import (
"errors"
"fmt"
"math/big"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/sei-protocol/sei-chain/x/evm/state"
)
const UnknownMethodCallGas uint64 = 3000
type Contexter interface {
Ctx() sdk.Context
}
type Precompile struct {
abi.ABI
}
func (p Precompile) RequiredGas(input []byte, isTransaction bool) uint64 {
argsBz := input[4:] // first four bytes are method ID
if isTransaction {
return storetypes.KVGasConfig().WriteCostFlat + (storetypes.KVGasConfig().WriteCostPerByte * uint64(len(argsBz)))
}
return storetypes.KVGasConfig().ReadCostFlat + (storetypes.KVGasConfig().ReadCostPerByte * uint64(len(argsBz)))
}
func (p Precompile) Prepare(evm *vm.EVM, input []byte) (sdk.Context, *abi.Method, []interface{}, error) {
ctxer, ok := evm.StateDB.(Contexter)
if !ok {
return sdk.Context{}, nil, nil, errors.New("cannot get context from EVM")
}
methodID, err := ExtractMethodID(input)
if err != nil {
return sdk.Context{}, nil, nil, err
}
method, err := p.ABI.MethodById(methodID)
if err != nil {
return sdk.Context{}, nil, nil, err
}
argsBz := input[4:]
args, err := method.Inputs.Unpack(argsBz)
if err != nil {
return sdk.Context{}, nil, nil, err
}
return ctxer.Ctx(), method, args, nil
}
func (p Precompile) GetABI() abi.ABI {
return p.ABI
}
func ValidateArgsLength(args []interface{}, length int) error {
if len(args) != length {
return fmt.Errorf("expected %d arguments but got %d", length, len(args))
}
return nil
}
func ValidateNonPayable(value *big.Int) error {
if value != nil && value.Sign() != 0 {
return errors.New("sending funds to a non-payable function")
}
return nil
}
func HandlePaymentUsei(ctx sdk.Context, precompileAddr sdk.AccAddress, payer sdk.AccAddress, value *big.Int, bankKeeper BankKeeper) (sdk.Coin, error) {
usei, wei := state.SplitUseiWeiAmount(value)
if !wei.IsZero() {
return sdk.Coin{}, fmt.Errorf("selected precompile function does not allow payment with non-zero wei remainder: received %s", value)
}
coin := sdk.NewCoin(sdk.MustGetBaseDenom(), usei)
// refund payer because the following precompile logic will debit the payments from payer's account
// this creates a new event manager to avoid surfacing these as cosmos events
if err := bankKeeper.SendCoins(ctx.WithEventManager(sdk.NewEventManager()), precompileAddr, payer, sdk.NewCoins(coin)); err != nil {
return sdk.Coin{}, err
}
return coin, nil
}
func HandlePaymentUseiWei(ctx sdk.Context, precompileAddr sdk.AccAddress, payer sdk.AccAddress, value *big.Int, bankKeeper BankKeeper) (sdk.Int, sdk.Int, error) {
usei, wei := state.SplitUseiWeiAmount(value)
// refund payer because the following precompile logic will debit the payments from payer's account
// this creates a new event manager to avoid surfacing these as cosmos events
if err := bankKeeper.SendCoinsAndWei(ctx.WithEventManager(sdk.NewEventManager()), precompileAddr, payer, usei, wei); err != nil {
return sdk.Int{}, sdk.Int{}, err
}
return usei, wei, nil
}
/*
*
sei gas = evm gas * multiplier
sei gas price = fee / sei gas = fee / (evm gas * multiplier) = evm gas / multiplier
*/
func GetRemainingGas(ctx sdk.Context, evmKeeper EVMKeeper) uint64 {
gasMultipler := evmKeeper.GetPriorityNormalizer(ctx)
seiGasRemaining := ctx.GasMeter().Limit() - ctx.GasMeter().GasConsumedToLimit()
return sdk.NewDecFromInt(sdk.NewIntFromUint64(seiGasRemaining)).Quo(gasMultipler).TruncateInt().Uint64()
}
func ExtractMethodID(input []byte) ([]byte, error) {
// Check if the input has at least the length needed for methodID
if len(input) < 4 {
return nil, errors.New("input too short to extract method ID")
}
return input[:4], nil
}