Skip to content

Commit

Permalink
refactor bank/oracle precompile to use dynamic gas
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen committed Nov 11, 2024
1 parent c7a4b81 commit 7d65e9d
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 66 deletions.
121 changes: 67 additions & 54 deletions precompiles/bank/bank.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ type CoinBalance struct {
Denom string
}

func NewPrecompile(bankKeeper pcommon.BankKeeper, bankMsgServer pcommon.BankMsgServer, evmKeeper pcommon.EVMKeeper, accountKeeper pcommon.AccountKeeper) (*pcommon.Precompile, error) {
func NewPrecompile(bankKeeper pcommon.BankKeeper, bankMsgServer pcommon.BankMsgServer, evmKeeper pcommon.EVMKeeper, accountKeeper pcommon.AccountKeeper) (*pcommon.DynamicGasPrecompile, error) {
newAbi := pcommon.MustGetABI(f, "abi.json")
p := &PrecompileExecutor{
bankKeeper: bankKeeper,
Expand Down Expand Up @@ -90,15 +90,15 @@ func NewPrecompile(bankKeeper pcommon.BankKeeper, bankMsgServer pcommon.BankMsgS
}
}

return pcommon.NewPrecompile(newAbi, p, p.address, "bank"), nil
return pcommon.NewDynamicGasPrecompile(newAbi, p, p.address, "bank"), nil
}

// RequiredGas returns the required bare minimum gas to execute the precompile.
func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64 {
return pcommon.DefaultGasCost(input, p.IsTransaction(method.Name))
}

func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM) (bz []byte, err error) {
func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
switch method.Name {
case SendMethod:
return p.send(ctx, caller, method, args, value, readOnly)
Expand All @@ -120,37 +120,38 @@ func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller
return
}

func (p PrecompileExecutor) send(ctx sdk.Context, caller common.Address, method *abi.Method, args []interface{}, value *big.Int, readOnly bool) ([]byte, error) {
func (p PrecompileExecutor) send(ctx sdk.Context, caller common.Address, method *abi.Method, args []interface{}, value *big.Int, readOnly bool) ([]byte, uint64, error) {
if readOnly {
return nil, errors.New("cannot call send from staticcall")
return nil, 0, errors.New("cannot call send from staticcall")
}
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 4); err != nil {
return nil, err
return nil, 0, err
}
denom := args[2].(string)
if denom == "" {
return nil, errors.New("invalid denom")
return nil, 0, errors.New("invalid denom")
}
pointer, _, exists := p.evmKeeper.GetERC20NativePointer(ctx, denom)
if !exists || pointer.Cmp(caller) != 0 {
return nil, fmt.Errorf("only pointer %s can send %s but got %s", pointer.Hex(), denom, caller.Hex())
return nil, 0, fmt.Errorf("only pointer %s can send %s but got %s", pointer.Hex(), denom, caller.Hex())
}
amount := args[3].(*big.Int)
if amount.Cmp(utils.Big0) == 0 {
// short circuit
return method.Outputs.Pack(true)
bz, err := method.Outputs.Pack(true)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}
senderSeiAddr, err := p.accAddressFromArg(ctx, args[0])
if err != nil {
return nil, err
return nil, 0, err
}
receiverSeiAddr, err := p.accAddressFromArg(ctx, args[1])
if err != nil {
return nil, err
return nil, 0, err
}

msg := &banktypes.MsgSend{
Expand All @@ -161,95 +162,98 @@ func (p PrecompileExecutor) send(ctx sdk.Context, caller common.Address, method

err = msg.ValidateBasic()
if err != nil {
return nil, err
return nil, 0, err
}

if _, err = p.bankMsgServer.Send(sdk.WrapSDKContext(ctx), msg); err != nil {
return nil, err
return nil, 0, err
}

return method.Outputs.Pack(true)
bz, err := method.Outputs.Pack(true)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) sendNative(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address, callingContract common.Address, value *big.Int, readOnly bool) ([]byte, error) {
func (p PrecompileExecutor) sendNative(ctx sdk.Context, method *abi.Method, args []interface{}, caller common.Address, callingContract common.Address, value *big.Int, readOnly bool) ([]byte, uint64, error) {
if readOnly {
return nil, errors.New("cannot call sendNative from staticcall")
return nil, 0, errors.New("cannot call sendNative from staticcall")
}
if ctx.EVMPrecompileCalledFromDelegateCall() {
return nil, errors.New("cannot delegatecall sendNative")
return nil, 0, errors.New("cannot delegatecall sendNative")
}
if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err
}
if value == nil || value.Sign() == 0 {
return nil, errors.New("set `value` field to non-zero to send")
return nil, 0, errors.New("set `value` field to non-zero to send")
}

senderSeiAddr, ok := p.evmKeeper.GetSeiAddress(ctx, caller)
if !ok {
return nil, errors.New("invalid addr")
return nil, 0, errors.New("invalid addr")
}

receiverAddr, ok := (args[0]).(string)
if !ok || receiverAddr == "" {
return nil, errors.New("invalid addr")
return nil, 0, errors.New("invalid addr")
}

receiverSeiAddr, err := sdk.AccAddressFromBech32(receiverAddr)
if err != nil {
return nil, err
return nil, 0, err
}

usei, wei, err := pcommon.HandlePaymentUseiWei(ctx, p.evmKeeper.GetSeiAddressOrDefault(ctx, p.address), senderSeiAddr, value, p.bankKeeper)
if err != nil {
return nil, err
return nil, 0, err
}

if err := p.bankKeeper.SendCoinsAndWei(ctx, senderSeiAddr, receiverSeiAddr, usei, wei); err != nil {
return nil, err
return nil, 0, err
}
accExists := p.accountKeeper.HasAccount(ctx, receiverSeiAddr)
if !accExists {
defer telemetry.IncrCounter(1, "new", "account")
p.accountKeeper.SetAccount(ctx, p.accountKeeper.NewAccountWithAddress(ctx, receiverSeiAddr))
}

return method.Outputs.Pack(true)
bz, err := method.Outputs.Pack(true)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) balance(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) balance(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 2); err != nil {
return nil, err
return nil, 0, err
}

addr, err := p.accAddressFromArg(ctx, args[0])
if err != nil {
return nil, err
return nil, 0, err
}
denom := args[1].(string)
if denom == "" {
return nil, errors.New("invalid denom")
return nil, 0, errors.New("invalid denom")
}

return method.Outputs.Pack(p.bankKeeper.GetBalance(ctx, addr, denom).Amount.BigInt())
bz, err := method.Outputs.Pack(p.bankKeeper.GetBalance(ctx, addr, denom).Amount.BigInt())
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) all_balances(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) all_balances(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err
}

addr, err := p.accAddressFromArg(ctx, args[0])
if err != nil {
return nil, err
return nil, 0, err
}

coins := p.bankKeeper.GetAllBalances(ctx, addr)
Expand All @@ -264,64 +268,69 @@ func (p PrecompileExecutor) all_balances(ctx sdk.Context, method *abi.Method, ar
})
}

return method.Outputs.Pack(coinBalances)
bz, err := method.Outputs.Pack(coinBalances)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) name(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) name(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err
}

denom := args[0].(string)
metadata, found := p.bankKeeper.GetDenomMetaData(ctx, denom)
if !found {
return nil, fmt.Errorf("denom %s not found", denom)
return nil, 0, fmt.Errorf("denom %s not found", denom)
}
return method.Outputs.Pack(metadata.Name)
bz, err := method.Outputs.Pack(metadata.Name)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) symbol(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) symbol(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err
}

denom := args[0].(string)
metadata, found := p.bankKeeper.GetDenomMetaData(ctx, denom)
if !found {
return nil, fmt.Errorf("denom %s not found", denom)
return nil, 0, fmt.Errorf("denom %s not found", denom)
}
return method.Outputs.Pack(metadata.Symbol)
bz, err := method.Outputs.Pack(metadata.Symbol)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) decimals(_ sdk.Context, method *abi.Method, _ []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) decimals(ctx sdk.Context, method *abi.Method, _ []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

// all native tokens are integer-based, returns decimals for microdenom (usei)
return method.Outputs.Pack(uint8(0))
bz, err := method.Outputs.Pack(uint8(0))
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) totalSupply(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) totalSupply(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err
}

denom := args[0].(string)
coin := p.bankKeeper.GetSupply(ctx, denom)
return method.Outputs.Pack(coin.Amount.BigInt())
bz, err := method.Outputs.Pack(coin.Amount.BigInt())
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) accAddressFromArg(ctx sdk.Context, arg interface{}) (sdk.AccAddress, error) {
Expand Down Expand Up @@ -351,3 +360,7 @@ func (PrecompileExecutor) IsTransaction(method string) bool {
func (p PrecompileExecutor) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("precompile", "bank")
}

func (p PrecompileExecutor) EVMKeeper() pcommon.EVMKeeper {
return p.evmKeeper
}
30 changes: 18 additions & 12 deletions precompiles/oracle/oracle.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ type OracleTwap struct {
LookbackSeconds int64 `json:"lookbackSeconds"`
}

func NewPrecompile(oracleKeeper pcommon.OracleKeeper, evmKeeper pcommon.EVMKeeper) (*pcommon.Precompile, error) {
func NewPrecompile(oracleKeeper pcommon.OracleKeeper, evmKeeper pcommon.EVMKeeper) (*pcommon.DynamicGasPrecompile, error) {
newAbi := pcommon.MustGetABI(f, "abi.json")

p := &PrecompileExecutor{
Expand All @@ -70,15 +70,15 @@ func NewPrecompile(oracleKeeper pcommon.OracleKeeper, evmKeeper pcommon.EVMKeepe
}
}

return pcommon.NewPrecompile(newAbi, p, common.HexToAddress(OracleAddress), "oracle"), nil
return pcommon.NewDynamicGasPrecompile(newAbi, p, common.HexToAddress(OracleAddress), "oracle"), nil
}

// RequiredGas returns the required bare minimum gas to execute the precompile.
func (p PrecompileExecutor) RequiredGas(input []byte, method *abi.Method) uint64 {
return pcommon.DefaultGasCost(input, p.IsTransaction(method.Name))
}

func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM) (bz []byte, err error) {
func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64) (bz []byte, remainingGas uint64, err error) {
switch method.Name {
case GetExchangeRatesMethod:
return p.getExchangeRates(ctx, method, args, value)
Expand All @@ -88,42 +88,48 @@ func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller
return
}

func (p PrecompileExecutor) getExchangeRates(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) getExchangeRates(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 0); err != nil {
return nil, err
return nil, 0, err
}
exchangeRates := []DenomOracleExchangeRatePair{}
p.oracleKeeper.IterateBaseExchangeRates(ctx, func(denom string, rate types.OracleExchangeRate) (stop bool) {
exchangeRates = append(exchangeRates, DenomOracleExchangeRatePair{Denom: denom, OracleExchangeRateVal: OracleExchangeRate{ExchangeRate: rate.ExchangeRate.String(), LastUpdate: rate.LastUpdate.String(), LastUpdateTimestamp: rate.LastUpdateTimestamp}})
return false
})

return method.Outputs.Pack(exchangeRates)
bz, err := method.Outputs.Pack(exchangeRates)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) getOracleTwaps(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, error) {
func (p PrecompileExecutor) getOracleTwaps(ctx sdk.Context, method *abi.Method, args []interface{}, value *big.Int) ([]byte, uint64, error) {
if err := pcommon.ValidateNonPayable(value); err != nil {
return nil, err
return nil, 0, err
}

if err := pcommon.ValidateArgsLength(args, 1); err != nil {
return nil, err
return nil, 0, err
}
lookbackSeconds := args[0].(uint64)
twaps, err := p.oracleKeeper.CalculateTwaps(ctx, lookbackSeconds)
if err != nil {
return nil, err
return nil, 0, err
}
// Convert twap to string
oracleTwaps := make([]OracleTwap, 0, len(twaps))
for _, twap := range twaps {
oracleTwaps = append(oracleTwaps, OracleTwap{Denom: twap.Denom, Twap: twap.Twap.String(), LookbackSeconds: twap.LookbackSeconds})
}
return method.Outputs.Pack(oracleTwaps)
bz, err := method.Outputs.Pack(oracleTwaps)
return bz, pcommon.GetRemainingGas(ctx, p.evmKeeper), err
}

func (p PrecompileExecutor) EVMKeeper() pcommon.EVMKeeper {
return p.evmKeeper
}

func (PrecompileExecutor) IsTransaction(string) bool {
Expand Down

0 comments on commit 7d65e9d

Please sign in to comment.