diff --git a/.gitignore b/.gitignore index ec583e5102..6705e73c3e 100644 --- a/.gitignore +++ b/.gitignore @@ -115,8 +115,6 @@ web_modules/ # Yarn Integrity file .yarn-integrity - - # dotenv environment variable files .env .env.development.local diff --git a/CHANGELOG.md b/CHANGELOG.md index a0da485e8b..84f9d5eadf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,13 +39,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - +- [#2345](https://github.com/NibiruChain/nibiru/pull/2345) - feat(evm): add +"eth.evm.v1.MsgConvertEvmToCoin" tx for ERC20 to bank coin conversions with a +non-Ethereum transaction. This change introduces new message types, CLI commands, +and keeper logic to cover both directions of conversion, including special-case +handling for NIBI via WNIBI. - [#2353](https://github.com/NibiruChain/nibiru/pull/2353) - refactor(oracle): remove dead code from asset registry - [#2371](https://github.com/NibiruChain/nibiru/pull/2371) - feat(evm): fix UnmarshalJSON to accept ASCII hex strings - [#2372](https://github.com/NibiruChain/nibiru/pull/2372) - feat(tokenfactory-cli): add CLI commands for set denom functions @@ -57,10 +63,10 @@ See https://github.com/dangoslen/changelog-enforcer. ## [v2.6.0](https://github.com/NibiruChain/nibiru/releases/tag/v2.6.0) - 2025-08-05 - [#2331](https://github.com/NibiruChain/nibiru/pull/2331) - test(evm-e2e): WNIBI tests for deposit, transfer and total supply -- [#2334](https://gittub.com/NibiruChain/nibiru/pull/2334) - feat(evm-embeds): Publish new version for `@nibiruchain/solidity@0.0.6`, which updates `NibiruOracleChainLinkLike.sol` to have additional methods used by Aave. +- [#2334](https://github.com/NibiruChain/nibiru/pull/2334) - feat(evm-embeds): Publish new version for `@nibiruchain/solidity@0.0.6`, which updates `NibiruOracleChainLinkLike.sol` to have additional methods used by Aave. - [#2340](https://github.com/NibiruChain/nibiru/pull/2340) - fix: evm indexer proper parsing of the start block -- [#2344](https://gittub.com/NibiruChain/nibiru/pull/23344) - feat(evm): Add some evm messages into the evm codec. -- [#2346](https://gittub.com/NibiruChain/nibiru/pull/2346) - fix(buf-gen-rs): improve Rust proto binding generation script robustness and get it to work with a forked Cosmos-SDK dependency and exit correctly on failure +- [#2344](https://github.com/NibiruChain/nibiru/pull/2344) - feat(evm): Add some evm messages into the evm codec. +- [#2346](https://github.com/NibiruChain/nibiru/pull/2346) - fix(buf-gen-rs): improve Rust proto binding generation script robustness and get it to work with a forked Cosmos-SDK dependency and exit correctly on failure - [#2348](https://github.com/NibiruChain/nibiru/pull/2348) - fix(oracle): max expiration a label rather than an invalidation for additional query liveness - [#2350](https://github.com/NibiruChain/nibiru/pull/2350) - fix(simapp): sim tests with empty validator set panic - [#2352](https://github.com/NibiruChain/nibiru/pull/2352) - chore(token-registry): Add bank coin versions of USDC and USDT from Stargate and LayerZero, and update ErisEvm.sol to fix redeem @@ -68,6 +74,7 @@ See https://github.com/dangoslen/changelog-enforcer. - [#2357](https://github.com/NibiruChain/nibiru/pull/2357) - fix: proper statedb isolation in nibiru bank_extension ### Dependencies + - Bump `form-data` from 4.0.1 to 4.0.4 ([#2347](https://github.com/NibiruChain/nibiru/pull/2347)) - Bump `golang.org/x/oauth2` from 0.16.0 to 0.27.0 ([#2342](https://github.com/NibiruChain/nibiru/pull/2342)) - Bump `undici` from 5.28.5 to 5.29.0 ([#2310](https://github.com/NibiruChain/nibiru/pull/2310)) diff --git a/api/eth/evm/v1/events.pulsar.go b/api/eth/evm/v1/events.pulsar.go index cf6841a350..78fb3fc55b 100644 --- a/api/eth/evm/v1/events.pulsar.go +++ b/api/eth/evm/v1/events.pulsar.go @@ -4477,6 +4477,697 @@ func (x *fastReflection_EventContractExecuted) ProtoMethods() *protoiface.Method } } +var ( + md_EventConvertEvmToCoin protoreflect.MessageDescriptor + fd_EventConvertEvmToCoin_sender protoreflect.FieldDescriptor + fd_EventConvertEvmToCoin_erc20_contract_address protoreflect.FieldDescriptor + fd_EventConvertEvmToCoin_to_address protoreflect.FieldDescriptor + fd_EventConvertEvmToCoin_bank_coin protoreflect.FieldDescriptor + fd_EventConvertEvmToCoin_sender_eth_addr protoreflect.FieldDescriptor +) + +func init() { + file_eth_evm_v1_events_proto_init() + md_EventConvertEvmToCoin = File_eth_evm_v1_events_proto.Messages().ByName("EventConvertEvmToCoin") + fd_EventConvertEvmToCoin_sender = md_EventConvertEvmToCoin.Fields().ByName("sender") + fd_EventConvertEvmToCoin_erc20_contract_address = md_EventConvertEvmToCoin.Fields().ByName("erc20_contract_address") + fd_EventConvertEvmToCoin_to_address = md_EventConvertEvmToCoin.Fields().ByName("to_address") + fd_EventConvertEvmToCoin_bank_coin = md_EventConvertEvmToCoin.Fields().ByName("bank_coin") + fd_EventConvertEvmToCoin_sender_eth_addr = md_EventConvertEvmToCoin.Fields().ByName("sender_eth_addr") +} + +var _ protoreflect.Message = (*fastReflection_EventConvertEvmToCoin)(nil) + +type fastReflection_EventConvertEvmToCoin EventConvertEvmToCoin + +func (x *EventConvertEvmToCoin) ProtoReflect() protoreflect.Message { + return (*fastReflection_EventConvertEvmToCoin)(x) +} + +func (x *EventConvertEvmToCoin) slowProtoReflect() protoreflect.Message { + mi := &file_eth_evm_v1_events_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_EventConvertEvmToCoin_messageType fastReflection_EventConvertEvmToCoin_messageType +var _ protoreflect.MessageType = fastReflection_EventConvertEvmToCoin_messageType{} + +type fastReflection_EventConvertEvmToCoin_messageType struct{} + +func (x fastReflection_EventConvertEvmToCoin_messageType) Zero() protoreflect.Message { + return (*fastReflection_EventConvertEvmToCoin)(nil) +} +func (x fastReflection_EventConvertEvmToCoin_messageType) New() protoreflect.Message { + return new(fastReflection_EventConvertEvmToCoin) +} +func (x fastReflection_EventConvertEvmToCoin_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_EventConvertEvmToCoin +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_EventConvertEvmToCoin) Descriptor() protoreflect.MessageDescriptor { + return md_EventConvertEvmToCoin +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_EventConvertEvmToCoin) Type() protoreflect.MessageType { + return _fastReflection_EventConvertEvmToCoin_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_EventConvertEvmToCoin) New() protoreflect.Message { + return new(fastReflection_EventConvertEvmToCoin) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_EventConvertEvmToCoin) Interface() protoreflect.ProtoMessage { + return (*EventConvertEvmToCoin)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_EventConvertEvmToCoin) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Sender != "" { + value := protoreflect.ValueOfString(x.Sender) + if !f(fd_EventConvertEvmToCoin_sender, value) { + return + } + } + if x.Erc20ContractAddress != "" { + value := protoreflect.ValueOfString(x.Erc20ContractAddress) + if !f(fd_EventConvertEvmToCoin_erc20_contract_address, value) { + return + } + } + if x.ToAddress != "" { + value := protoreflect.ValueOfString(x.ToAddress) + if !f(fd_EventConvertEvmToCoin_to_address, value) { + return + } + } + if x.BankCoin != nil { + value := protoreflect.ValueOfMessage(x.BankCoin.ProtoReflect()) + if !f(fd_EventConvertEvmToCoin_bank_coin, value) { + return + } + } + if x.SenderEthAddr != "" { + value := protoreflect.ValueOfString(x.SenderEthAddr) + if !f(fd_EventConvertEvmToCoin_sender_eth_addr, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_EventConvertEvmToCoin) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "eth.evm.v1.EventConvertEvmToCoin.sender": + return x.Sender != "" + case "eth.evm.v1.EventConvertEvmToCoin.erc20_contract_address": + return x.Erc20ContractAddress != "" + case "eth.evm.v1.EventConvertEvmToCoin.to_address": + return x.ToAddress != "" + case "eth.evm.v1.EventConvertEvmToCoin.bank_coin": + return x.BankCoin != nil + case "eth.evm.v1.EventConvertEvmToCoin.sender_eth_addr": + return x.SenderEthAddr != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.EventConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.EventConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_EventConvertEvmToCoin) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "eth.evm.v1.EventConvertEvmToCoin.sender": + x.Sender = "" + case "eth.evm.v1.EventConvertEvmToCoin.erc20_contract_address": + x.Erc20ContractAddress = "" + case "eth.evm.v1.EventConvertEvmToCoin.to_address": + x.ToAddress = "" + case "eth.evm.v1.EventConvertEvmToCoin.bank_coin": + x.BankCoin = nil + case "eth.evm.v1.EventConvertEvmToCoin.sender_eth_addr": + x.SenderEthAddr = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.EventConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.EventConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_EventConvertEvmToCoin) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "eth.evm.v1.EventConvertEvmToCoin.sender": + value := x.Sender + return protoreflect.ValueOfString(value) + case "eth.evm.v1.EventConvertEvmToCoin.erc20_contract_address": + value := x.Erc20ContractAddress + return protoreflect.ValueOfString(value) + case "eth.evm.v1.EventConvertEvmToCoin.to_address": + value := x.ToAddress + return protoreflect.ValueOfString(value) + case "eth.evm.v1.EventConvertEvmToCoin.bank_coin": + value := x.BankCoin + return protoreflect.ValueOfMessage(value.ProtoReflect()) + case "eth.evm.v1.EventConvertEvmToCoin.sender_eth_addr": + value := x.SenderEthAddr + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.EventConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.EventConvertEvmToCoin does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_EventConvertEvmToCoin) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "eth.evm.v1.EventConvertEvmToCoin.sender": + x.Sender = value.Interface().(string) + case "eth.evm.v1.EventConvertEvmToCoin.erc20_contract_address": + x.Erc20ContractAddress = value.Interface().(string) + case "eth.evm.v1.EventConvertEvmToCoin.to_address": + x.ToAddress = value.Interface().(string) + case "eth.evm.v1.EventConvertEvmToCoin.bank_coin": + x.BankCoin = value.Message().Interface().(*v1beta1.Coin) + case "eth.evm.v1.EventConvertEvmToCoin.sender_eth_addr": + x.SenderEthAddr = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.EventConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.EventConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_EventConvertEvmToCoin) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "eth.evm.v1.EventConvertEvmToCoin.bank_coin": + if x.BankCoin == nil { + x.BankCoin = new(v1beta1.Coin) + } + return protoreflect.ValueOfMessage(x.BankCoin.ProtoReflect()) + case "eth.evm.v1.EventConvertEvmToCoin.sender": + panic(fmt.Errorf("field sender of message eth.evm.v1.EventConvertEvmToCoin is not mutable")) + case "eth.evm.v1.EventConvertEvmToCoin.erc20_contract_address": + panic(fmt.Errorf("field erc20_contract_address of message eth.evm.v1.EventConvertEvmToCoin is not mutable")) + case "eth.evm.v1.EventConvertEvmToCoin.to_address": + panic(fmt.Errorf("field to_address of message eth.evm.v1.EventConvertEvmToCoin is not mutable")) + case "eth.evm.v1.EventConvertEvmToCoin.sender_eth_addr": + panic(fmt.Errorf("field sender_eth_addr of message eth.evm.v1.EventConvertEvmToCoin is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.EventConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.EventConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_EventConvertEvmToCoin) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "eth.evm.v1.EventConvertEvmToCoin.sender": + return protoreflect.ValueOfString("") + case "eth.evm.v1.EventConvertEvmToCoin.erc20_contract_address": + return protoreflect.ValueOfString("") + case "eth.evm.v1.EventConvertEvmToCoin.to_address": + return protoreflect.ValueOfString("") + case "eth.evm.v1.EventConvertEvmToCoin.bank_coin": + m := new(v1beta1.Coin) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + case "eth.evm.v1.EventConvertEvmToCoin.sender_eth_addr": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.EventConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.EventConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_EventConvertEvmToCoin) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in eth.evm.v1.EventConvertEvmToCoin", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_EventConvertEvmToCoin) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_EventConvertEvmToCoin) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_EventConvertEvmToCoin) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_EventConvertEvmToCoin) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*EventConvertEvmToCoin) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Sender) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Erc20ContractAddress) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.ToAddress) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.BankCoin != nil { + l = options.Size(x.BankCoin) + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.SenderEthAddr) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*EventConvertEvmToCoin) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.SenderEthAddr) > 0 { + i -= len(x.SenderEthAddr) + copy(dAtA[i:], x.SenderEthAddr) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.SenderEthAddr))) + i-- + dAtA[i] = 0x32 + } + if x.BankCoin != nil { + encoded, err := options.Marshal(x.BankCoin) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x22 + } + if len(x.ToAddress) > 0 { + i -= len(x.ToAddress) + copy(dAtA[i:], x.ToAddress) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.ToAddress))) + i-- + dAtA[i] = 0x1a + } + if len(x.Erc20ContractAddress) > 0 { + i -= len(x.Erc20ContractAddress) + copy(dAtA[i:], x.Erc20ContractAddress) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Erc20ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if len(x.Sender) > 0 { + i -= len(x.Sender) + copy(dAtA[i:], x.Sender) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Sender))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*EventConvertEvmToCoin) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: EventConvertEvmToCoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: EventConvertEvmToCoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Erc20ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Erc20ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ToAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.ToAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field BankCoin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.BankCoin == nil { + x.BankCoin = &v1beta1.Coin{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.BankCoin); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field SenderEthAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.SenderEthAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + // Copyright (c) 2023-2024 Nibi, Inc. // Code generated by protoc-gen-go. DO NOT EDIT. @@ -4620,7 +5311,8 @@ func (x *EventTxLog) GetLogs() []*Log { return nil } -// EventBlockBloom defines an Ethereum block bloom filter event +// EventBlockBloom contains the bloom filter for an Ethereum block. +// The bloom filter encodes logs for efficient event filtering. type EventBlockBloom struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4717,7 +5409,8 @@ func (x *EventFunTokenCreated) GetIsMadeFromCoin() bool { return false } -// ConvertCoinToEvm defines sending fun token to erc20 event. +// EventConvertCoinToEvm is an event emitted when converting Bank Coins into +// ERC20 tokens with the "eth.evm.v1.MsgConvertCoinToEvm" transaction message. type EventConvertCoinToEvm struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -4917,6 +5610,75 @@ func (x *EventContractExecuted) GetContractAddr() string { return "" } +// EventConvertEvmToCoin is an event emitted when converting ERC20 tokens to Bank +// Coins with the "eth.evm.v1.MsgConvertEvmToCoin" transaction message. +type EventConvertEvmToCoin struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Erc20ContractAddress string `protobuf:"bytes,2,opt,name=erc20_contract_address,json=erc20ContractAddress,proto3" json:"erc20_contract_address,omitempty"` + ToAddress string `protobuf:"bytes,3,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` + BankCoin *v1beta1.Coin `protobuf:"bytes,4,opt,name=bank_coin,json=bankCoin,proto3" json:"bank_coin,omitempty"` + SenderEthAddr string `protobuf:"bytes,6,opt,name=sender_eth_addr,json=senderEthAddr,proto3" json:"sender_eth_addr,omitempty"` +} + +func (x *EventConvertEvmToCoin) Reset() { + *x = EventConvertEvmToCoin{} + if protoimpl.UnsafeEnabled { + mi := &file_eth_evm_v1_events_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventConvertEvmToCoin) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventConvertEvmToCoin) ProtoMessage() {} + +// Deprecated: Use EventConvertEvmToCoin.ProtoReflect.Descriptor instead. +func (*EventConvertEvmToCoin) Descriptor() ([]byte, []int) { + return file_eth_evm_v1_events_proto_rawDescGZIP(), []int{8} +} + +func (x *EventConvertEvmToCoin) GetSender() string { + if x != nil { + return x.Sender + } + return "" +} + +func (x *EventConvertEvmToCoin) GetErc20ContractAddress() string { + if x != nil { + return x.Erc20ContractAddress + } + return "" +} + +func (x *EventConvertEvmToCoin) GetToAddress() string { + if x != nil { + return x.ToAddress + } + return "" +} + +func (x *EventConvertEvmToCoin) GetBankCoin() *v1beta1.Coin { + if x != nil { + return x.BankCoin + } + return nil +} + +func (x *EventConvertEvmToCoin) GetSenderEthAddr() string { + if x != nil { + return x.SenderEthAddr + } + return "" +} + var File_eth_evm_v1_events_proto protoreflect.FileDescriptor var file_eth_evm_v1_events_proto_rawDesc = []byte{ @@ -4924,9 +5686,9 @@ var file_eth_evm_v1_events_proto_rawDesc = []byte{ 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x1a, 0x1e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x65, 0x74, 0x68, - 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, + 0x31, 0x2f, 0x65, 0x76, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, + 0x6f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x67, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc2, 0x01, 0x0a, 0x0f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, @@ -4987,16 +5749,32 @@ var file_eth_evm_v1_events_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x42, 0x8a, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, - 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x50, 0x01, 0x5a, 0x21, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, 0x3b, - 0x65, 0x76, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x45, 0x58, 0xaa, 0x02, 0x0a, 0x45, 0x74, - 0x68, 0x2e, 0x45, 0x76, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x45, 0x74, 0x68, 0x5c, 0x45, - 0x76, 0x6d, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, 0x6d, 0x5c, - 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x0c, 0x45, 0x74, 0x68, 0x3a, 0x3a, 0x45, 0x76, 0x6d, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0xfe, 0x01, 0x0a, 0x15, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x74, 0x45, 0x76, 0x6d, 0x54, 0x6f, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x65, 0x72, 0x63, 0x32, 0x30, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x14, 0x65, 0x72, 0x63, 0x32, 0x30, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x50, 0x0a, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x5f, + 0x63, 0x6f, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x43, 0x6f, 0x69, 0x6e, 0x42, 0x18, 0xc8, 0xde, 0x1f, 0x00, 0xf2, 0xde, 0x1f, 0x10, 0x79, + 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x22, 0x52, + 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x5f, 0x65, 0x74, 0x68, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x45, 0x74, 0x68, 0x41, 0x64, 0x64, + 0x72, 0x42, 0x8a, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, + 0x6d, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x21, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, + 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, + 0x3b, 0x65, 0x76, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x45, 0x58, 0xaa, 0x02, 0x0a, 0x45, + 0x74, 0x68, 0x2e, 0x45, 0x76, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x45, 0x74, 0x68, 0x5c, + 0x45, 0x76, 0x6d, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, 0x6d, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x0c, 0x45, 0x74, 0x68, 0x3a, 0x3a, 0x45, 0x76, 0x6d, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5011,7 +5789,7 @@ func file_eth_evm_v1_events_proto_rawDescGZIP() []byte { return file_eth_evm_v1_events_proto_rawDescData } -var file_eth_evm_v1_events_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_eth_evm_v1_events_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_eth_evm_v1_events_proto_goTypes = []interface{}{ (*EventEthereumTx)(nil), // 0: eth.evm.v1.EventEthereumTx (*EventTxLog)(nil), // 1: eth.evm.v1.EventTxLog @@ -5021,17 +5799,19 @@ var file_eth_evm_v1_events_proto_goTypes = []interface{}{ (*EventTransfer)(nil), // 5: eth.evm.v1.EventTransfer (*EventContractDeployed)(nil), // 6: eth.evm.v1.EventContractDeployed (*EventContractExecuted)(nil), // 7: eth.evm.v1.EventContractExecuted - (*Log)(nil), // 8: eth.evm.v1.Log - (*v1beta1.Coin)(nil), // 9: cosmos.base.v1beta1.Coin + (*EventConvertEvmToCoin)(nil), // 8: eth.evm.v1.EventConvertEvmToCoin + (*Log)(nil), // 9: eth.evm.v1.Log + (*v1beta1.Coin)(nil), // 10: cosmos.base.v1beta1.Coin } var file_eth_evm_v1_events_proto_depIdxs = []int32{ - 8, // 0: eth.evm.v1.EventTxLog.logs:type_name -> eth.evm.v1.Log - 9, // 1: eth.evm.v1.EventConvertCoinToEvm.bank_coin:type_name -> cosmos.base.v1beta1.Coin - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 9, // 0: eth.evm.v1.EventTxLog.logs:type_name -> eth.evm.v1.Log + 10, // 1: eth.evm.v1.EventConvertCoinToEvm.bank_coin:type_name -> cosmos.base.v1beta1.Coin + 10, // 2: eth.evm.v1.EventConvertEvmToCoin.bank_coin:type_name -> cosmos.base.v1beta1.Coin + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_eth_evm_v1_events_proto_init() } @@ -5137,6 +5917,18 @@ func file_eth_evm_v1_events_proto_init() { return nil } } + file_eth_evm_v1_events_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EventConvertEvmToCoin); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -5144,7 +5936,7 @@ func file_eth_evm_v1_events_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_eth_evm_v1_events_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/api/eth/evm/v1/evm.pulsar.go b/api/eth/evm/v1/evm.pulsar.go index 87026d65ff..27d93744b4 100644 --- a/api/eth/evm/v1/evm.pulsar.go +++ b/api/eth/evm/v1/evm.pulsar.go @@ -648,6 +648,7 @@ var ( fd_Params_extra_eips protoreflect.FieldDescriptor fd_Params_evm_channels protoreflect.FieldDescriptor fd_Params_create_funtoken_fee protoreflect.FieldDescriptor + fd_Params_canonical_wnibi protoreflect.FieldDescriptor ) func init() { @@ -656,6 +657,7 @@ func init() { fd_Params_extra_eips = md_Params.Fields().ByName("extra_eips") fd_Params_evm_channels = md_Params.Fields().ByName("evm_channels") fd_Params_create_funtoken_fee = md_Params.Fields().ByName("create_funtoken_fee") + fd_Params_canonical_wnibi = md_Params.Fields().ByName("canonical_wnibi") } var _ protoreflect.Message = (*fastReflection_Params)(nil) @@ -741,6 +743,12 @@ func (x *fastReflection_Params) Range(f func(protoreflect.FieldDescriptor, proto return } } + if x.CanonicalWnibi != "" { + value := protoreflect.ValueOfString(x.CanonicalWnibi) + if !f(fd_Params_canonical_wnibi, value) { + return + } + } } // Has reports whether a field is populated. @@ -762,6 +770,8 @@ func (x *fastReflection_Params) Has(fd protoreflect.FieldDescriptor) bool { return len(x.EvmChannels) != 0 case "eth.evm.v1.Params.create_funtoken_fee": return x.CreateFuntokenFee != "" + case "eth.evm.v1.Params.canonical_wnibi": + return x.CanonicalWnibi != "" default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.Params")) @@ -784,6 +794,8 @@ func (x *fastReflection_Params) Clear(fd protoreflect.FieldDescriptor) { x.EvmChannels = nil case "eth.evm.v1.Params.create_funtoken_fee": x.CreateFuntokenFee = "" + case "eth.evm.v1.Params.canonical_wnibi": + x.CanonicalWnibi = "" default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.Params")) @@ -815,6 +827,9 @@ func (x *fastReflection_Params) Get(descriptor protoreflect.FieldDescriptor) pro case "eth.evm.v1.Params.create_funtoken_fee": value := x.CreateFuntokenFee return protoreflect.ValueOfString(value) + case "eth.evm.v1.Params.canonical_wnibi": + value := x.CanonicalWnibi + return protoreflect.ValueOfString(value) default: if descriptor.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.Params")) @@ -845,6 +860,8 @@ func (x *fastReflection_Params) Set(fd protoreflect.FieldDescriptor, value proto x.EvmChannels = *clv.list case "eth.evm.v1.Params.create_funtoken_fee": x.CreateFuntokenFee = value.Interface().(string) + case "eth.evm.v1.Params.canonical_wnibi": + x.CanonicalWnibi = value.Interface().(string) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.Params")) @@ -879,6 +896,8 @@ func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protore return protoreflect.ValueOfList(value) case "eth.evm.v1.Params.create_funtoken_fee": panic(fmt.Errorf("field create_funtoken_fee of message eth.evm.v1.Params is not mutable")) + case "eth.evm.v1.Params.canonical_wnibi": + panic(fmt.Errorf("field canonical_wnibi of message eth.evm.v1.Params is not mutable")) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.Params")) @@ -900,6 +919,8 @@ func (x *fastReflection_Params) NewField(fd protoreflect.FieldDescriptor) protor return protoreflect.ValueOfList(&_Params_8_list{list: &list}) case "eth.evm.v1.Params.create_funtoken_fee": return protoreflect.ValueOfString("") + case "eth.evm.v1.Params.canonical_wnibi": + return protoreflect.ValueOfString("") default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.Params")) @@ -986,6 +1007,10 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { if l > 0 { n += 1 + l + runtime.Sov(uint64(l)) } + l = len(x.CanonicalWnibi) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } if x.unknownFields != nil { n += len(x.unknownFields) } @@ -1015,6 +1040,13 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } + if len(x.CanonicalWnibi) > 0 { + i -= len(x.CanonicalWnibi) + copy(dAtA[i:], x.CanonicalWnibi) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.CanonicalWnibi))) + i-- + dAtA[i] = 0x52 + } if len(x.CreateFuntokenFee) > 0 { i -= len(x.CreateFuntokenFee) copy(dAtA[i:], x.CreateFuntokenFee) @@ -1241,6 +1273,38 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods { } x.CreateFuntokenFee = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 10: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field CanonicalWnibi", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.CanonicalWnibi = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := runtime.Skip(dAtA[iNdEx:]) @@ -4667,6 +4731,8 @@ type Params struct { // Fee deducted and burned when calling "CreateFunToken" in units of // "evm_denom". CreateFuntokenFee string `protobuf:"bytes,9,opt,name=create_funtoken_fee,json=createFuntokenFee,proto3" json:"create_funtoken_fee,omitempty"` + // Hexadecimal address of the canonical WNIBI contract on Nibiru mainnet + CanonicalWnibi string `protobuf:"bytes,10,opt,name=canonical_wnibi,json=canonicalWnibi,proto3" json:"canonical_wnibi,omitempty"` } func (x *Params) Reset() { @@ -4710,6 +4776,13 @@ func (x *Params) GetCreateFuntokenFee() string { return "" } +func (x *Params) GetCanonicalWnibi() string { + if x != nil { + return x.CanonicalWnibi + } + return "" +} + // State represents a single Storage key value pair item. type State struct { state protoimpl.MessageState @@ -5091,7 +5164,7 @@ var file_eth_evm_v1_evm_proto_rawDesc = []byte{ 0x52, 0x09, 0x62, 0x61, 0x6e, 0x6b, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x12, 0x29, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x6d, 0x61, 0x64, 0x65, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4d, 0x61, 0x64, 0x65, 0x46, 0x72, - 0x6f, 0x6d, 0x43, 0x6f, 0x69, 0x6e, 0x22, 0xf8, 0x01, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x6f, 0x6d, 0x43, 0x6f, 0x69, 0x6e, 0x22, 0xd9, 0x02, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x65, 0x69, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x03, 0x42, 0x22, 0xe2, 0xde, 0x1f, 0x09, 0x45, 0x78, 0x74, 0x72, 0x61, 0x45, 0x49, 0x50, 0x73, 0xf2, 0xde, 0x1f, 0x11, 0x79, 0x61, 0x6d, 0x6c, 0x3a, 0x22, 0x65, 0x78, @@ -5104,85 +5177,91 @@ var file_eth_evm_v1_evm_proto_rawDesc = []byte{ 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1d, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x15, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x49, 0x6e, 0x74, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x65, 0x65, 0x3a, 0x04, 0xe8, 0xa0, 0x1f, 0x01, 0x4a, 0x04, 0x08, - 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, - 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, - 0x08, 0x22, 0x2f, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x22, 0xca, 0x02, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x32, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x0f, 0xea, 0xde, 0x1f, 0x0b, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x13, 0xea, 0xde, 0x1f, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x74, 0x78, 0x48, 0x61, - 0x73, 0x68, 0x12, 0x2f, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x14, 0xea, 0xde, 0x1f, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x74, 0x78, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x12, 0x2c, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, - 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xea, 0xde, 0x1f, 0x09, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, - 0x68, 0x12, 0x22, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x0c, 0xea, 0xde, 0x1f, 0x08, 0x6c, 0x6f, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x05, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, - 0x61, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x0c, 0x73, 0x74, 0x6f, 0x72, - 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x0f, - 0xea, 0xde, 0x1f, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x52, - 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x3a, 0x04, 0x88, 0xa0, - 0x1f, 0x00, 0x22, 0x43, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x33, 0x0a, 0x0d, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x70, 0x5f, 0x63, - 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x0f, 0xea, 0xde, 0x1f, 0x0b, 0x6f, - 0x6e, 0x6c, 0x79, 0x54, 0x6f, 0x70, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x0b, 0x6f, 0x6e, 0x6c, 0x79, - 0x54, 0x6f, 0x70, 0x43, 0x61, 0x6c, 0x6c, 0x22, 0xfa, 0x03, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x63, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x12, - 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x65, - 0x78, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x72, 0x65, 0x65, 0x78, 0x65, - 0x63, 0x12, 0x35, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, - 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x42, 0x10, 0xea, 0xde, 0x1f, 0x0c, 0x64, 0x69, - 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x3b, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x42, 0x12, 0xea, 0xde, 0x1f, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x12, 0x35, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x42, 0x10, 0xea, 0xde, 0x1f, 0x0c, 0x65, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x42, 0x0a, 0x12, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x08, 0x42, 0x14, 0xea, 0xde, 0x1f, 0x10, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x4f, 0x0a, 0x0d, - 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x10, 0xea, - 0xde, 0x1f, 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, 0x08, - 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, 0x52, - 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, - 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x42, 0x87, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x45, 0x76, 0x6d, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x21, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, - 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, - 0x3b, 0x65, 0x76, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x45, 0x58, 0xaa, 0x02, 0x0a, 0x45, - 0x74, 0x68, 0x2e, 0x45, 0x76, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x45, 0x74, 0x68, 0x5c, - 0x45, 0x76, 0x6d, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, 0x6d, - 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x0c, 0x45, 0x74, 0x68, 0x3a, 0x3a, 0x45, 0x76, 0x6d, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x65, 0x65, 0x12, 0x5f, 0x0a, 0x0f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, + 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x6e, 0x69, 0x62, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x36, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4e, 0x69, 0x62, 0x69, 0x72, 0x75, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x2f, 0x6e, 0x69, 0x62, 0x69, 0x72, 0x75, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x74, 0x68, 0x2e, 0x45, + 0x49, 0x50, 0x35, 0x35, 0x41, 0x64, 0x64, 0x72, 0x52, 0x0e, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, + 0x63, 0x61, 0x6c, 0x57, 0x6e, 0x69, 0x62, 0x69, 0x3a, 0x04, 0xe8, 0xa0, 0x1f, 0x01, 0x4a, 0x04, + 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, + 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, + 0x10, 0x08, 0x22, 0x2f, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x22, 0xca, 0x02, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x32, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x0f, 0xea, 0xde, 0x1f, 0x0b, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x07, 0x74, 0x78, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x13, 0xea, 0xde, 0x1f, 0x0f, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x06, 0x74, 0x78, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x2f, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, 0x14, 0xea, 0xde, 0x1f, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x07, 0x74, 0x78, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2c, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xea, 0xde, 0x1f, 0x09, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, + 0x73, 0x68, 0x12, 0x22, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x0c, 0xea, 0xde, 0x1f, 0x08, 0x6c, 0x6f, 0x67, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, + 0x22, 0x61, 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x75, 0x70, 0x6c, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x0c, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, + 0x0f, 0xea, 0xde, 0x1f, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, + 0x52, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x73, 0x3a, 0x04, 0x88, + 0xa0, 0x1f, 0x00, 0x22, 0x43, 0x0a, 0x0c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x33, 0x0a, 0x0d, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x74, 0x6f, 0x70, 0x5f, + 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x0f, 0xea, 0xde, 0x1f, 0x0b, + 0x6f, 0x6e, 0x6c, 0x79, 0x54, 0x6f, 0x70, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x0b, 0x6f, 0x6e, 0x6c, + 0x79, 0x54, 0x6f, 0x70, 0x43, 0x61, 0x6c, 0x6c, 0x22, 0xfa, 0x03, 0x0a, 0x0b, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x72, 0x61, 0x63, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x65, 0x78, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x72, 0x65, 0x65, 0x78, + 0x65, 0x63, 0x12, 0x35, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, + 0x61, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x42, 0x10, 0xea, 0xde, 0x1f, 0x0c, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x52, 0x0c, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x12, 0x3b, 0x0a, 0x0f, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x42, 0x12, 0xea, 0xde, 0x1f, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x64, 0x65, 0x62, 0x75, 0x67, 0x12, 0x14, 0x0a, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x12, 0x35, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x42, 0x10, 0xea, 0xde, 0x1f, 0x0c, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x52, 0x0c, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x42, 0x0a, 0x12, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x08, 0x42, 0x14, 0xea, 0xde, 0x1f, 0x10, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x10, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x4f, 0x0a, + 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x54, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x10, + 0xea, 0xde, 0x1f, 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x0c, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4a, 0x04, + 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a, 0x04, 0x08, 0x0a, 0x10, 0x0b, + 0x52, 0x0e, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x87, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x45, 0x76, 0x6d, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x21, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, + 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, + 0x31, 0x3b, 0x65, 0x76, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x45, 0x58, 0xaa, 0x02, 0x0a, + 0x45, 0x74, 0x68, 0x2e, 0x45, 0x76, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x45, 0x74, 0x68, + 0x5c, 0x45, 0x76, 0x6d, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, + 0x6d, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x0c, 0x45, 0x74, 0x68, 0x3a, 0x3a, 0x45, 0x76, 0x6d, 0x3a, 0x3a, 0x56, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/eth/evm/v1/tx.pulsar.go b/api/eth/evm/v1/tx.pulsar.go index 8ac17b52fd..409ed2a77f 100644 --- a/api/eth/evm/v1/tx.pulsar.go +++ b/api/eth/evm/v1/tx.pulsar.go @@ -7665,6 +7665,974 @@ func (x *fastReflection_MsgConvertCoinToEvmResponse) ProtoMethods() *protoiface. } } +var ( + md_MsgConvertEvmToCoin protoreflect.MessageDescriptor + fd_MsgConvertEvmToCoin_sender protoreflect.FieldDescriptor + fd_MsgConvertEvmToCoin_erc20_addr protoreflect.FieldDescriptor + fd_MsgConvertEvmToCoin_amount protoreflect.FieldDescriptor + fd_MsgConvertEvmToCoin_to_addr protoreflect.FieldDescriptor +) + +func init() { + file_eth_evm_v1_tx_proto_init() + md_MsgConvertEvmToCoin = File_eth_evm_v1_tx_proto.Messages().ByName("MsgConvertEvmToCoin") + fd_MsgConvertEvmToCoin_sender = md_MsgConvertEvmToCoin.Fields().ByName("sender") + fd_MsgConvertEvmToCoin_erc20_addr = md_MsgConvertEvmToCoin.Fields().ByName("erc20_addr") + fd_MsgConvertEvmToCoin_amount = md_MsgConvertEvmToCoin.Fields().ByName("amount") + fd_MsgConvertEvmToCoin_to_addr = md_MsgConvertEvmToCoin.Fields().ByName("to_addr") +} + +var _ protoreflect.Message = (*fastReflection_MsgConvertEvmToCoin)(nil) + +type fastReflection_MsgConvertEvmToCoin MsgConvertEvmToCoin + +func (x *MsgConvertEvmToCoin) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgConvertEvmToCoin)(x) +} + +func (x *MsgConvertEvmToCoin) slowProtoReflect() protoreflect.Message { + mi := &file_eth_evm_v1_tx_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgConvertEvmToCoin_messageType fastReflection_MsgConvertEvmToCoin_messageType +var _ protoreflect.MessageType = fastReflection_MsgConvertEvmToCoin_messageType{} + +type fastReflection_MsgConvertEvmToCoin_messageType struct{} + +func (x fastReflection_MsgConvertEvmToCoin_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgConvertEvmToCoin)(nil) +} +func (x fastReflection_MsgConvertEvmToCoin_messageType) New() protoreflect.Message { + return new(fastReflection_MsgConvertEvmToCoin) +} +func (x fastReflection_MsgConvertEvmToCoin_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgConvertEvmToCoin +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgConvertEvmToCoin) Descriptor() protoreflect.MessageDescriptor { + return md_MsgConvertEvmToCoin +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgConvertEvmToCoin) Type() protoreflect.MessageType { + return _fastReflection_MsgConvertEvmToCoin_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgConvertEvmToCoin) New() protoreflect.Message { + return new(fastReflection_MsgConvertEvmToCoin) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgConvertEvmToCoin) Interface() protoreflect.ProtoMessage { + return (*MsgConvertEvmToCoin)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgConvertEvmToCoin) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.Sender != "" { + value := protoreflect.ValueOfString(x.Sender) + if !f(fd_MsgConvertEvmToCoin_sender, value) { + return + } + } + if x.Erc20Addr != "" { + value := protoreflect.ValueOfString(x.Erc20Addr) + if !f(fd_MsgConvertEvmToCoin_erc20_addr, value) { + return + } + } + if x.Amount != "" { + value := protoreflect.ValueOfString(x.Amount) + if !f(fd_MsgConvertEvmToCoin_amount, value) { + return + } + } + if x.ToAddr != "" { + value := protoreflect.ValueOfString(x.ToAddr) + if !f(fd_MsgConvertEvmToCoin_to_addr, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgConvertEvmToCoin) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "eth.evm.v1.MsgConvertEvmToCoin.sender": + return x.Sender != "" + case "eth.evm.v1.MsgConvertEvmToCoin.erc20_addr": + return x.Erc20Addr != "" + case "eth.evm.v1.MsgConvertEvmToCoin.amount": + return x.Amount != "" + case "eth.evm.v1.MsgConvertEvmToCoin.to_addr": + return x.ToAddr != "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoin) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "eth.evm.v1.MsgConvertEvmToCoin.sender": + x.Sender = "" + case "eth.evm.v1.MsgConvertEvmToCoin.erc20_addr": + x.Erc20Addr = "" + case "eth.evm.v1.MsgConvertEvmToCoin.amount": + x.Amount = "" + case "eth.evm.v1.MsgConvertEvmToCoin.to_addr": + x.ToAddr = "" + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgConvertEvmToCoin) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "eth.evm.v1.MsgConvertEvmToCoin.sender": + value := x.Sender + return protoreflect.ValueOfString(value) + case "eth.evm.v1.MsgConvertEvmToCoin.erc20_addr": + value := x.Erc20Addr + return protoreflect.ValueOfString(value) + case "eth.evm.v1.MsgConvertEvmToCoin.amount": + value := x.Amount + return protoreflect.ValueOfString(value) + case "eth.evm.v1.MsgConvertEvmToCoin.to_addr": + value := x.ToAddr + return protoreflect.ValueOfString(value) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoin does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoin) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "eth.evm.v1.MsgConvertEvmToCoin.sender": + x.Sender = value.Interface().(string) + case "eth.evm.v1.MsgConvertEvmToCoin.erc20_addr": + x.Erc20Addr = value.Interface().(string) + case "eth.evm.v1.MsgConvertEvmToCoin.amount": + x.Amount = value.Interface().(string) + case "eth.evm.v1.MsgConvertEvmToCoin.to_addr": + x.ToAddr = value.Interface().(string) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoin) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "eth.evm.v1.MsgConvertEvmToCoin.sender": + panic(fmt.Errorf("field sender of message eth.evm.v1.MsgConvertEvmToCoin is not mutable")) + case "eth.evm.v1.MsgConvertEvmToCoin.erc20_addr": + panic(fmt.Errorf("field erc20_addr of message eth.evm.v1.MsgConvertEvmToCoin is not mutable")) + case "eth.evm.v1.MsgConvertEvmToCoin.amount": + panic(fmt.Errorf("field amount of message eth.evm.v1.MsgConvertEvmToCoin is not mutable")) + case "eth.evm.v1.MsgConvertEvmToCoin.to_addr": + panic(fmt.Errorf("field to_addr of message eth.evm.v1.MsgConvertEvmToCoin is not mutable")) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgConvertEvmToCoin) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "eth.evm.v1.MsgConvertEvmToCoin.sender": + return protoreflect.ValueOfString("") + case "eth.evm.v1.MsgConvertEvmToCoin.erc20_addr": + return protoreflect.ValueOfString("") + case "eth.evm.v1.MsgConvertEvmToCoin.amount": + return protoreflect.ValueOfString("") + case "eth.evm.v1.MsgConvertEvmToCoin.to_addr": + return protoreflect.ValueOfString("") + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoin")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoin does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgConvertEvmToCoin) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in eth.evm.v1.MsgConvertEvmToCoin", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgConvertEvmToCoin) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoin) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgConvertEvmToCoin) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgConvertEvmToCoin) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgConvertEvmToCoin) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + l = len(x.Sender) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Erc20Addr) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.Amount) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + l = len(x.ToAddr) + if l > 0 { + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgConvertEvmToCoin) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if len(x.ToAddr) > 0 { + i -= len(x.ToAddr) + copy(dAtA[i:], x.ToAddr) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.ToAddr))) + i-- + dAtA[i] = 0x22 + } + if len(x.Amount) > 0 { + i -= len(x.Amount) + copy(dAtA[i:], x.Amount) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Amount))) + i-- + dAtA[i] = 0x1a + } + if len(x.Erc20Addr) > 0 { + i -= len(x.Erc20Addr) + copy(dAtA[i:], x.Erc20Addr) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Erc20Addr))) + i-- + dAtA[i] = 0x12 + } + if len(x.Sender) > 0 { + i -= len(x.Sender) + copy(dAtA[i:], x.Sender) + i = runtime.EncodeVarint(dAtA, i, uint64(len(x.Sender))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgConvertEvmToCoin) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgConvertEvmToCoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgConvertEvmToCoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Erc20Addr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Erc20Addr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field ToAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + x.ToAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_MsgConvertEvmToCoinResponse protoreflect.MessageDescriptor +) + +func init() { + file_eth_evm_v1_tx_proto_init() + md_MsgConvertEvmToCoinResponse = File_eth_evm_v1_tx_proto.Messages().ByName("MsgConvertEvmToCoinResponse") +} + +var _ protoreflect.Message = (*fastReflection_MsgConvertEvmToCoinResponse)(nil) + +type fastReflection_MsgConvertEvmToCoinResponse MsgConvertEvmToCoinResponse + +func (x *MsgConvertEvmToCoinResponse) ProtoReflect() protoreflect.Message { + return (*fastReflection_MsgConvertEvmToCoinResponse)(x) +} + +func (x *MsgConvertEvmToCoinResponse) slowProtoReflect() protoreflect.Message { + mi := &file_eth_evm_v1_tx_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_MsgConvertEvmToCoinResponse_messageType fastReflection_MsgConvertEvmToCoinResponse_messageType +var _ protoreflect.MessageType = fastReflection_MsgConvertEvmToCoinResponse_messageType{} + +type fastReflection_MsgConvertEvmToCoinResponse_messageType struct{} + +func (x fastReflection_MsgConvertEvmToCoinResponse_messageType) Zero() protoreflect.Message { + return (*fastReflection_MsgConvertEvmToCoinResponse)(nil) +} +func (x fastReflection_MsgConvertEvmToCoinResponse_messageType) New() protoreflect.Message { + return new(fastReflection_MsgConvertEvmToCoinResponse) +} +func (x fastReflection_MsgConvertEvmToCoinResponse_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_MsgConvertEvmToCoinResponse +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Descriptor() protoreflect.MessageDescriptor { + return md_MsgConvertEvmToCoinResponse +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Type() protoreflect.MessageType { + return _fastReflection_MsgConvertEvmToCoinResponse_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_MsgConvertEvmToCoinResponse) New() protoreflect.Message { + return new(fastReflection_MsgConvertEvmToCoinResponse) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Interface() protoreflect.ProtoMessage { + return (*MsgConvertEvmToCoinResponse)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoinResponse")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoinResponse does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoinResponse")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoinResponse does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoinResponse")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoinResponse does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoinResponse")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoinResponse does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoinResponse) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoinResponse")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoinResponse does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_MsgConvertEvmToCoinResponse) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: eth.evm.v1.MsgConvertEvmToCoinResponse")) + } + panic(fmt.Errorf("message eth.evm.v1.MsgConvertEvmToCoinResponse does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_MsgConvertEvmToCoinResponse) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in eth.evm.v1.MsgConvertEvmToCoinResponse", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_MsgConvertEvmToCoinResponse) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_MsgConvertEvmToCoinResponse) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_MsgConvertEvmToCoinResponse) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_MsgConvertEvmToCoinResponse) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*MsgConvertEvmToCoinResponse) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*MsgConvertEvmToCoinResponse) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*MsgConvertEvmToCoinResponse) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgConvertEvmToCoinResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: MsgConvertEvmToCoinResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + // Copyright (c) 2023-2024 Nibi, Inc. // Code generated by protoc-gen-go. DO NOT EDIT. @@ -8505,15 +9473,114 @@ func (*MsgConvertCoinToEvmResponse) Descriptor() ([]byte, []int) { return file_eth_evm_v1_tx_proto_rawDescGZIP(), []int{11} } +// MsgConvertEvmToCoin: Arguments to send an ERC20 token to bank coin representation +type MsgConvertEvmToCoin struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Sender: "nibi"-prefixed Bech32 address for the signer of the transaction. + // This is also the address whose ERC20 balance will be deducted. + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // Hexadecimal address of the ERC20 token to be converted and sent + Erc20Addr string `protobuf:"bytes,2,opt,name=erc20_addr,json=erc20Addr,proto3" json:"erc20_addr,omitempty"` + // Amount of ERC20 tokens to convert + Amount string `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` + // Recipient address for the bank coins in Ethereum hexadecimal or + // nibi-prefixed Bech32 format. + // + // Currently, accounts corresponding to Wasm contracts cannot hold ERC20 tokens + // because the function that maps between Bech32 and Eth hex addresses is not + // bijective for these types of accounts. + // + // See [bug(evm): nibid q evm account is not symmetric for wasm + // addresses](https://github.com/NibiruChain/nibiru/issues/2138) + ToAddr string `protobuf:"bytes,4,opt,name=to_addr,json=toAddr,proto3" json:"to_addr,omitempty"` +} + +func (x *MsgConvertEvmToCoin) Reset() { + *x = MsgConvertEvmToCoin{} + if protoimpl.UnsafeEnabled { + mi := &file_eth_evm_v1_tx_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgConvertEvmToCoin) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgConvertEvmToCoin) ProtoMessage() {} + +// Deprecated: Use MsgConvertEvmToCoin.ProtoReflect.Descriptor instead. +func (*MsgConvertEvmToCoin) Descriptor() ([]byte, []int) { + return file_eth_evm_v1_tx_proto_rawDescGZIP(), []int{12} +} + +func (x *MsgConvertEvmToCoin) GetSender() string { + if x != nil { + return x.Sender + } + return "" +} + +func (x *MsgConvertEvmToCoin) GetErc20Addr() string { + if x != nil { + return x.Erc20Addr + } + return "" +} + +func (x *MsgConvertEvmToCoin) GetAmount() string { + if x != nil { + return x.Amount + } + return "" +} + +func (x *MsgConvertEvmToCoin) GetToAddr() string { + if x != nil { + return x.ToAddr + } + return "" +} + +type MsgConvertEvmToCoinResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *MsgConvertEvmToCoinResponse) Reset() { + *x = MsgConvertEvmToCoinResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_eth_evm_v1_tx_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MsgConvertEvmToCoinResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MsgConvertEvmToCoinResponse) ProtoMessage() {} + +// Deprecated: Use MsgConvertEvmToCoinResponse.ProtoReflect.Descriptor instead. +func (*MsgConvertEvmToCoinResponse) Descriptor() ([]byte, []int) { + return file_eth_evm_v1_tx_proto_rawDescGZIP(), []int{13} +} + var File_eth_evm_v1_tx_proto protoreflect.FileDescriptor var file_eth_evm_v1_tx_proto_rawDesc = []byte{ 0x0a, 0x13, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, - 0x31, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, - 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x63, 0x6f, 0x73, 0x6d, - 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, + 0x31, 0x1a, 0x1e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x17, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, + 0x2f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x67, 0x6f, 0x67, @@ -8663,40 +9730,61 @@ var file_eth_evm_v1_tx_proto_rawDesc = []byte{ 0x61, 0x6e, 0x6b, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x22, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6b, 0x43, 0x6f, 0x69, 0x6e, 0x22, 0x1d, 0x0a, 0x1b, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0xfd, 0x02, 0x0a, 0x03, 0x4d, 0x73, 0x67, 0x12, 0x6e, 0x0a, 0x0a, 0x45, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x12, 0x19, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, - 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x54, 0x78, 0x1a, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, - 0x2e, 0x4d, 0x73, 0x67, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x1a, - 0x2f, 0x6e, 0x69, 0x62, 0x69, 0x72, 0x75, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5f, 0x74, 0x78, 0x12, 0x50, 0x0a, 0x0c, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x1b, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, + 0x73, 0x65, 0x22, 0xd4, 0x01, 0x0a, 0x13, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, + 0x74, 0x45, 0x76, 0x6d, 0x54, 0x6f, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x12, 0x55, 0x0a, 0x0a, 0x65, 0x72, 0x63, 0x32, 0x30, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0xc8, 0xde, 0x1f, 0x00, 0xda, 0xde, 0x1f, 0x2e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4e, 0x69, 0x62, 0x69, 0x72, + 0x75, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x6e, 0x69, 0x62, 0x69, 0x72, 0x75, 0x2f, 0x76, 0x32, + 0x2f, 0x65, 0x74, 0x68, 0x2e, 0x45, 0x49, 0x50, 0x35, 0x35, 0x41, 0x64, 0x64, 0x72, 0x52, 0x09, + 0x65, 0x72, 0x63, 0x32, 0x30, 0x41, 0x64, 0x64, 0x72, 0x12, 0x35, 0x0a, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1d, 0xc8, 0xde, 0x1f, 0x00, 0xda, + 0xde, 0x1f, 0x15, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, + 0x6d, 0x61, 0x74, 0x68, 0x2e, 0x49, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x22, 0x1d, 0x0a, 0x1b, 0x4d, 0x73, 0x67, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x76, 0x6d, 0x54, 0x6f, 0x43, 0x6f, 0x69, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xdb, 0x03, 0x0a, 0x03, 0x4d, 0x73, 0x67, + 0x12, 0x6e, 0x0a, 0x0a, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x12, 0x19, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x45, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x54, 0x78, 0x1a, 0x21, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x54, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x1c, 0x22, 0x1a, 0x2f, 0x6e, 0x69, 0x62, 0x69, 0x72, 0x75, 0x2f, 0x65, 0x76, + 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5f, 0x74, 0x78, + 0x12, 0x50, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x1b, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, + 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x23, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x10, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0x25, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x46, 0x75, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x43, - 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, 0x12, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, - 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, - 0x43, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, 0x1a, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, - 0x74, 0x43, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x42, 0x86, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, - 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x21, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, - 0x70, 0x69, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x65, 0x76, - 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x45, 0x58, 0xaa, 0x02, 0x0a, 0x45, 0x74, 0x68, 0x2e, - 0x45, 0x76, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, 0x6d, - 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, 0x6d, 0x5c, 0x56, 0x31, - 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x45, - 0x74, 0x68, 0x3a, 0x3a, 0x45, 0x76, 0x6d, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, 0x1a, + 0x27, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, + 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x43, 0x6f, 0x69, 0x6e, 0x54, 0x6f, 0x45, 0x76, 0x6d, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x74, 0x45, 0x76, 0x6d, 0x54, 0x6f, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x1f, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x6f, 0x6e, + 0x76, 0x65, 0x72, 0x74, 0x45, 0x76, 0x6d, 0x54, 0x6f, 0x43, 0x6f, 0x69, 0x6e, 0x1a, 0x27, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x73, 0x67, 0x43, 0x6f, + 0x6e, 0x76, 0x65, 0x72, 0x74, 0x45, 0x76, 0x6d, 0x54, 0x6f, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x86, 0x01, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x65, 0x76, 0x6d, 0x2e, 0x76, 0x31, 0x42, 0x07, 0x54, 0x78, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x21, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, + 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x65, 0x76, 0x6d, 0x2f, 0x76, + 0x31, 0x3b, 0x65, 0x76, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x45, 0x45, 0x58, 0xaa, 0x02, 0x0a, + 0x45, 0x74, 0x68, 0x2e, 0x45, 0x76, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0a, 0x45, 0x74, 0x68, + 0x5c, 0x45, 0x76, 0x6d, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x16, 0x45, 0x74, 0x68, 0x5c, 0x45, 0x76, + 0x6d, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x0c, 0x45, 0x74, 0x68, 0x3a, 0x3a, 0x45, 0x76, 0x6d, 0x3a, 0x3a, 0x56, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -8711,7 +9799,7 @@ func file_eth_evm_v1_tx_proto_rawDescGZIP() []byte { return file_eth_evm_v1_tx_proto_rawDescData } -var file_eth_evm_v1_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_eth_evm_v1_tx_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_eth_evm_v1_tx_proto_goTypes = []interface{}{ (*MsgEthereumTx)(nil), // 0: eth.evm.v1.MsgEthereumTx (*LegacyTx)(nil), // 1: eth.evm.v1.LegacyTx @@ -8725,31 +9813,35 @@ var file_eth_evm_v1_tx_proto_goTypes = []interface{}{ (*MsgCreateFunTokenResponse)(nil), // 9: eth.evm.v1.MsgCreateFunTokenResponse (*MsgConvertCoinToEvm)(nil), // 10: eth.evm.v1.MsgConvertCoinToEvm (*MsgConvertCoinToEvmResponse)(nil), // 11: eth.evm.v1.MsgConvertCoinToEvmResponse - (*anypb.Any)(nil), // 12: google.protobuf.Any - (*AccessTuple)(nil), // 13: eth.evm.v1.AccessTuple - (*Log)(nil), // 14: eth.evm.v1.Log - (*Params)(nil), // 15: eth.evm.v1.Params - (*FunToken)(nil), // 16: eth.evm.v1.FunToken - (*v1beta1.Coin)(nil), // 17: cosmos.base.v1beta1.Coin + (*MsgConvertEvmToCoin)(nil), // 12: eth.evm.v1.MsgConvertEvmToCoin + (*MsgConvertEvmToCoinResponse)(nil), // 13: eth.evm.v1.MsgConvertEvmToCoinResponse + (*anypb.Any)(nil), // 14: google.protobuf.Any + (*AccessTuple)(nil), // 15: eth.evm.v1.AccessTuple + (*Log)(nil), // 16: eth.evm.v1.Log + (*Params)(nil), // 17: eth.evm.v1.Params + (*FunToken)(nil), // 18: eth.evm.v1.FunToken + (*v1beta1.Coin)(nil), // 19: cosmos.base.v1beta1.Coin } var file_eth_evm_v1_tx_proto_depIdxs = []int32{ - 12, // 0: eth.evm.v1.MsgEthereumTx.data:type_name -> google.protobuf.Any - 13, // 1: eth.evm.v1.AccessListTx.accesses:type_name -> eth.evm.v1.AccessTuple - 13, // 2: eth.evm.v1.DynamicFeeTx.accesses:type_name -> eth.evm.v1.AccessTuple - 14, // 3: eth.evm.v1.MsgEthereumTxResponse.logs:type_name -> eth.evm.v1.Log - 15, // 4: eth.evm.v1.MsgUpdateParams.params:type_name -> eth.evm.v1.Params - 16, // 5: eth.evm.v1.MsgCreateFunTokenResponse.funtoken_mapping:type_name -> eth.evm.v1.FunToken - 17, // 6: eth.evm.v1.MsgConvertCoinToEvm.bank_coin:type_name -> cosmos.base.v1beta1.Coin + 14, // 0: eth.evm.v1.MsgEthereumTx.data:type_name -> google.protobuf.Any + 15, // 1: eth.evm.v1.AccessListTx.accesses:type_name -> eth.evm.v1.AccessTuple + 15, // 2: eth.evm.v1.DynamicFeeTx.accesses:type_name -> eth.evm.v1.AccessTuple + 16, // 3: eth.evm.v1.MsgEthereumTxResponse.logs:type_name -> eth.evm.v1.Log + 17, // 4: eth.evm.v1.MsgUpdateParams.params:type_name -> eth.evm.v1.Params + 18, // 5: eth.evm.v1.MsgCreateFunTokenResponse.funtoken_mapping:type_name -> eth.evm.v1.FunToken + 19, // 6: eth.evm.v1.MsgConvertCoinToEvm.bank_coin:type_name -> cosmos.base.v1beta1.Coin 0, // 7: eth.evm.v1.Msg.EthereumTx:input_type -> eth.evm.v1.MsgEthereumTx 6, // 8: eth.evm.v1.Msg.UpdateParams:input_type -> eth.evm.v1.MsgUpdateParams 8, // 9: eth.evm.v1.Msg.CreateFunToken:input_type -> eth.evm.v1.MsgCreateFunToken 10, // 10: eth.evm.v1.Msg.ConvertCoinToEvm:input_type -> eth.evm.v1.MsgConvertCoinToEvm - 5, // 11: eth.evm.v1.Msg.EthereumTx:output_type -> eth.evm.v1.MsgEthereumTxResponse - 7, // 12: eth.evm.v1.Msg.UpdateParams:output_type -> eth.evm.v1.MsgUpdateParamsResponse - 9, // 13: eth.evm.v1.Msg.CreateFunToken:output_type -> eth.evm.v1.MsgCreateFunTokenResponse - 11, // 14: eth.evm.v1.Msg.ConvertCoinToEvm:output_type -> eth.evm.v1.MsgConvertCoinToEvmResponse - 11, // [11:15] is the sub-list for method output_type - 7, // [7:11] is the sub-list for method input_type + 12, // 11: eth.evm.v1.Msg.ConvertEvmToCoin:input_type -> eth.evm.v1.MsgConvertEvmToCoin + 5, // 12: eth.evm.v1.Msg.EthereumTx:output_type -> eth.evm.v1.MsgEthereumTxResponse + 7, // 13: eth.evm.v1.Msg.UpdateParams:output_type -> eth.evm.v1.MsgUpdateParamsResponse + 9, // 14: eth.evm.v1.Msg.CreateFunToken:output_type -> eth.evm.v1.MsgCreateFunTokenResponse + 11, // 15: eth.evm.v1.Msg.ConvertCoinToEvm:output_type -> eth.evm.v1.MsgConvertCoinToEvmResponse + 13, // 16: eth.evm.v1.Msg.ConvertEvmToCoin:output_type -> eth.evm.v1.MsgConvertEvmToCoinResponse + 12, // [12:17] is the sub-list for method output_type + 7, // [7:12] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name @@ -8906,6 +9998,30 @@ func file_eth_evm_v1_tx_proto_init() { return nil } } + file_eth_evm_v1_tx_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgConvertEvmToCoin); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_eth_evm_v1_tx_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MsgConvertEvmToCoinResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -8913,7 +10029,7 @@ func file_eth_evm_v1_tx_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_eth_evm_v1_tx_proto_rawDesc, NumEnums: 0, - NumMessages: 12, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/api/eth/evm/v1/tx_grpc.pb.go b/api/eth/evm/v1/tx_grpc.pb.go index 1b35e22230..7306623c70 100644 --- a/api/eth/evm/v1/tx_grpc.pb.go +++ b/api/eth/evm/v1/tx_grpc.pb.go @@ -36,6 +36,9 @@ type MsgClient interface { // given recipient address ("to_eth_addr") in the corresponding ERC20 // representation. ConvertCoinToEvm(ctx context.Context, in *MsgConvertCoinToEvm, opts ...grpc.CallOption) (*MsgConvertCoinToEvmResponse, error) + // ConvertEvmToCoin: Sends an ERC20 token with a valid "FunToken" mapping to the + // given recipient address as a bank coin. + ConvertEvmToCoin(ctx context.Context, in *MsgConvertEvmToCoin, opts ...grpc.CallOption) (*MsgConvertEvmToCoinResponse, error) } type msgClient struct { @@ -82,6 +85,15 @@ func (c *msgClient) ConvertCoinToEvm(ctx context.Context, in *MsgConvertCoinToEv return out, nil } +func (c *msgClient) ConvertEvmToCoin(ctx context.Context, in *MsgConvertEvmToCoin, opts ...grpc.CallOption) (*MsgConvertEvmToCoinResponse, error) { + out := new(MsgConvertEvmToCoinResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/ConvertEvmToCoin", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. // All implementations must embed UnimplementedMsgServer // for forward compatibility @@ -100,6 +112,9 @@ type MsgServer interface { // given recipient address ("to_eth_addr") in the corresponding ERC20 // representation. ConvertCoinToEvm(context.Context, *MsgConvertCoinToEvm) (*MsgConvertCoinToEvmResponse, error) + // ConvertEvmToCoin: Sends an ERC20 token with a valid "FunToken" mapping to the + // given recipient address as a bank coin. + ConvertEvmToCoin(context.Context, *MsgConvertEvmToCoin) (*MsgConvertEvmToCoinResponse, error) mustEmbedUnimplementedMsgServer() } @@ -119,6 +134,9 @@ func (UnimplementedMsgServer) CreateFunToken(context.Context, *MsgCreateFunToken func (UnimplementedMsgServer) ConvertCoinToEvm(context.Context, *MsgConvertCoinToEvm) (*MsgConvertCoinToEvmResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ConvertCoinToEvm not implemented") } +func (UnimplementedMsgServer) ConvertEvmToCoin(context.Context, *MsgConvertEvmToCoin) (*MsgConvertEvmToCoinResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConvertEvmToCoin not implemented") +} func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {} // UnsafeMsgServer may be embedded to opt out of forward compatibility for this service. @@ -204,6 +222,24 @@ func _Msg_ConvertCoinToEvm_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Msg_ConvertEvmToCoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConvertEvmToCoin) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConvertEvmToCoin(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/ConvertEvmToCoin", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConvertEvmToCoin(ctx, req.(*MsgConvertEvmToCoin)) + } + return interceptor(ctx, in, info, handler) +} + // Msg_ServiceDesc is the grpc.ServiceDesc for Msg service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -227,6 +263,10 @@ var Msg_ServiceDesc = grpc.ServiceDesc{ MethodName: "ConvertCoinToEvm", Handler: _Msg_ConvertCoinToEvm_Handler, }, + { + MethodName: "ConvertEvmToCoin", + Handler: _Msg_ConvertEvmToCoin_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "eth/evm/v1/tx.proto", diff --git a/app/appconst/appconst.go b/app/appconst/appconst.go index 997baf06d3..7c4f46a224 100644 --- a/app/appconst/appconst.go +++ b/app/appconst/appconst.go @@ -10,6 +10,8 @@ import ( "runtime" "strings" + gethcommon "github.com/ethereum/go-ethereum/common" + db "github.com/cometbft/cometbft-db" "github.com/cosmos/cosmos-sdk/version" ) @@ -25,6 +27,14 @@ const ( var ( DefaultDBBackend db.BackendType = db.PebbleDBBackend HavePebbleDBBuildTag bool + + // MAINNET_WNIBI_ADDR is the (real) hex address of WNIBI on mainnet. NIBI acts as + // "ether" in the Nibiru EVM state. WNIBI is the Nibiru equivalent of WETH on + // Ethereum. + MAINNET_WNIBI_ADDR = gethcommon.HexToAddress("0x0CaCF669f8446BeCA826913a3c6B96aCD4b02a97") + + // MAINNET_STNIBI_ADDR is the (real) hex address of stNIBI on mainnet. + MAINNET_STNIBI_ADDR = gethcommon.HexToAddress("0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b") ) // RuntimeVersion returns a formatted string with versioning and build metadata, diff --git a/app/upgrades.go b/app/upgrades.go index f4e365c340..88bffdddfd 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -30,6 +30,7 @@ import ( "github.com/NibiruChain/nibiru/v2/app/upgrades/v2_4_0" "github.com/NibiruChain/nibiru/v2/app/upgrades/v2_5_0" "github.com/NibiruChain/nibiru/v2/app/upgrades/v2_6_0" + "github.com/NibiruChain/nibiru/v2/app/upgrades/v2_7_0" ) var Upgrades = []upgrades.Upgrade{ @@ -48,6 +49,7 @@ var Upgrades = []upgrades.Upgrade{ v2_4_0.Upgrade, v2_5_0.Upgrade, v2_6_0.Upgrade, + v2_7_0.Upgrade, } func (app *NibiruApp) setupUpgrades() { diff --git a/app/upgrades/v2_5_0/v2_5_0.go b/app/upgrades/v2_5_0/v2_5_0.go index 0f2c8b177c..e57deea395 100644 --- a/app/upgrades/v2_5_0/v2_5_0.go +++ b/app/upgrades/v2_5_0/v2_5_0.go @@ -17,6 +17,7 @@ import ( gethcore "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/NibiruChain/nibiru/v2/app/appconst" "github.com/NibiruChain/nibiru/v2/app/keepers" "github.com/NibiruChain/nibiru/v2/app/upgrades" "github.com/NibiruChain/nibiru/v2/x/evm" @@ -40,7 +41,7 @@ var Upgrade = upgrades.Upgrade{ plan upgradetypes.Plan, fromVM module.VersionMap, ) (module.VersionMap, error) { - err := UpgradeStNibiContractOnMainnet(nibiru, ctx, MAINNET_STNIBI_ADDR) + err := UpgradeStNibiContractOnMainnet(nibiru, ctx, appconst.MAINNET_STNIBI_ADDR) if err != nil { panic(fmt.Errorf("v2.5.0 upgrade failure: %w", err)) } @@ -51,9 +52,6 @@ var Upgrade = upgrades.Upgrade{ StoreUpgrades: storetypes.StoreUpgrades{}, } -// MAINNET_STNIBI_ADDR is the (real) hex address of stNIBI on mainnet. -var MAINNET_STNIBI_ADDR = gethcommon.HexToAddress("0xcA0a9Fb5FBF692fa12fD13c0A900EC56Bb3f0a7b") - func UpgradeStNibiContractOnMainnet( keepers *keepers.PublicKeepers, ctx sdk.Context, @@ -160,7 +158,7 @@ func UpgradeStNibiContractOnMainnet( evmResp, err := keepers.EvmKeeper.CallContractWithInput( ctx, evmObj, evmMsg.From, nil, true /*commit*/, contractInput, - evmkeeper.Erc20GasLimitDeploy, + evmkeeper.Erc20GasLimitDeploy, nil, ) if err != nil { return fmt.Errorf("failed to deploy ERC20 contract: %w", err) diff --git a/app/upgrades/v2_5_0/v2_5_0_test.go b/app/upgrades/v2_5_0/v2_5_0_test.go index 640f6211fd..0c53b57e9f 100644 --- a/app/upgrades/v2_5_0/v2_5_0_test.go +++ b/app/upgrades/v2_5_0/v2_5_0_test.go @@ -179,6 +179,7 @@ func (s *Suite) TestUpgrade() { false, // commit input, evmkeeper.Erc20GasLimitQuery, + nil, ) s.Require().NoError(err) diff --git a/app/upgrades/v2_6_0/constants.go b/app/upgrades/v2_6_0/v2_6_0.go similarity index 100% rename from app/upgrades/v2_6_0/constants.go rename to app/upgrades/v2_6_0/v2_6_0.go diff --git a/app/upgrades/v2_7_0/v2_7_0.go b/app/upgrades/v2_7_0/v2_7_0.go new file mode 100644 index 0000000000..2715f7c3e6 --- /dev/null +++ b/app/upgrades/v2_7_0/v2_7_0.go @@ -0,0 +1,59 @@ +package v2_7_0 + +import ( + "fmt" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + clientkeeper "github.com/cosmos/ibc-go/v7/modules/core/02-client/keeper" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/app/keepers" + "github.com/NibiruChain/nibiru/v2/app/upgrades" + "github.com/NibiruChain/nibiru/v2/eth" +) + +const UpgradeName = "v2.7.0" + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: func( + mm *module.Manager, + cfg module.Configurator, + nibiru *keepers.PublicKeepers, + clientKeeper clientkeeper.Keeper, + ) upgradetypes.UpgradeHandler { + return func( + ctx sdk.Context, + plan upgradetypes.Plan, + fromVM module.VersionMap, + ) (module.VersionMap, error) { + err := AddWnibiToNibiruEvm(nibiru, ctx) + if err != nil { + panic(fmt.Errorf("v2.7.0 upgrade failure: %w", err)) + } + + return mm.RunMigrations(ctx, cfg, fromVM) + } + }, + StoreUpgrades: storetypes.StoreUpgrades{}, +} + +// AddWnibiToNibiruEvm adds the canonical WNIBI contract address to the EVM +// module parameters. +func AddWnibiToNibiruEvm( + keepers *keepers.PublicKeepers, + ctx sdk.Context, +) error { + wnibiAddrMainnet := appconst.MAINNET_WNIBI_ADDR + evmParams := keepers.EvmKeeper.GetParams(ctx) + evmParams.CanonicalWnibi = eth.EIP55Addr{ + Address: wnibiAddrMainnet, + } + + err := keepers.EvmKeeper.SetParams(ctx, evmParams) + + return err +} diff --git a/eth/eip55.go b/eth/eip55.go index ea277ebb13..84b27cd877 100644 --- a/eth/eip55.go +++ b/eth/eip55.go @@ -109,3 +109,7 @@ func (h *EIP55Addr) UnmarshalJSON(bz []byte) error { func (h EIP55Addr) Size() int { return len(h.Bytes()) } + +func (h EIP55Addr) Equal(other EIP55Addr) bool { + return h.Address == other.Address +} diff --git a/justfile b/justfile index b4eee0eac2..261ed31982 100644 --- a/justfile +++ b/justfile @@ -53,14 +53,13 @@ gen-proto-rs: lint: #!/usr/bin/env bash - source contrib/bashlib.sh - if ! which_ok golangci-lint; then - log_info "Installing golangci-lint" - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.64.8 - fi - - golangci-lint run --allow-parallel-runners --fix - + echo "Running golangci-lint with docker!" + docker run --rm \ + -v "$(pwd)":/app \ + -v ~/.cache/golangci-lint/v2.1.6:/root/.cache \ + -w /app \ + golangci/golangci-lint:v2.1.6 \ + golangci-lint run -v --fix 2>&1 # Runs a Nibiru local network. Ex: "just localnet", "just localnet --features featureA" localnet *PASS_FLAGS: diff --git a/proto/eth/evm/v1/events.proto b/proto/eth/evm/v1/events.proto index a99b98dddd..f19204f812 100644 --- a/proto/eth/evm/v1/events.proto +++ b/proto/eth/evm/v1/events.proto @@ -3,8 +3,8 @@ syntax = "proto3"; package eth.evm.v1; import "cosmos/base/v1beta1/coin.proto"; -import "gogoproto/gogo.proto"; import "eth/evm/v1/evm.proto"; +import "gogoproto/gogo.proto"; option go_package = "github.com/NibiruChain/nibiru/v2/x/evm"; @@ -29,10 +29,11 @@ message EventEthereumTx { // EventTxLog defines the event for an Ethereum transaction log message EventTxLog { // tx_logs is an array of transaction logs - repeated Log logs = 1 [ (gogoproto.nullable) = false ]; + repeated Log logs = 1 [(gogoproto.nullable) = false]; } -// EventBlockBloom defines an Ethereum block bloom filter event +// EventBlockBloom contains the bloom filter for an Ethereum block. +// The bloom filter encodes logs for efficient event filtering. message EventBlockBloom { // bloom is the bloom filter of the block string bloom = 1; @@ -46,7 +47,8 @@ message EventFunTokenCreated { bool is_made_from_coin = 4; } -// ConvertCoinToEvm defines sending fun token to erc20 event. +// EventConvertCoinToEvm is an event emitted when converting Bank Coins into +// ERC20 tokens with the "eth.evm.v1.MsgConvertCoinToEvm" transaction message. message EventConvertCoinToEvm { string sender = 1; string erc20_contract_address = 2; @@ -75,3 +77,16 @@ message EventContractExecuted { string sender = 1; string contract_addr = 2; } + +// EventConvertEvmToCoin is an event emitted when converting ERC20 tokens to Bank +// Coins with the "eth.evm.v1.MsgConvertEvmToCoin" transaction message. +message EventConvertEvmToCoin { + string sender = 1; + string erc20_contract_address = 2; + string to_address = 3; + cosmos.base.v1beta1.Coin bank_coin = 4 [ + (gogoproto.moretags) = "yaml:\"bank_coin\"", + (gogoproto.nullable) = false + ]; + string sender_eth_addr = 6; +} diff --git a/proto/eth/evm/v1/evm.proto b/proto/eth/evm/v1/evm.proto index db785891a9..0d086acb39 100644 --- a/proto/eth/evm/v1/evm.proto +++ b/proto/eth/evm/v1/evm.proto @@ -50,7 +50,7 @@ message Params { // All precompiles present according to the VM are active. reserved 7; // evm_channels is the list of channel identifiers from EVM compatible chains - repeated string evm_channels = 8 [ (gogoproto.customname) = "EVMChannels" ]; + repeated string evm_channels = 8 [(gogoproto.customname) = "EVMChannels"]; // Fee deducted and burned when calling "CreateFunToken" in units of // "evm_denom". @@ -58,6 +58,12 @@ message Params { (gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false ]; + + // Hexadecimal address of the canonical WNIBI contract on Nibiru mainnet + string canonical_wnibi = 10 [ + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/eth.EIP55Addr", + (gogoproto.nullable) = false + ]; } // State represents a single Storage key value pair item. @@ -83,15 +89,15 @@ message Log { bytes data = 3; // block_number of the block in which the transaction was included - uint64 block_number = 4 [ (gogoproto.jsontag) = "blockNumber" ]; + uint64 block_number = 4 [(gogoproto.jsontag) = "blockNumber"]; // tx_hash is the transaction hash - string tx_hash = 5 [ (gogoproto.jsontag) = "transactionHash" ]; + string tx_hash = 5 [(gogoproto.jsontag) = "transactionHash"]; // tx_index of the transaction in the block - uint64 tx_index = 6 [ (gogoproto.jsontag) = "transactionIndex" ]; + uint64 tx_index = 6 [(gogoproto.jsontag) = "transactionIndex"]; // block_hash of the block in which the transaction was included - string block_hash = 7 [ (gogoproto.jsontag) = "blockHash" ]; + string block_hash = 7 [(gogoproto.jsontag) = "blockHash"]; // index of the log in the block - uint64 index = 8 [ (gogoproto.jsontag) = "logIndex" ]; + uint64 index = 8 [(gogoproto.jsontag) = "logIndex"]; // removed is true if this log was reverted due to a chain // reorganisation. You must pay attention to this field if you receive logs @@ -106,13 +112,13 @@ message AccessTuple { // address is a hex formatted ethereum address string address = 1; // storage_keys are hex formatted hashes of the storage keys - repeated string storage_keys = 2 [ (gogoproto.jsontag) = "storageKeys" ]; + repeated string storage_keys = 2 [(gogoproto.jsontag) = "storageKeys"]; } // TracerConfig stores additional tracer args. For geth it's only one attr: // onlyTopCall message TracerConfig { - bool only_top_call = 1 [ (gogoproto.jsontag) = "onlyTopCall" ]; + bool only_top_call = 1 [(gogoproto.jsontag) = "onlyTopCall"]; } // TraceConfig holds extra parameters to trace functions. @@ -130,9 +136,9 @@ message TraceConfig { // reexec defines the number of blocks the tracer is willing to go back uint64 reexec = 3; // disable_stack switches stack capture - bool disable_stack = 5 [ (gogoproto.jsontag) = "disableStack" ]; + bool disable_stack = 5 [(gogoproto.jsontag) = "disableStack"]; // disable_storage switches storage capture - bool disable_storage = 6 [ (gogoproto.jsontag) = "disableStorage" ]; + bool disable_storage = 6 [(gogoproto.jsontag) = "disableStorage"]; // debug can be used to print output during capture end bool debug = 8; // limit defines the maximum length of output, but zero means unlimited @@ -140,9 +146,9 @@ message TraceConfig { // DEPRECATED: chain_config reserved 10; // enable_memory switches memory capture - bool enable_memory = 11 [ (gogoproto.jsontag) = "enableMemory" ]; + bool enable_memory = 11 [(gogoproto.jsontag) = "enableMemory"]; // enable_return_data switches the capture of return data - bool enable_return_data = 12 [ (gogoproto.jsontag) = "enableReturnData" ]; + bool enable_return_data = 12 [(gogoproto.jsontag) = "enableReturnData"]; // tracer_config configures the tracer options - TracerConfig tracer_config = 13 [ (gogoproto.jsontag) = "tracerConfig" ]; + TracerConfig tracer_config = 13 [(gogoproto.jsontag) = "tracerConfig"]; } diff --git a/proto/eth/evm/v1/tx.proto b/proto/eth/evm/v1/tx.proto index a1896fe86f..5ec1d26c9c 100644 --- a/proto/eth/evm/v1/tx.proto +++ b/proto/eth/evm/v1/tx.proto @@ -2,8 +2,8 @@ syntax = "proto3"; package eth.evm.v1; -import "cosmos/msg/v1/msg.proto"; import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/msg/v1/msg.proto"; import "cosmos_proto/cosmos.proto"; import "eth/evm/v1/evm.proto"; import "gogoproto/gogo.proto"; @@ -17,7 +17,7 @@ service Msg { // EthereumTx defines a method submitting Ethereum transactions. rpc EthereumTx(MsgEthereumTx) returns (MsgEthereumTxResponse) { option (google.api.http).post = "/nibiru/evm/v1/ethereum_tx"; - }; + } // UpdateParams defined a governance operation for updating the x/evm module // parameters. The authority is hard-coded to the x/gov module account rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); @@ -31,8 +31,11 @@ service Msg { // ConvertCoinToEvm: Sends a coin with a valid "FunToken" mapping to the // given recipient address ("to_eth_addr") in the corresponding ERC20 // representation. - rpc ConvertCoinToEvm(MsgConvertCoinToEvm) - returns (MsgConvertCoinToEvmResponse); + rpc ConvertCoinToEvm(MsgConvertCoinToEvm) returns (MsgConvertCoinToEvmResponse); + + // ConvertEvmToCoin: Sends an ERC20 token with a valid "FunToken" mapping to the + // given recipient address as a bank coin. + rpc ConvertEvmToCoin(MsgConvertEvmToCoin) returns (MsgConvertEvmToCoinResponse); } // MsgEthereumTx encapsulates an Ethereum transaction as an SDK message. @@ -43,9 +46,9 @@ message MsgEthereumTx { google.protobuf.Any data = 1; // size is the encoded storage size of the transaction (DEPRECATED) - double size = 2 [ (gogoproto.jsontag) = "-" ]; + double size = 2 [(gogoproto.jsontag) = "-"]; // hash of the transaction in hex format - string hash = 3 [ (gogoproto.moretags) = "rlp:\"-\"" ]; + string hash = 3 [(gogoproto.moretags) = "rlp:\"-\""]; // from is the ethereum signer address in hex format. This address value is // checked against the address derived from the signature (V, R, S) using the // secp256k1 elliptic curve @@ -66,9 +69,9 @@ message LegacyTx { // nonce corresponds to the account nonce (transaction sequence). uint64 nonce = 1; // gas_price defines the value for each gas unit - string gas_price = 2 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + string gas_price = 2 [(gogoproto.customtype) = "cosmossdk.io/math.Int"]; // gas defines the gas limit defined for the transaction. - uint64 gas = 3 [ (gogoproto.customname) = "GasLimit" ]; + uint64 gas = 3 [(gogoproto.customname) = "GasLimit"]; // to is the hex formatted address of the recipient string to = 4; // value defines the unsigned integer value of the transaction amount. @@ -113,9 +116,9 @@ message AccessListTx { // nonce corresponds to the account nonce (transaction sequence). uint64 nonce = 2; // gas_price defines the value for each gas unit - string gas_price = 3 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + string gas_price = 3 [(gogoproto.customtype) = "cosmossdk.io/math.Int"]; // gas defines the gas limit defined for the transaction. - uint64 gas = 4 [ (gogoproto.customname) = "GasLimit" ]; + uint64 gas = 4 [(gogoproto.customname) = "GasLimit"]; // to is the recipient address in hex format string to = 5; // value defines the unsigned integer value of the transaction amount. @@ -166,11 +169,11 @@ message DynamicFeeTx { // nonce corresponds to the account nonce (transaction sequence). uint64 nonce = 2; // gas_tip_cap defines the max value for the gas tip - string gas_tip_cap = 3 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + string gas_tip_cap = 3 [(gogoproto.customtype) = "cosmossdk.io/math.Int"]; // gas_fee_cap defines the max value for the gas fee - string gas_fee_cap = 4 [ (gogoproto.customtype) = "cosmossdk.io/math.Int" ]; + string gas_fee_cap = 4 [(gogoproto.customtype) = "cosmossdk.io/math.Int"]; // gas defines the gas limit defined for the transaction. - uint64 gas = 5 [ (gogoproto.customname) = "GasLimit" ]; + uint64 gas = 5 [(gogoproto.customname) = "GasLimit"]; // to is the hex formatted address of the recipient string to = 6; // value defines the the transaction amount. @@ -219,7 +222,7 @@ message MsgEthereumTxResponse { string hash = 1; // logs contains the transaction hash and the proto-compatible ethereum // logs. - repeated Log logs = 2 [ (gogoproto.nullable) = false ]; + repeated Log logs = 2 [(gogoproto.nullable) = false]; // ret is the returned data from evm function (result or data supplied with // revert opcode) bytes ret = 3; @@ -234,11 +237,11 @@ message MsgUpdateParams { option (cosmos.msg.v1.signer) = "authority"; // authority is the address of the governance account. - string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // params defines the x/evm parameters to update. // NOTE: All parameters must be supplied. - eth.evm.v1.Params params = 2 [ (gogoproto.nullable) = false ]; + eth.evm.v1.Params params = 2 [(gogoproto.nullable) = false]; } // MsgUpdateParamsResponse defines the response structure for executing a @@ -264,7 +267,7 @@ message MsgCreateFunToken { message MsgCreateFunTokenResponse { // Fungible token mapping corresponding to ERC20 tokens. - eth.evm.v1.FunToken funtoken_mapping = 1 [ (gogoproto.nullable) = false ]; + eth.evm.v1.FunToken funtoken_mapping = 1 [(gogoproto.nullable) = false]; } // MsgConvertCoinToEvm: Arguments to send a Bank Coin to ERC-20 representation @@ -285,3 +288,34 @@ message MsgConvertCoinToEvm { ]; } message MsgConvertCoinToEvmResponse {} + +// MsgConvertEvmToCoin: Arguments to send an ERC20 token to bank coin representation +message MsgConvertEvmToCoin { + // Sender: "nibi"-prefixed Bech32 address for the signer of the transaction. + // This is also the address whose ERC20 balance will be deducted. + string sender = 1; + + // Hexadecimal address of the ERC20 token to be converted and sent + string erc20_addr = 2 [ + (gogoproto.customtype) = "github.com/NibiruChain/nibiru/v2/eth.EIP55Addr", + (gogoproto.nullable) = false + ]; + + // Amount of ERC20 tokens to convert + string amount = 3 [ + (gogoproto.customtype) = "cosmossdk.io/math.Int", + (gogoproto.nullable) = false + ]; + + // Recipient address for the bank coins in Ethereum hexadecimal or + // nibi-prefixed Bech32 format. + // + // Currently, accounts corresponding to Wasm contracts cannot hold ERC20 tokens + // because the function that maps between Bech32 and Eth hex addresses is not + // bijective for these types of accounts. + // + // See [bug(evm): nibid q evm account is not symmetric for wasm + // addresses](https://github.com/NibiruChain/nibiru/issues/2138) + string to_addr = 4; +} +message MsgConvertEvmToCoinResponse {} diff --git a/x/common/testutil/genesis/genesis.go b/x/common/testutil/genesis/genesis.go index b604ca9acb..3d82cc2d0a 100644 --- a/x/common/testutil/genesis/genesis.go +++ b/x/common/testutil/genesis/genesis.go @@ -1,35 +1,87 @@ package genesis import ( + "fmt" "time" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + auth "github.com/cosmos/cosmos-sdk/x/auth/types" gov "github.com/cosmos/cosmos-sdk/x/gov/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1" "github.com/NibiruChain/nibiru/v2/app" "github.com/NibiruChain/nibiru/v2/x/common/denoms" "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" ) -/* - NewTestGenesisState returns 'NewGenesisState' using the default - -genesis as input. The blockchain genesis state is represented as a map from module -identifier strings to raw json messages. -*/ +// NewTestGenesisState returns [app.GenesisState] using the default genesis as input. +// The blockchain genesis state is represented as a map from module identifier +// strings to raw json messages. func NewTestGenesisState(appCodec codec.Codec) app.GenesisState { genState := app.ModuleBasics.DefaultGenesis(appCodec) // Set short voting period to allow fast gov proposals in tests - var govGenState govtypes.GenesisState - appCodec.MustUnmarshalJSON(genState[gov.ModuleName], &govGenState) - *govGenState.Params.VotingPeriod = 20 * time.Second - govGenState.Params.MinDeposit = sdk.NewCoins(sdk.NewInt64Coin(denoms.NIBI, 1e6)) // min deposit of 1 NIBI - genState[gov.ModuleName] = appCodec.MustMarshalJSON(&govGenState) + { + var govGenState govtypes.GenesisState + appCodec.MustUnmarshalJSON(genState[gov.ModuleName], &govGenState) + *govGenState.Params.VotingPeriod = 20 * time.Second + govGenState.Params.MinDeposit = sdk.NewCoins(sdk.NewInt64Coin(denoms.NIBI, 1e6)) // min deposit of 1 NIBI + jsonBz, err := appCodec.MarshalJSON(&govGenState) + if err != nil { + panic(fmt.Errorf("failed to marshal gov genesis: %w", err)) + } + genState[gov.ModuleName] = jsonBz + } testapp.SetDefaultSudoGenesis(genState) + // EVM Genesis - Set WNIBI to mimic mainnet (Nibiru v2.7.0) + { + var evmGenState evm.GenesisState + moduleName := evm.ModuleName + + err := appCodec.UnmarshalJSON(genState[moduleName], &evmGenState) + if err != nil { + panic(fmt.Errorf("failed to unmarshal genesis from default: moduleName %s: %w", moduleName, err)) + } + evmGenState.Accounts = append(evmGenState.Accounts, evmtest.WNIBI_GENESIS_EVM_ACC) + + jsonBz, err := appCodec.MarshalJSON(&evmGenState) + if err != nil { + panic(fmt.Errorf("failed to marshal evm genesis: %w", err)) + } + genState[moduleName] = jsonBz + } + + { + var authGenState auth.GenesisState + moduleName := auth.ModuleName + + err := appCodec.UnmarshalJSON(genState[moduleName], &authGenState) + if err != nil { + panic(fmt.Errorf("failed to unmarshal genesis from default: moduleName %s: %w", moduleName, err)) + } + wnibiAcc := evmtest.WNIBI_GENESIS_AUTH_ACC + nextOpenAccNumber := len(authGenState.Accounts) + wnibiAcc.AccountNumber = uint64(nextOpenAccNumber) + + // error not possible here because of compile-time asserts in + // eth_account.go implementation of eth.EthAccount + protobufAnyAccs, _ := auth.PackAccounts(auth.GenesisAccounts{ + &wnibiAcc, + }) + wnibiAccAsProtobufAny := protobufAnyAccs[0] + authGenState.Accounts = append(authGenState.Accounts, wnibiAccAsProtobufAny) + + jsonBz, err := appCodec.MarshalJSON(&authGenState) + if err != nil { + panic(fmt.Errorf("failed to marshal auth genesis: %w", err)) + } + genState[moduleName] = jsonBz + } + return genState } diff --git a/x/evm/cli/cli_test.go b/x/evm/cli/cli_test.go index 99b8b684e5..c73243a685 100644 --- a/x/evm/cli/cli_test.go +++ b/x/evm/cli/cli_test.go @@ -49,6 +49,80 @@ func (s *Suite) TestCmdConvertCoinToEvm() { } } +func (s *Suite) TestCmdConvertEvmToCoin() { + testCases := []TestCase{ + { + name: "happy: convert-evm-to-coin", + args: []string{ + "convert-evm-to-coin", + dummyFuntoken.Erc20Addr.String(), + "123456", + s.testAcc.Address.String(), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "", + }, + { + name: "sad: invalid erc20 address", + args: []string{ + "convert-evm-to-coin", + "not-an-address", + "123456", + s.testAcc.Address.String(), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "invalid ERC20 contract address", + }, + { + name: "sad: invalid amount", + args: []string{ + "convert-evm-to-coin", + dummyFuntoken.Erc20Addr.String(), + "not-a-number", + s.testAcc.Address.String(), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "invalid amount", + }, + { + name: "sad: invalid recipient address", + args: []string{ + "convert-evm-to-coin", + dummyFuntoken.Erc20Addr.String(), + "123456", + "invalid-address", + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "invalid recipient address", + }, + { + name: "sad: missing args", + args: []string{ + "convert-evm-to-coin", + dummyFuntoken.Erc20Addr.String(), + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "accepts 3 arg(s), received 1", + }, + { + name: "sad: too many args", + args: []string{ + "convert-evm-to-coin", + dummyFuntoken.Erc20Addr.String(), + "123456", + s.testAcc.Address.String(), + "extra-arg", + }, + extraArgs: []string{fmt.Sprintf("--from=%s", s.testAcc.Address)}, + wantErr: "accepts 3 arg(s), received 4", + }, + } + + for _, tc := range testCases { + tc.RunTxCmd(s) + } +} + func (s *Suite) TestCmdCreateFunToken() { testCases := []TestCase{ { diff --git a/x/evm/cli/tx.go b/x/evm/cli/tx.go index dbda5f7fd1..62187334f5 100644 --- a/x/evm/cli/tx.go +++ b/x/evm/cli/tx.go @@ -28,6 +28,7 @@ func GetTxCmd() *cobra.Command { cmds := []*cobra.Command{ CmdCreateFunToken(), CmdConvertCoinToEvm(), + CmdConvertEvmToCoin(), } for _, cmd := range cmds { txCmd.AddCommand(cmd) @@ -123,3 +124,49 @@ func CmdConvertCoinToEvm() *cobra.Command { flags.AddTxFlagsToCmd(cmd) return cmd } + +// CmdConvertEvmToCoin broadcast MsgConvertEvmToCoin +func CmdConvertEvmToCoin() *cobra.Command { + cmd := &cobra.Command{ + Use: "convert-evm-to-coin [erc20_contract_address] [amount] [to_address] [flags]", + Short: `Convert ERC20 tokens to their bank coin representation and send to the [to_address] account`, + Long: heredoc.Doc(` + Convert ERC20 tokens to their bank coin representation. + + Example: + convert-evm-to-coin 0x1234...abcd 100000000 nibi1...xyz --from mykey + `), + Args: cobra.ExactArgs(3), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + erc20Addr, err := eth.NewEIP55AddrFromStr(args[0]) + if err != nil { + return fmt.Errorf("invalid ERC20 contract address: %w", err) + } + + amount, ok := sdk.NewIntFromString(args[1]) + if !ok { + return fmt.Errorf("invalid amount: %s", args[1]) + } + + toAddress := args[2] + if _, err := sdk.AccAddressFromBech32(toAddress); err != nil { + return fmt.Errorf("invalid recipient address: %w", err) + } + + msg := &evm.MsgConvertEvmToCoin{ + Sender: clientCtx.GetFromAddress().String(), + Erc20Addr: erc20Addr, + Amount: amount, + ToAddr: toAddress, + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/evm/codec.go b/x/evm/codec.go index 21fbb91d42..852abf60fa 100644 --- a/x/evm/codec.go +++ b/x/evm/codec.go @@ -36,6 +36,7 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) { &MsgUpdateParams{}, &MsgCreateFunToken{}, &MsgConvertCoinToEvm{}, + &MsgConvertEvmToCoin{}, ) registry.RegisterInterface( "eth.evm.v1.TxData", diff --git a/x/evm/deps.go b/x/evm/deps.go index 2325def185..ccb9c8133d 100644 --- a/x/evm/deps.go +++ b/x/evm/deps.go @@ -36,3 +36,10 @@ type StakingKeeper interface { GetHistoricalInfo(ctx sdk.Context, height int64) (stakingtypes.HistoricalInfo, bool) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator stakingtypes.Validator, found bool) } + +type SudoKeeper interface { + // CheckPermissions Checks if a contract is contained within the set of sudo + // contracts defined in the x/sudo module. These smart contracts are able to + // execute certain permissioned functions. + CheckPermissions(contract sdk.AccAddress, ctx sdk.Context) error +} diff --git a/x/evm/embeds/embeds.go b/x/evm/embeds/embeds.go index 567bf058f1..c972c70cfd 100644 --- a/x/evm/embeds/embeds.go +++ b/x/evm/embeds/embeds.go @@ -25,6 +25,9 @@ var ( funtokenPrecompileJSON []byte //go:embed artifacts/contracts/Wasm.sol/IWasm.json wasmPrecompileJSON []byte + //go:embed artifacts/contracts/WNIBI.sol/WNIBI.json + wnibiContractJSON []byte + //go:embed artifacts/contracts/TestERC20.sol/TestERC20.json testErc20Json []byte //go:embed artifacts/contracts/TestERC20MaliciousName.sol/TestERC20MaliciousName.json @@ -89,6 +92,12 @@ var ( Name: "Oracle.sol", EmbedJSON: oracleContractJSON, } + // SmartContract_Funtoken: Wrapped NIBI contract ERC20. + SmartContract_WNIBI = CompiledEvmContract{ + Name: "WNIBI.sol", + EmbedJSON: wnibiContractJSON, + } + SmartContract_TestERC20 = CompiledEvmContract{ Name: "TestERC20.sol", EmbedJSON: testErc20Json, @@ -184,6 +193,8 @@ func init() { SmartContract_FunToken.MustLoad() SmartContract_Wasm.MustLoad() SmartContract_Oracle.MustLoad() + SmartContract_WNIBI.MustLoad() + SmartContract_TestERC20.MustLoad() SmartContract_TestERC20MaliciousName.MustLoad() SmartContract_TestERC20MaliciousTransfer.MustLoad() diff --git a/x/evm/errors.go b/x/evm/errors.go index c53161e95a..4cbda99815 100644 --- a/x/evm/errors.go +++ b/x/evm/errors.go @@ -50,6 +50,8 @@ var ( // ErrInvalidAccount returns an error if the account is not an EVM compatible account ErrInvalidAccount = sdkioerrors.Register(ModuleName, codeErrInvalidAccount, "account type is not a valid ethereum account") + + ErrCanonicalWnibi = "canonical WNIBI address in state is a not a smart contract" ) // NewRevertError unpacks the revert return bytes and returns a wrapped error diff --git a/x/evm/events.pb.go b/x/evm/events.pb.go index e0efcbf02d..162c337584 100644 --- a/x/evm/events.pb.go +++ b/x/evm/events.pb.go @@ -170,7 +170,8 @@ func (m *EventTxLog) GetLogs() []Log { return nil } -// EventBlockBloom defines an Ethereum block bloom filter event +// EventBlockBloom contains the bloom filter for an Ethereum block. +// The bloom filter encodes logs for efficient event filtering. type EventBlockBloom struct { // bloom is the bloom filter of the block Bloom string `protobuf:"bytes,1,opt,name=bloom,proto3" json:"bloom,omitempty"` @@ -285,7 +286,8 @@ func (m *EventFunTokenCreated) GetIsMadeFromCoin() bool { return false } -// ConvertCoinToEvm defines sending fun token to erc20 event. +// EventConvertCoinToEvm is an event emitted when converting Bank Coins into +// ERC20 tokens with the "eth.evm.v1.MsgConvertCoinToEvm" transaction message. type EventConvertCoinToEvm struct { Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` Erc20ContractAddress string `protobuf:"bytes,2,opt,name=erc20_contract_address,json=erc20ContractAddress,proto3" json:"erc20_contract_address,omitempty"` @@ -521,6 +523,84 @@ func (m *EventContractExecuted) GetContractAddr() string { return "" } +// EventConvertEvmToCoin is an event emitted when converting ERC20 tokens to Bank +// Coins with the "eth.evm.v1.MsgConvertEvmToCoin" transaction message. +type EventConvertEvmToCoin struct { + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + Erc20ContractAddress string `protobuf:"bytes,2,opt,name=erc20_contract_address,json=erc20ContractAddress,proto3" json:"erc20_contract_address,omitempty"` + ToAddress string `protobuf:"bytes,3,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` + BankCoin types.Coin `protobuf:"bytes,4,opt,name=bank_coin,json=bankCoin,proto3" json:"bank_coin" yaml:"bank_coin"` + SenderEthAddr string `protobuf:"bytes,6,opt,name=sender_eth_addr,json=senderEthAddr,proto3" json:"sender_eth_addr,omitempty"` +} + +func (m *EventConvertEvmToCoin) Reset() { *m = EventConvertEvmToCoin{} } +func (m *EventConvertEvmToCoin) String() string { return proto.CompactTextString(m) } +func (*EventConvertEvmToCoin) ProtoMessage() {} +func (*EventConvertEvmToCoin) Descriptor() ([]byte, []int) { + return fileDescriptor_f8bc26b53c788f17, []int{8} +} +func (m *EventConvertEvmToCoin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventConvertEvmToCoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventConvertEvmToCoin.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventConvertEvmToCoin) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventConvertEvmToCoin.Merge(m, src) +} +func (m *EventConvertEvmToCoin) XXX_Size() int { + return m.Size() +} +func (m *EventConvertEvmToCoin) XXX_DiscardUnknown() { + xxx_messageInfo_EventConvertEvmToCoin.DiscardUnknown(m) +} + +var xxx_messageInfo_EventConvertEvmToCoin proto.InternalMessageInfo + +func (m *EventConvertEvmToCoin) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *EventConvertEvmToCoin) GetErc20ContractAddress() string { + if m != nil { + return m.Erc20ContractAddress + } + return "" +} + +func (m *EventConvertEvmToCoin) GetToAddress() string { + if m != nil { + return m.ToAddress + } + return "" +} + +func (m *EventConvertEvmToCoin) GetBankCoin() types.Coin { + if m != nil { + return m.BankCoin + } + return types.Coin{} +} + +func (m *EventConvertEvmToCoin) GetSenderEthAddr() string { + if m != nil { + return m.SenderEthAddr + } + return "" +} + func init() { proto.RegisterType((*EventEthereumTx)(nil), "eth.evm.v1.EventEthereumTx") proto.RegisterType((*EventTxLog)(nil), "eth.evm.v1.EventTxLog") @@ -530,52 +610,55 @@ func init() { proto.RegisterType((*EventTransfer)(nil), "eth.evm.v1.EventTransfer") proto.RegisterType((*EventContractDeployed)(nil), "eth.evm.v1.EventContractDeployed") proto.RegisterType((*EventContractExecuted)(nil), "eth.evm.v1.EventContractExecuted") + proto.RegisterType((*EventConvertEvmToCoin)(nil), "eth.evm.v1.EventConvertEvmToCoin") } func init() { proto.RegisterFile("eth/evm/v1/events.proto", fileDescriptor_f8bc26b53c788f17) } var fileDescriptor_f8bc26b53c788f17 = []byte{ - // 628 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcf, 0x6e, 0xd3, 0x4e, - 0x10, 0x8e, 0xdb, 0xf4, 0x4f, 0xb6, 0xbf, 0xfe, 0x0a, 0x56, 0x28, 0x6e, 0x05, 0x6e, 0x65, 0x24, - 0x68, 0x2f, 0x36, 0x09, 0x48, 0x48, 0x9c, 0x20, 0x69, 0x2a, 0x0e, 0x05, 0xa1, 0x28, 0x5c, 0x90, - 0x90, 0xb5, 0xb6, 0xa7, 0xb6, 0xd5, 0xec, 0x4e, 0xb5, 0xbb, 0xb6, 0xd2, 0xb7, 0xe0, 0x51, 0x78, - 0x06, 0x4e, 0x3d, 0xf6, 0x06, 0xa7, 0x0a, 0xb5, 0x6f, 0xc0, 0x13, 0xa0, 0x5d, 0xbb, 0x4d, 0x03, - 0xea, 0x05, 0x6e, 0x33, 0xdf, 0xfc, 0xd9, 0x99, 0x6f, 0x3e, 0x2d, 0xb9, 0x0f, 0x2a, 0x0b, 0xa0, - 0x64, 0x41, 0xd9, 0x09, 0xa0, 0x04, 0xae, 0xa4, 0x7f, 0x2c, 0x50, 0xa1, 0x4d, 0x40, 0x65, 0x3e, - 0x94, 0xcc, 0x2f, 0x3b, 0x9b, 0x6e, 0x8c, 0x92, 0xa1, 0x0c, 0x22, 0x2a, 0x21, 0x28, 0x3b, 0x11, - 0x28, 0xda, 0x09, 0x62, 0xcc, 0x79, 0x95, 0xbb, 0xd9, 0x4e, 0x31, 0x45, 0x63, 0x06, 0xda, 0xba, - 0x42, 0x67, 0x5a, 0xb3, 0x0a, 0xf5, 0xbe, 0x5a, 0x64, 0x6d, 0xa0, 0x1f, 0x1a, 0xa8, 0x0c, 0x04, - 0x14, 0x6c, 0x34, 0xb1, 0xd7, 0xc9, 0x22, 0x65, 0x58, 0x70, 0xe5, 0x58, 0xdb, 0xd6, 0x4e, 0x6b, - 0x58, 0x7b, 0xf6, 0x06, 0x59, 0x06, 0x95, 0x85, 0x19, 0x95, 0x99, 0x33, 0x67, 0x22, 0x4b, 0xa0, - 0xb2, 0x37, 0x54, 0x66, 0x76, 0x9b, 0x2c, 0xe4, 0x3c, 0x81, 0x89, 0x33, 0x6f, 0xf0, 0xca, 0xd1, - 0x05, 0x29, 0x95, 0x61, 0x21, 0x21, 0x71, 0x9a, 0x55, 0x41, 0x4a, 0xe5, 0x07, 0x09, 0x89, 0x6d, - 0x93, 0xa6, 0xe9, 0xb3, 0x60, 0x60, 0x63, 0xdb, 0x0f, 0x48, 0x4b, 0x40, 0x9c, 0x1f, 0xe7, 0xc0, - 0x95, 0xb3, 0x68, 0x02, 0x53, 0x40, 0x37, 0x2b, 0x59, 0x08, 0x42, 0xa0, 0x70, 0x96, 0xaa, 0x66, - 0x25, 0x1b, 0x68, 0xd7, 0x7b, 0x41, 0x88, 0xd9, 0x61, 0x34, 0x39, 0xc0, 0xd4, 0xde, 0x25, 0xcd, - 0x31, 0xa6, 0xd2, 0xb1, 0xb6, 0xe7, 0x77, 0x56, 0xba, 0x6b, 0xfe, 0x94, 0x39, 0xff, 0x00, 0xd3, - 0x5e, 0xf3, 0xf4, 0x7c, 0xab, 0x31, 0x34, 0x29, 0xde, 0x93, 0x7a, 0xf9, 0xde, 0x18, 0xe3, 0xa3, - 0xde, 0x18, 0x91, 0xe9, 0x4d, 0x22, 0x6d, 0xd4, 0xbb, 0x57, 0x8e, 0xf7, 0xc5, 0x22, 0x6d, 0x93, - 0xb9, 0x5f, 0xf0, 0x11, 0x1e, 0x01, 0xef, 0x0b, 0xa0, 0x0a, 0x12, 0xfb, 0x21, 0x21, 0x11, 0xe5, - 0x47, 0x61, 0x02, 0xfc, 0xba, 0xa6, 0xa5, 0x91, 0x3d, 0x0d, 0xd8, 0xcf, 0xc9, 0x3a, 0x88, 0xb8, - 0xfb, 0x34, 0x8c, 0x91, 0x2b, 0x41, 0x63, 0x15, 0xd2, 0x24, 0x11, 0x20, 0x65, 0x4d, 0x60, 0xdb, - 0x44, 0xfb, 0x75, 0xf0, 0x75, 0x15, 0xb3, 0x1d, 0xb2, 0x14, 0xeb, 0xfe, 0x28, 0x6a, 0x3e, 0xaf, - 0x5c, 0x7b, 0x97, 0xdc, 0xcd, 0x65, 0xc8, 0x68, 0x02, 0xe1, 0xa1, 0x40, 0x16, 0xea, 0xab, 0x1b, - 0x6a, 0x97, 0x87, 0xff, 0xe7, 0xf2, 0x2d, 0x4d, 0x60, 0x5f, 0x20, 0xeb, 0x63, 0xce, 0xbd, 0x6f, - 0x16, 0xb9, 0x67, 0x46, 0xee, 0x23, 0x2f, 0x41, 0x28, 0x0d, 0x8e, 0x70, 0x50, 0x32, 0x7d, 0x5f, - 0x09, 0x3c, 0x01, 0x71, 0x75, 0xdf, 0xca, 0xfb, 0xcb, 0x61, 0x5d, 0xb2, 0xa2, 0x30, 0xd4, 0xc2, - 0xd0, 0xd9, 0xf5, 0xc0, 0x2d, 0x85, 0x03, 0x95, 0xe9, 0x14, 0xfb, 0x3d, 0x31, 0x7c, 0x4c, 0x47, - 0x5d, 0xe9, 0x6e, 0xf8, 0x95, 0x82, 0x7d, 0xad, 0x60, 0xbf, 0x56, 0xb0, 0xaf, 0x07, 0xec, 0x39, - 0xfa, 0x3a, 0x3f, 0xcf, 0xb7, 0xee, 0x9c, 0x50, 0x36, 0x7e, 0xe9, 0x5d, 0x57, 0x7a, 0xc3, 0x65, - 0x6d, 0x9b, 0xcd, 0x3e, 0x91, 0xd5, 0xea, 0xdc, 0x82, 0x72, 0x79, 0x08, 0xe2, 0xd6, 0x85, 0x66, - 0x04, 0x35, 0xf7, 0xbb, 0xa0, 0xa6, 0x32, 0x9f, 0xbf, 0x29, 0x73, 0x6f, 0x34, 0xe5, 0xcd, 0x2c, - 0xba, 0x07, 0xc7, 0x63, 0x3c, 0x81, 0xe4, 0xd6, 0x67, 0x1e, 0x91, 0xd5, 0x19, 0xc6, 0xea, 0xa7, - 0xfe, 0x8b, 0x6f, 0x30, 0xf5, 0x47, 0xd7, 0xc1, 0x04, 0xe2, 0x42, 0xfd, 0x63, 0xd7, 0xde, 0xab, - 0xd3, 0x0b, 0xd7, 0x3a, 0xbb, 0x70, 0xad, 0x1f, 0x17, 0xae, 0xf5, 0xf9, 0xd2, 0x6d, 0x9c, 0x5d, - 0xba, 0x8d, 0xef, 0x97, 0x6e, 0xe3, 0xe3, 0xe3, 0x34, 0x57, 0x59, 0x11, 0xf9, 0x31, 0xb2, 0xe0, - 0x5d, 0x1e, 0xe5, 0xa2, 0xe8, 0x67, 0x34, 0xe7, 0x01, 0x37, 0x76, 0x50, 0x76, 0x83, 0x89, 0xfe, - 0x06, 0xa2, 0x45, 0xf3, 0x0f, 0x3c, 0xfb, 0x15, 0x00, 0x00, 0xff, 0xff, 0x52, 0xaf, 0x8b, 0xfe, - 0x7a, 0x04, 0x00, 0x00, + // 668 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0x8d, 0xdb, 0xf4, 0x27, 0xd3, 0xaf, 0x5f, 0xc1, 0x0a, 0xc5, 0xad, 0xa8, 0x5b, 0x19, 0xa9, + 0xb4, 0x1b, 0x9b, 0x04, 0x24, 0x24, 0x56, 0x90, 0x34, 0x15, 0x8b, 0x82, 0x50, 0x14, 0x36, 0x48, + 0xc8, 0x9a, 0xd8, 0xb7, 0xb6, 0xd5, 0xcc, 0xdc, 0x6a, 0x66, 0x62, 0xa5, 0x6f, 0xc1, 0xa3, 0xf0, + 0x0c, 0xac, 0xba, 0xec, 0x0e, 0x56, 0x15, 0x6a, 0xdf, 0x80, 0x07, 0x40, 0x68, 0xc6, 0x4e, 0xd2, + 0x16, 0x75, 0x03, 0xdd, 0xdd, 0xff, 0xb9, 0xf7, 0x9c, 0xa3, 0x21, 0x0f, 0x41, 0xa5, 0x01, 0xe4, + 0x2c, 0xc8, 0x1b, 0x01, 0xe4, 0xc0, 0x95, 0xf4, 0x8f, 0x05, 0x2a, 0xb4, 0x09, 0xa8, 0xd4, 0x87, + 0x9c, 0xf9, 0x79, 0x63, 0xdd, 0x8d, 0x50, 0x32, 0x94, 0x41, 0x9f, 0x4a, 0x08, 0xf2, 0x46, 0x1f, + 0x14, 0x6d, 0x04, 0x11, 0x66, 0xbc, 0xa8, 0x5d, 0xaf, 0x5f, 0x1b, 0xc2, 0xc6, 0xd1, 0x04, 0x13, + 0x34, 0x66, 0xa0, 0xad, 0x22, 0xea, 0x7d, 0xb5, 0xc8, 0x4a, 0x47, 0x3f, 0xd4, 0x51, 0x29, 0x08, + 0x18, 0xb2, 0xde, 0xc8, 0x5e, 0x25, 0xf3, 0x94, 0xe1, 0x90, 0x2b, 0xc7, 0xda, 0xb2, 0x76, 0x6a, + 0xdd, 0xd2, 0xb3, 0xd7, 0xc8, 0x22, 0xa8, 0x34, 0x4c, 0xa9, 0x4c, 0x9d, 0x19, 0x93, 0x59, 0x00, + 0x95, 0xbe, 0xa1, 0x32, 0xb5, 0xeb, 0x64, 0x2e, 0xe3, 0x31, 0x8c, 0x9c, 0x59, 0x13, 0x2f, 0x1c, + 0xdd, 0x90, 0x50, 0x19, 0x0e, 0x25, 0xc4, 0x4e, 0xb5, 0x68, 0x48, 0xa8, 0xfc, 0x20, 0x21, 0xb6, + 0x6d, 0x52, 0x35, 0x73, 0xe6, 0x4c, 0xd8, 0xd8, 0xf6, 0x23, 0x52, 0x13, 0x10, 0x65, 0xc7, 0x19, + 0x70, 0xe5, 0xcc, 0x9b, 0xc4, 0x34, 0xa0, 0x87, 0xe5, 0x2c, 0x04, 0x21, 0x50, 0x38, 0x0b, 0xc5, + 0xb0, 0x9c, 0x75, 0xb4, 0xeb, 0xbd, 0x20, 0xc4, 0xdc, 0xd0, 0x1b, 0x1d, 0x60, 0x62, 0xef, 0x92, + 0xea, 0x00, 0x13, 0xe9, 0x58, 0x5b, 0xb3, 0x3b, 0x4b, 0xcd, 0x15, 0x7f, 0x8a, 0x9c, 0x7f, 0x80, + 0x49, 0xab, 0x7a, 0x7a, 0xbe, 0x59, 0xe9, 0x9a, 0x12, 0xef, 0x49, 0x79, 0x7c, 0x6b, 0x80, 0xd1, + 0x51, 0x6b, 0x80, 0xc8, 0xf4, 0x25, 0x7d, 0x6d, 0x94, 0xb7, 0x17, 0x8e, 0xf7, 0xc5, 0x22, 0x75, + 0x53, 0xb9, 0x3f, 0xe4, 0x3d, 0x3c, 0x02, 0xde, 0x16, 0x40, 0x15, 0xc4, 0xf6, 0x06, 0x21, 0x7d, + 0xca, 0x8f, 0xc2, 0x18, 0xf8, 0xa4, 0xa7, 0xa6, 0x23, 0x7b, 0x3a, 0x60, 0x3f, 0x27, 0xab, 0x20, + 0xa2, 0xe6, 0xd3, 0x30, 0x42, 0xae, 0x04, 0x8d, 0x54, 0x48, 0xe3, 0x58, 0x80, 0x94, 0x25, 0x80, + 0x75, 0x93, 0x6d, 0x97, 0xc9, 0xd7, 0x45, 0xce, 0x76, 0xc8, 0x42, 0xa4, 0xe7, 0xa3, 0x28, 0xf1, + 0x1c, 0xbb, 0xf6, 0x2e, 0xb9, 0x9f, 0xc9, 0x90, 0xd1, 0x18, 0xc2, 0x43, 0x81, 0x2c, 0xd4, 0xac, + 0x1b, 0x68, 0x17, 0xbb, 0xff, 0x67, 0xf2, 0x2d, 0x8d, 0x61, 0x5f, 0x20, 0x6b, 0x63, 0xc6, 0xbd, + 0x6f, 0x16, 0x79, 0x60, 0x56, 0x6e, 0x23, 0xcf, 0x41, 0x28, 0x1d, 0xec, 0x61, 0x27, 0x67, 0x9a, + 0x5f, 0x09, 0x3c, 0x06, 0x31, 0xe6, 0xb7, 0xf0, 0xfe, 0x72, 0x59, 0x97, 0x2c, 0x29, 0x0c, 0xb5, + 0x30, 0x74, 0x75, 0xb9, 0x70, 0x4d, 0x61, 0x47, 0xa5, 0xba, 0xc4, 0x7e, 0x4f, 0x0c, 0x1e, 0xd3, + 0x55, 0x97, 0x9a, 0x6b, 0x7e, 0xa1, 0x60, 0x5f, 0x2b, 0xd8, 0x2f, 0x15, 0xec, 0xeb, 0x05, 0x5b, + 0x8e, 0x66, 0xe7, 0xe7, 0xf9, 0xe6, 0xbd, 0x13, 0xca, 0x06, 0x2f, 0xbd, 0x49, 0xa7, 0xd7, 0x5d, + 0xd4, 0xb6, 0xb9, 0xec, 0x13, 0x59, 0x2e, 0xe8, 0x16, 0x94, 0xcb, 0x43, 0x10, 0xb7, 0x1e, 0x74, + 0x4d, 0x50, 0x33, 0x37, 0x05, 0x35, 0x95, 0xf9, 0xec, 0x55, 0x99, 0x7b, 0xbd, 0x29, 0x6e, 0xe6, + 0xd0, 0x3d, 0x38, 0x1e, 0xe0, 0x09, 0xc4, 0xb7, 0x3e, 0xf3, 0x98, 0x2c, 0x5f, 0x43, 0xac, 0x7c, + 0xea, 0xbf, 0xe8, 0x0a, 0x52, 0x7f, 0x4c, 0xed, 0x8c, 0x20, 0x1a, 0xaa, 0x7f, 0x9d, 0xfa, 0xeb, + 0x06, 0xc9, 0x9d, 0x9c, 0xf5, 0x50, 0x83, 0x74, 0xc7, 0x24, 0x6f, 0x10, 0xa2, 0x70, 0x52, 0x39, + 0xe1, 0x78, 0x9c, 0xbe, 0x73, 0x8e, 0xed, 0x6d, 0xb2, 0x52, 0x2c, 0x3c, 0x55, 0x56, 0xf1, 0x23, + 0x2c, 0x17, 0xe1, 0x52, 0x5d, 0xad, 0x57, 0xa7, 0x17, 0xae, 0x75, 0x76, 0xe1, 0x5a, 0x3f, 0x2e, + 0x5c, 0xeb, 0xf3, 0xa5, 0x5b, 0x39, 0xbb, 0x74, 0x2b, 0xdf, 0x2f, 0xdd, 0xca, 0xc7, 0xed, 0x24, + 0x53, 0xe9, 0xb0, 0xef, 0x47, 0xc8, 0x82, 0x77, 0x59, 0x3f, 0x13, 0xc3, 0x76, 0x4a, 0x33, 0x1e, + 0x70, 0x63, 0x07, 0x79, 0x33, 0x18, 0xe9, 0xdf, 0xb1, 0x3f, 0x6f, 0x3e, 0xc2, 0x67, 0xbf, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x51, 0xcd, 0x5e, 0xf1, 0x7b, 0x05, 0x00, 0x00, } func (m *EventEthereumTx) Marshal() (dAtA []byte, err error) { @@ -943,6 +1026,67 @@ func (m *EventContractExecuted) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *EventConvertEvmToCoin) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventConvertEvmToCoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventConvertEvmToCoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SenderEthAddr) > 0 { + i -= len(m.SenderEthAddr) + copy(dAtA[i:], m.SenderEthAddr) + i = encodeVarintEvents(dAtA, i, uint64(len(m.SenderEthAddr))) + i-- + dAtA[i] = 0x32 + } + { + size, err := m.BankCoin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.ToAddress) > 0 { + i -= len(m.ToAddress) + copy(dAtA[i:], m.ToAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ToAddress))) + i-- + dAtA[i] = 0x1a + } + if len(m.Erc20ContractAddress) > 0 { + i -= len(m.Erc20ContractAddress) + copy(dAtA[i:], m.Erc20ContractAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Erc20ContractAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -1121,6 +1265,33 @@ func (m *EventContractExecuted) Size() (n int) { return n } +func (m *EventConvertEvmToCoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Erc20ContractAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ToAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = m.BankCoin.Size() + n += 1 + l + sovEvents(uint64(l)) + l = len(m.SenderEthAddr) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2286,6 +2457,217 @@ func (m *EventContractExecuted) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventConvertEvmToCoin) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventConvertEvmToCoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventConvertEvmToCoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20ContractAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Erc20ContractAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field BankCoin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.BankCoin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SenderEthAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SenderEthAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/evm/evm.pb.go b/x/evm/evm.pb.go index fac1e30bbc..006fc356fe 100644 --- a/x/evm/evm.pb.go +++ b/x/evm/evm.pb.go @@ -96,6 +96,8 @@ type Params struct { // Fee deducted and burned when calling "CreateFunToken" in units of // "evm_denom". CreateFuntokenFee cosmossdk_io_math.Int `protobuf:"bytes,9,opt,name=create_funtoken_fee,json=createFuntokenFee,proto3,customtype=cosmossdk.io/math.Int" json:"create_funtoken_fee"` + // Hexadecimal address of the canonical WNIBI contract on Nibiru mainnet + CanonicalWnibi github_com_NibiruChain_nibiru_v2_eth.EIP55Addr `protobuf:"bytes,10,opt,name=canonical_wnibi,json=canonicalWnibi,proto3,customtype=github.com/NibiruChain/nibiru/v2/eth.EIP55Addr" json:"canonical_wnibi"` } func (m *Params) Reset() { *m = Params{} } @@ -553,67 +555,69 @@ func init() { func init() { proto.RegisterFile("eth/evm/v1/evm.proto", fileDescriptor_98abbdadb327b7d0) } var fileDescriptor_98abbdadb327b7d0 = []byte{ - // 954 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x55, 0x41, 0x6f, 0xe3, 0x44, - 0x14, 0xae, 0x1b, 0x27, 0x71, 0x26, 0xe9, 0xd6, 0x9d, 0x16, 0x64, 0x21, 0x6d, 0x1c, 0xf9, 0x80, - 0x82, 0xb4, 0x4a, 0xd8, 0xae, 0xca, 0xa1, 0x5c, 0x68, 0xb2, 0xad, 0x68, 0xa0, 0x4b, 0x35, 0x5b, - 0x38, 0x70, 0xb1, 0x26, 0xf6, 0x6b, 0x62, 0xc5, 0xf6, 0x44, 0x33, 0xe3, 0x28, 0xf9, 0x07, 0x1c, - 0xf9, 0x09, 0x7b, 0xe7, 0x8f, 0xac, 0x38, 0xed, 0x11, 0x71, 0xb0, 0x50, 0x7b, 0x41, 0x39, 0x72, - 0x42, 0x9c, 0xd0, 0x8c, 0xdd, 0x4d, 0x0a, 0x12, 0x9c, 0xfc, 0xbe, 0xef, 0xcd, 0x7b, 0xf3, 0xe6, - 0xfb, 0xc6, 0x36, 0x3a, 0x02, 0x39, 0xed, 0xc3, 0x22, 0xe9, 0x2f, 0x9e, 0xab, 0x47, 0x6f, 0xce, - 0x99, 0x64, 0x18, 0x81, 0x9c, 0xf6, 0x14, 0x5c, 0x3c, 0xff, 0xe8, 0x68, 0xc2, 0x26, 0x4c, 0xd3, - 0x7d, 0x15, 0x15, 0x2b, 0xbc, 0x9f, 0x0c, 0x64, 0x5d, 0x64, 0xe9, 0x0d, 0x9b, 0x41, 0x8a, 0xbf, - 0x45, 0x08, 0x78, 0x70, 0xfc, 0xa9, 0x4f, 0xc3, 0x90, 0x3b, 0x46, 0xc7, 0xe8, 0x36, 0x06, 0x9f, - 0xbd, 0xcd, 0xdd, 0x9d, 0x5f, 0x73, 0xb7, 0x37, 0x89, 0xe4, 0x34, 0x1b, 0xf7, 0x02, 0x96, 0xf4, - 0x5f, 0x45, 0xe3, 0x88, 0x67, 0xc3, 0x29, 0x8d, 0xd2, 0x7e, 0xaa, 0xe3, 0xfe, 0xe2, 0xb8, 0xaf, - 0xf6, 0x3a, 0xbf, 0xbc, 0x3e, 0x39, 0x39, 0x0b, 0x43, 0x4e, 0x1a, 0xba, 0x93, 0x0a, 0xf1, 0x53, - 0x84, 0xc6, 0x34, 0x9d, 0xf9, 0x21, 0xa4, 0x2c, 0x71, 0x76, 0x55, 0x5b, 0xd2, 0x50, 0xcc, 0x4b, - 0x45, 0xe0, 0x4f, 0xd0, 0x41, 0x24, 0xfc, 0x84, 0x86, 0xe0, 0xdf, 0x72, 0x96, 0xf8, 0x01, 0x8b, - 0x52, 0xa7, 0xd2, 0x31, 0xba, 0x16, 0x79, 0x12, 0x89, 0x2b, 0x1a, 0xc2, 0x05, 0x67, 0xc9, 0x90, - 0x45, 0xa9, 0xf7, 0xa7, 0x81, 0x6a, 0xd7, 0x94, 0xd3, 0x44, 0xe0, 0x33, 0x84, 0x60, 0x29, 0x39, - 0xf5, 0x21, 0x9a, 0x0b, 0xc7, 0xec, 0x54, 0xba, 0x95, 0x81, 0x77, 0x97, 0xbb, 0x8d, 0x73, 0xc5, - 0x9e, 0x5f, 0x5e, 0x8b, 0x3f, 0x72, 0xf7, 0x60, 0x45, 0x93, 0xf8, 0xd4, 0xdb, 0x2c, 0xf4, 0x48, - 0x43, 0x83, 0xf3, 0x68, 0x2e, 0xf0, 0x31, 0x6a, 0xc1, 0x22, 0xf1, 0x83, 0x29, 0x4d, 0x53, 0x88, - 0x85, 0x63, 0x75, 0x2a, 0xdd, 0xc6, 0x60, 0xff, 0x2e, 0x77, 0x9b, 0xe7, 0xdf, 0x5d, 0x0d, 0x4b, - 0x9a, 0x34, 0x61, 0x91, 0x3c, 0x00, 0x7c, 0x85, 0x0e, 0x03, 0x0e, 0x54, 0x82, 0x7f, 0x9b, 0xa5, - 0x52, 0xa9, 0xe6, 0xdf, 0x02, 0x38, 0x0d, 0xad, 0xd5, 0xd3, 0x52, 0xab, 0x0f, 0x02, 0x26, 0x12, - 0x26, 0x44, 0x38, 0xeb, 0x45, 0xac, 0x9f, 0x50, 0x39, 0xed, 0x5d, 0xa6, 0x92, 0x1c, 0x14, 0x95, - 0x17, 0x65, 0xe1, 0x05, 0xc0, 0xa9, 0xf9, 0xfb, 0x1b, 0xd7, 0x18, 0x99, 0x96, 0x61, 0xef, 0x8e, - 0x4c, 0x6b, 0xd7, 0xae, 0x8c, 0x4c, 0xab, 0x62, 0x9b, 0x23, 0xd3, 0xaa, 0xda, 0xb5, 0x91, 0x69, - 0xd5, 0xec, 0xfa, 0xc8, 0xb4, 0xea, 0xb6, 0xe5, 0xf5, 0x51, 0xf5, 0xb5, 0xa4, 0x12, 0xb0, 0x8d, - 0x2a, 0x33, 0x58, 0x15, 0xee, 0x10, 0x15, 0xe2, 0x23, 0x54, 0x5d, 0xd0, 0x38, 0x83, 0x52, 0xda, - 0x02, 0x78, 0x3f, 0xef, 0xa2, 0xca, 0xd7, 0x6c, 0x82, 0x1d, 0x54, 0x57, 0x76, 0x82, 0x10, 0x65, - 0xcd, 0x03, 0xc4, 0x1f, 0xa2, 0x9a, 0x64, 0xf3, 0x28, 0x10, 0xce, 0xae, 0x3a, 0x39, 0x29, 0x11, - 0xc6, 0xc8, 0x0c, 0xa9, 0xa4, 0xda, 0x83, 0x16, 0xd1, 0xb1, 0xd2, 0x6a, 0x1c, 0xb3, 0x60, 0xe6, - 0xa7, 0x59, 0x32, 0x06, 0xee, 0x98, 0x1d, 0xa3, 0x6b, 0x0e, 0xf6, 0xd7, 0xb9, 0xdb, 0xd4, 0xfc, - 0x2b, 0x4d, 0x93, 0x6d, 0x80, 0x9f, 0xa1, 0xba, 0x5c, 0xfa, 0x53, 0x2a, 0xa6, 0x4e, 0x55, 0xeb, - 0x73, 0xb8, 0xce, 0xdd, 0x7d, 0xc9, 0x69, 0x2a, 0x68, 0x20, 0x23, 0x96, 0x7e, 0x49, 0xc5, 0x94, - 0xd4, 0xe4, 0x52, 0x3d, 0x71, 0x1f, 0x59, 0x72, 0xe9, 0x47, 0x69, 0x08, 0x4b, 0xa7, 0xa6, 0xbb, - 0x1f, 0xad, 0x73, 0xd7, 0xde, 0x5a, 0x7e, 0xa9, 0x72, 0xa4, 0x2e, 0x97, 0x3a, 0xc0, 0xcf, 0x10, - 0x2a, 0x46, 0xd2, 0x3b, 0xd4, 0xf5, 0x0e, 0x7b, 0xeb, 0xdc, 0x6d, 0x68, 0x56, 0xf7, 0xde, 0x84, - 0xd8, 0x43, 0xd5, 0xa2, 0xb7, 0xa5, 0x7b, 0xb7, 0xd6, 0xb9, 0x6b, 0xc5, 0x6c, 0x52, 0xf4, 0x2c, - 0x52, 0x4a, 0x2a, 0x0e, 0x09, 0x5b, 0x40, 0xa8, 0x0d, 0xb5, 0xc8, 0x03, 0xf4, 0x28, 0x6a, 0x9e, - 0x05, 0x01, 0x08, 0x71, 0x93, 0xcd, 0x63, 0xf8, 0x0f, 0x4d, 0x8f, 0x51, 0x4b, 0x48, 0xc6, 0xe9, - 0x04, 0xfc, 0x19, 0xac, 0x4a, 0x65, 0x0b, 0x9d, 0x4a, 0xfe, 0x2b, 0x58, 0x09, 0xb2, 0x0d, 0x4e, - 0xcd, 0x1f, 0xde, 0xb8, 0x3b, 0xde, 0x10, 0xb5, 0x6e, 0x38, 0x0d, 0x80, 0x0f, 0x59, 0x7a, 0x1b, - 0x4d, 0xf0, 0x0b, 0xb4, 0xc7, 0xd2, 0x78, 0xe5, 0x4b, 0x36, 0xf7, 0x03, 0x1a, 0xc7, 0x7a, 0x27, - 0xab, 0x68, 0xa5, 0x12, 0x37, 0x6c, 0x3e, 0xa4, 0x71, 0x4c, 0xb6, 0x81, 0xf7, 0x57, 0x05, 0x35, - 0x75, 0x97, 0xb2, 0x89, 0xb2, 0x58, 0x37, 0x2d, 0xe7, 0x2c, 0x91, 0x3a, 0x80, 0x8c, 0x12, 0x60, - 0x99, 0x2c, 0x2f, 0xcd, 0x03, 0x54, 0x15, 0x1c, 0x60, 0x09, 0x81, 0xb6, 0xdf, 0x24, 0x25, 0xc2, - 0x27, 0x68, 0x2f, 0x8c, 0x04, 0x1d, 0xc7, 0xe0, 0x0b, 0x49, 0x83, 0x99, 0xb6, 0xd4, 0x1a, 0xd8, - 0xeb, 0xdc, 0x6d, 0x95, 0x89, 0xd7, 0x8a, 0x27, 0x8f, 0x10, 0xfe, 0x1c, 0xed, 0x6f, 0xca, 0xf4, - 0x91, 0xb5, 0xb9, 0xd6, 0x00, 0xaf, 0x73, 0xf7, 0xc9, 0xfb, 0xa5, 0x3a, 0x43, 0xfe, 0x81, 0xd5, - 0xc5, 0x0e, 0x61, 0x9c, 0x4d, 0xb4, 0x67, 0x16, 0x29, 0x80, 0x62, 0xe3, 0x28, 0x89, 0xa4, 0xf6, - 0xa8, 0x4a, 0x0a, 0xa0, 0xe6, 0x83, 0x54, 0xef, 0x93, 0x40, 0xc2, 0xf8, 0xca, 0x69, 0x6e, 0xe6, - 0x2b, 0x12, 0x57, 0x9a, 0x27, 0x8f, 0x10, 0x1e, 0x20, 0x5c, 0x96, 0x71, 0x90, 0x19, 0x4f, 0x7d, - 0x7d, 0xf3, 0x5b, 0xba, 0x56, 0xdf, 0xbf, 0x22, 0x4b, 0x74, 0xf2, 0x25, 0x95, 0x94, 0xfc, 0x8b, - 0xc1, 0xdf, 0xa0, 0xbd, 0x42, 0x56, 0x3f, 0xd0, 0xaa, 0x3b, 0x7b, 0x1d, 0xa3, 0xdb, 0x3c, 0x76, - 0x7a, 0x9b, 0xaf, 0x6f, 0x6f, 0xdb, 0xda, 0x62, 0x28, 0xb9, 0xc5, 0x90, 0x47, 0x68, 0x64, 0x5a, - 0xa6, 0x5d, 0x2d, 0xde, 0xfb, 0x91, 0x69, 0x21, 0xbb, 0xf9, 0x5e, 0x99, 0xf2, 0x70, 0xe4, 0xf0, - 0x01, 0x6f, 0x4d, 0x3d, 0xf8, 0xe2, 0xed, 0x5d, 0xdb, 0x78, 0x77, 0xd7, 0x36, 0x7e, 0xbb, 0x6b, - 0x1b, 0x3f, 0xde, 0xb7, 0x77, 0xde, 0xdd, 0xb7, 0x77, 0x7e, 0xb9, 0x6f, 0xef, 0x7c, 0xff, 0xf1, - 0xff, 0x7e, 0xbc, 0x97, 0xea, 0xaf, 0x31, 0xae, 0xe9, 0x9f, 0xc2, 0x8b, 0xbf, 0x03, 0x00, 0x00, - 0xff, 0xff, 0x33, 0x26, 0x36, 0x60, 0x4e, 0x06, 0x00, 0x00, + // 979 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x55, 0x41, 0x6f, 0xe3, 0x44, + 0x14, 0xae, 0x13, 0x27, 0x71, 0x26, 0x69, 0xeb, 0x4e, 0x0b, 0xb2, 0x90, 0x36, 0x8e, 0x7c, 0x40, + 0x41, 0x5a, 0x25, 0x6c, 0x57, 0xe5, 0x50, 0x2e, 0x34, 0xd9, 0x56, 0x34, 0xd0, 0xa5, 0x9a, 0x2d, + 0x20, 0x71, 0xb1, 0x26, 0xf6, 0x6b, 0x62, 0xc5, 0x9e, 0x89, 0x3c, 0x93, 0x90, 0xfc, 0x03, 0x8e, + 0xfc, 0x84, 0xbd, 0xf3, 0x47, 0x56, 0x9c, 0xf6, 0x08, 0x1c, 0x2c, 0xd4, 0x5e, 0x50, 0x8e, 0x1c, + 0x39, 0xa1, 0x19, 0xbb, 0x6d, 0x0a, 0x12, 0x1c, 0xf6, 0x94, 0xf7, 0x7d, 0x6f, 0xde, 0x37, 0xcf, + 0xdf, 0x7b, 0xb1, 0xd1, 0x01, 0xc8, 0x49, 0x0f, 0x16, 0x49, 0x6f, 0xf1, 0x4c, 0xfd, 0x74, 0x67, + 0x29, 0x97, 0x1c, 0x23, 0x90, 0x93, 0xae, 0x82, 0x8b, 0x67, 0x1f, 0x1c, 0x8c, 0xf9, 0x98, 0x6b, + 0xba, 0xa7, 0xa2, 0xfc, 0x84, 0xf7, 0x93, 0x81, 0xac, 0xb3, 0x39, 0xbb, 0xe2, 0x53, 0x60, 0xf8, + 0x6b, 0x84, 0x20, 0x0d, 0x0e, 0x3f, 0xf6, 0x69, 0x18, 0xa6, 0x8e, 0xd1, 0x36, 0x3a, 0xf5, 0xfe, + 0x27, 0x6f, 0x32, 0x77, 0xeb, 0xb7, 0xcc, 0xed, 0x8e, 0x23, 0x39, 0x99, 0x8f, 0xba, 0x01, 0x4f, + 0x7a, 0x2f, 0xa3, 0x51, 0x94, 0xce, 0x07, 0x13, 0x1a, 0xb1, 0x1e, 0xd3, 0x71, 0x6f, 0x71, 0xd8, + 0x53, 0x77, 0x9d, 0x9e, 0x5f, 0x1e, 0x1d, 0x9d, 0x84, 0x61, 0x4a, 0xea, 0x5a, 0x49, 0x85, 0xf8, + 0x09, 0x42, 0x23, 0xca, 0xa6, 0x7e, 0x08, 0x8c, 0x27, 0x4e, 0x49, 0xc9, 0x92, 0xba, 0x62, 0x5e, + 0x28, 0x02, 0x7f, 0x84, 0xf6, 0x22, 0xe1, 0x27, 0x34, 0x04, 0xff, 0x3a, 0xe5, 0x89, 0x1f, 0xf0, + 0x88, 0x39, 0xe5, 0xb6, 0xd1, 0xb1, 0xc8, 0x4e, 0x24, 0x2e, 0x68, 0x08, 0x67, 0x29, 0x4f, 0x06, + 0x3c, 0x62, 0xde, 0xaf, 0x25, 0x54, 0xbd, 0xa4, 0x29, 0x4d, 0x04, 0x3e, 0x41, 0x08, 0x96, 0x32, + 0xa5, 0x3e, 0x44, 0x33, 0xe1, 0x98, 0xed, 0x72, 0xa7, 0xdc, 0xf7, 0x6e, 0x32, 0xb7, 0x7e, 0xaa, + 0xd8, 0xd3, 0xf3, 0x4b, 0xf1, 0x67, 0xe6, 0xee, 0xad, 0x68, 0x12, 0x1f, 0x7b, 0x0f, 0x07, 0x3d, + 0x52, 0xd7, 0xe0, 0x34, 0x9a, 0x09, 0x7c, 0x88, 0x9a, 0xb0, 0x48, 0xfc, 0x60, 0x42, 0x19, 0x83, + 0x58, 0x38, 0x56, 0xbb, 0xdc, 0xa9, 0xf7, 0x77, 0x6f, 0x32, 0xb7, 0x71, 0xfa, 0xcd, 0xc5, 0xa0, + 0xa0, 0x49, 0x03, 0x16, 0xc9, 0x1d, 0xc0, 0x17, 0x68, 0x3f, 0x48, 0x81, 0x4a, 0xf0, 0xaf, 0xe7, + 0x4c, 0x2a, 0xd7, 0xfc, 0x6b, 0x00, 0xa7, 0xae, 0xbd, 0x7a, 0x52, 0x78, 0xf5, 0x5e, 0xc0, 0x45, + 0xc2, 0x85, 0x08, 0xa7, 0xdd, 0x88, 0xf7, 0x12, 0x2a, 0x27, 0xdd, 0x73, 0x26, 0xc9, 0x5e, 0x5e, + 0x79, 0x56, 0x14, 0x9e, 0x01, 0x60, 0x1f, 0xed, 0x06, 0x94, 0x71, 0x16, 0x05, 0x34, 0xf6, 0xbf, + 0x57, 0x5e, 0x3a, 0xe8, 0x9d, 0x6c, 0xdf, 0xb9, 0x97, 0xfb, 0x56, 0x1d, 0x39, 0x36, 0xff, 0x78, + 0xed, 0x1a, 0x43, 0xd3, 0x32, 0xec, 0xd2, 0xd0, 0xb4, 0x4a, 0x76, 0x79, 0x68, 0x5a, 0x65, 0xdb, + 0x1c, 0x9a, 0x56, 0xc5, 0xae, 0x0e, 0x4d, 0xab, 0x6a, 0xd7, 0x86, 0xa6, 0x55, 0xb3, 0x2d, 0xaf, + 0x87, 0x2a, 0xaf, 0x24, 0x95, 0x80, 0x6d, 0x54, 0x9e, 0xc2, 0x2a, 0x1f, 0x3f, 0x51, 0x21, 0x3e, + 0x40, 0x95, 0x05, 0x8d, 0xe7, 0x50, 0xcc, 0x2e, 0x07, 0xde, 0xcf, 0x25, 0x54, 0xfe, 0x92, 0x8f, + 0xb1, 0x83, 0x6a, 0x6a, 0x5f, 0x40, 0x88, 0xa2, 0xe6, 0x0e, 0xe2, 0xf7, 0x51, 0x55, 0xf2, 0x59, + 0x14, 0x08, 0xa7, 0xa4, 0xac, 0x25, 0x05, 0xc2, 0x18, 0x99, 0x21, 0x95, 0x54, 0x0f, 0xb9, 0x49, + 0x74, 0xac, 0x86, 0x31, 0x8a, 0x79, 0x30, 0xf5, 0xd9, 0x3c, 0x19, 0x41, 0xea, 0x98, 0x6d, 0xa3, + 0x63, 0xf6, 0x77, 0xd7, 0x99, 0xdb, 0xd0, 0xfc, 0x4b, 0x4d, 0x93, 0x4d, 0x80, 0x9f, 0xa2, 0x9a, + 0x5c, 0xfa, 0x13, 0x2a, 0x26, 0x4e, 0x45, 0xbb, 0xb6, 0xbf, 0xce, 0xdc, 0x5d, 0x99, 0x52, 0x26, + 0x68, 0x20, 0x23, 0xce, 0x3e, 0xa7, 0x62, 0x42, 0xaa, 0x72, 0xa9, 0x7e, 0x71, 0x0f, 0x59, 0x72, + 0xe9, 0x47, 0x2c, 0x84, 0xa5, 0x53, 0xd5, 0xea, 0x07, 0xeb, 0xcc, 0xb5, 0x37, 0x8e, 0x9f, 0xab, + 0x1c, 0xa9, 0xc9, 0xa5, 0x0e, 0xf0, 0x53, 0x84, 0xf2, 0x96, 0xf4, 0x0d, 0x35, 0x7d, 0xc3, 0xf6, + 0x3a, 0x73, 0xeb, 0x9a, 0xd5, 0xda, 0x0f, 0x21, 0xf6, 0x50, 0x25, 0xd7, 0xb6, 0xb4, 0x76, 0x73, + 0x9d, 0xb9, 0x56, 0xcc, 0xc7, 0xb9, 0x66, 0x9e, 0x52, 0x56, 0xa5, 0x90, 0xf0, 0x05, 0x84, 0x7a, + 0x63, 0x2c, 0x72, 0x07, 0x3d, 0x8a, 0x1a, 0x27, 0x41, 0x00, 0x42, 0x5c, 0xcd, 0x67, 0x31, 0xfc, + 0x87, 0xa7, 0x87, 0xa8, 0x29, 0x24, 0x4f, 0xe9, 0x18, 0xfc, 0x29, 0xac, 0x0a, 0x67, 0x73, 0x9f, + 0x0a, 0xfe, 0x0b, 0x58, 0x09, 0xb2, 0x09, 0x8e, 0xcd, 0x1f, 0x5e, 0xbb, 0x5b, 0xde, 0x00, 0x35, + 0xaf, 0x52, 0x1a, 0x40, 0x3a, 0xe0, 0xec, 0x3a, 0x1a, 0xe3, 0xe7, 0x68, 0x9b, 0xb3, 0x78, 0xe5, + 0x4b, 0x3e, 0xf3, 0x03, 0x1a, 0xc7, 0xfa, 0x26, 0x2b, 0x97, 0x52, 0x89, 0x2b, 0x3e, 0x1b, 0xd0, + 0x38, 0x26, 0x9b, 0xc0, 0xfb, 0xab, 0x8c, 0x1a, 0x5a, 0xa5, 0x10, 0x51, 0x23, 0xd6, 0xa2, 0x45, + 0x9f, 0x05, 0x52, 0x0f, 0x20, 0xa3, 0x04, 0xf8, 0x5c, 0x16, 0x4b, 0x73, 0x07, 0x55, 0x45, 0x0a, + 0xb0, 0x84, 0x40, 0x8f, 0xdf, 0x24, 0x05, 0xc2, 0x47, 0x68, 0x3b, 0x8c, 0x04, 0x1d, 0xc5, 0xe0, + 0x0b, 0x49, 0x83, 0xa9, 0x1e, 0xa9, 0xd5, 0xb7, 0xd7, 0x99, 0xdb, 0x2c, 0x12, 0xaf, 0x14, 0x4f, + 0x1e, 0x21, 0xfc, 0x29, 0xda, 0x7d, 0x28, 0xd3, 0x8f, 0xac, 0x87, 0x6b, 0xf5, 0xf1, 0x3a, 0x73, + 0x77, 0xee, 0x8f, 0xea, 0x0c, 0xf9, 0x07, 0x56, 0x8b, 0x1d, 0xc2, 0x68, 0x3e, 0xd6, 0x33, 0xb3, + 0x48, 0x0e, 0x14, 0x1b, 0x47, 0x49, 0x24, 0xf5, 0x8c, 0x2a, 0x24, 0x07, 0xaa, 0x3f, 0x60, 0xfa, + 0x9e, 0x04, 0x12, 0x9e, 0xae, 0x9c, 0xc6, 0x43, 0x7f, 0x79, 0xe2, 0x42, 0xf3, 0xe4, 0x11, 0xc2, + 0x7d, 0x84, 0x8b, 0xb2, 0x14, 0xe4, 0x3c, 0x65, 0xbe, 0xde, 0xfc, 0xa6, 0xae, 0xd5, 0xfb, 0x97, + 0x67, 0x89, 0x4e, 0xbe, 0xa0, 0x92, 0x92, 0x7f, 0x31, 0xf8, 0x2b, 0xb4, 0x9d, 0xdb, 0xea, 0x07, + 0xda, 0x75, 0x67, 0xbb, 0x6d, 0x74, 0x1a, 0x87, 0x4e, 0xf7, 0xe1, 0xf5, 0xde, 0xdd, 0x1c, 0x6d, + 0xde, 0x94, 0xdc, 0x60, 0xc8, 0x23, 0x34, 0x34, 0x2d, 0xd3, 0xae, 0xe4, 0xff, 0xfb, 0xa1, 0x69, + 0x21, 0xbb, 0x71, 0xef, 0x4c, 0xf1, 0x70, 0x64, 0xff, 0x0e, 0x6f, 0x74, 0xdd, 0xff, 0xec, 0xcd, + 0x4d, 0xcb, 0x78, 0x7b, 0xd3, 0x32, 0x7e, 0xbf, 0x69, 0x19, 0x3f, 0xde, 0xb6, 0xb6, 0xde, 0xde, + 0xb6, 0xb6, 0x7e, 0xb9, 0x6d, 0x6d, 0x7d, 0xf7, 0xe1, 0xff, 0xbe, 0xa6, 0x96, 0xea, 0xb3, 0x34, + 0xaa, 0xea, 0xaf, 0xce, 0xf3, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x55, 0xc2, 0xca, 0x8f, 0xaf, + 0x06, 0x00, 0x00, } func (this *Params) Equal(that interface{}) bool { @@ -654,6 +658,9 @@ func (this *Params) Equal(that interface{}) bool { if !this.CreateFuntokenFee.Equal(that1.CreateFuntokenFee) { return false } + if !this.CanonicalWnibi.Equal(that1.CanonicalWnibi) { + return false + } return true } func (m *FunToken) Marshal() (dAtA []byte, err error) { @@ -726,6 +733,16 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.CanonicalWnibi.Size() + i -= size + if _, err := m.CanonicalWnibi.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvm(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 { size := m.CreateFuntokenFee.Size() i -= size @@ -1120,6 +1137,8 @@ func (m *Params) Size() (n int) { } l = m.CreateFuntokenFee.Size() n += 1 + l + sovEvm(uint64(l)) + l = m.CanonicalWnibi.Size() + n += 1 + l + sovEvm(uint64(l)) return n } @@ -1569,6 +1588,40 @@ func (m *Params) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CanonicalWnibi", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvm + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvm + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvm + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.CanonicalWnibi.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvm(dAtA[iNdEx:]) diff --git a/x/evm/evmmodule/genesis_test.go b/x/evm/evmmodule/genesis_test.go index 08eac19ed9..936c545c6e 100644 --- a/x/evm/evmmodule/genesis_test.go +++ b/x/evm/evmmodule/genesis_test.go @@ -3,14 +3,18 @@ package evmmodule_test import ( "math/big" + "strings" "testing" sdkmath "cosmossdk.io/math" + "github.com/MakeNowJust/heredoc/v2" sdk "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" "github.com/stretchr/testify/suite" "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" "github.com/NibiruChain/nibiru/v2/x/evm" "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/evmmodule" @@ -28,102 +32,215 @@ func TestKeeperSuite(t *testing.T) { // TestExportInitGenesis // - creates evm state with erc20 contract, sends tokens to user A and B -// - creates fungible token from unibi coin and sends to user C -// - exports / imports genesis +// - creates fungible token mapping for the erc20 and send some to C +// - exports and imports genesis // - verifies that contracts are in place and user balances match // - verifies that fungible token is in place and the balance is correct func (s *Suite) TestExportInitGenesis() { - deps := evmtest.NewTestDeps() - erc20Contract := embeds.SmartContract_TestERC20 - fromUser := deps.Sender.EthAddr - toUserA := gethcommon.HexToAddress("0xAE8A5F44A9b55Ae6D2c9C228253E8fAfb837d2F2") - toUserB := gethcommon.HexToAddress("0xf893292542F2578F1004e62fd723901ddE5EC5Cf") - toUserC := gethcommon.HexToAddress("0xe90f75496E744b92B52535bB05a29123D0D94D49") - amountToSendA := big.NewInt(1550) - amountToSendB := big.NewInt(333) - amountToSendC := big.NewInt(228) - - // Create ERC-20 contract - deployResp, err := evmtest.DeployContract(&deps, erc20Contract) - s.Require().NoError(err) - erc20Addr := deployResp.ContractAddr - - evmObj, _ := deps.NewEVM() - totalSupply, err := deps.EvmKeeper.ERC20().LoadERC20BigInt( - deps.Ctx, evmObj, erc20Contract.ABI, erc20Addr, "totalSupply", + var ( + deps = evmtest.NewTestDeps() + // Keep the sender as a variable since "deps" will be reset after we export the genesis + depsSender = deps.Sender + erc20Contract = embeds.SmartContract_TestERC20 + toUserA = gethcommon.HexToAddress("0xAE8A5F44A9b55Ae6D2c9C228253E8fAfb837d2F2") + toUserB = gethcommon.HexToAddress("0xf893292542F2578F1004e62fd723901ddE5EC5Cf") + toUserC = gethcommon.HexToAddress("0xe90f75496E744b92B52535bB05a29123D0D94D49") + amountToSendA = big.NewInt(1500) + amountToSendB = big.NewInt(350) + amountToSendC = big.NewInt(200) + + // [evm.FunToken] mapping used throughout the test + ft evm.FunToken + + totalSupply *big.Int + erc20Addr gethcommon.Address ) - s.Require().NoError(err) - - // Transfer ERC-20 tokens to user A - _, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserA, amountToSendA, deps.Ctx, evmObj) - s.Require().NoError(err) - - // Transfer ERC-20 tokens to user B - _, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, fromUser, toUserB, amountToSendB, deps.Ctx, evmObj) - s.Require().NoError(err) - - // Create fungible token from bank coin - funToken := evmtest.CreateFunTokenForBankCoin(deps, "unibi", &s.Suite) - s.Require().NoError(err) - funTokenAddr := funToken.Erc20Addr.Address - - // Fund sender's wallet - spendableCoins := sdk.NewCoins(sdk.NewInt64Coin("unibi", totalSupply.Int64())) - err = deps.App.BankKeeper.MintCoins(deps.Ctx, evm.ModuleName, spendableCoins) - s.Require().NoError(err) - err = deps.App.BankKeeper.SendCoinsFromModuleToAccount( - deps.Ctx, evm.ModuleName, deps.Sender.NibiruAddr, spendableCoins, - ) - s.Require().NoError(err) - - eip55Addr, err := eth.NewEIP55AddrFromStr(toUserC.String()) - s.Require().NoError(err) - // Send fungible token coins from bank to evm - _, err = deps.EvmKeeper.ConvertCoinToEvm( - deps.Ctx, - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.Coin{Denom: "unibi", Amount: sdkmath.NewInt(amountToSendC.Int64())}, - ToEthAddr: eip55Addr, - }, - ) - s.Require().NoError(err) - // Export genesis + // Create re-usable function to perform the assertion later afer exporting + // the genesis. + assertBalsOfAB := func(deps evmtest.TestDeps, evmObj *vm.EVM) { + evmtest.FunTokenBalanceAssert{ + Account: toUserA, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: amountToSendA, + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + Account: toUserB, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: amountToSendB, + }.Assert(s.T(), deps, evmObj) + } + + assertBalsAfterConvert := func(deps evmtest.TestDeps) { + s.T().Log("Assert balances for users A, B, C, the sender and the EVM") + + // NOTE: You need a fresh evmObj here + evmObj, _ := deps.NewEVM() + + assertBalsOfAB(deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + Account: toUserC, + FunToken: ft, + BalanceBank: amountToSendC, + BalanceERC20: big.NewInt(0), + Description: "C receives amountToSendC as Bank Coins", + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + Account: evm.EVM_MODULE_ADDRESS, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: amountToSendC, + Description: "EVM holds ERC20 tokens to back the newly minted Bank Coins", + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + Account: depsSender.EthAddr, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: new(big.Int).Sub( + totalSupply, + big.NewInt(amountToSendA.Int64()+amountToSendB.Int64()+amountToSendC.Int64()), + ), + Description: "Sender should lose amountToSendC", + }.Assert(s.T(), deps, evmObj) + } + + { + // Create ERC-20 contract + deployResp, err := evmtest.DeployContract(&deps, erc20Contract) + s.Require().NoError(err) + erc20Addr = deployResp.ContractAddr + + evmObj, sdb := deps.NewEVM() + totalSupply, err = deps.EvmKeeper.ERC20().LoadERC20BigInt( + deps.Ctx, evmObj, erc20Contract.ABI, erc20Addr, "totalSupply", + ) + s.Require().NoError(err) + s.Require().Equal("1000000"+strings.Repeat("0", 18), totalSupply.String()) + s.T().Log(heredoc.Docf( + `Deployed ERC20 with total supply held by deps.Sender +totalSupply: %s +deps.Sender.EthAddr: %s +erc20Addr: %s +amountToSendA: %s +amountToSendB: %s +amountToSendC: %s`, + totalSupply, + deps.Sender.EthAddr.Hex(), + erc20Addr, + amountToSendA, + amountToSendB, + amountToSendC, + )) + + s.T().Log("Transfer ERC-20 tokens to [userA, userB]") + _, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, deps.Sender.EthAddr, toUserA, amountToSendA, deps.Ctx, evmObj) + s.Require().NoError(err) + + s.T().Log("Transfer ERC-20 tokens to user B") + _, _, err = deps.EvmKeeper.ERC20().Transfer(erc20Addr, deps.Sender.EthAddr, toUserB, amountToSendB, deps.Ctx, evmObj) + s.Require().NoError(err) + s.NoError( + sdb.Commit(), + ) + } + + s.T().Logf("Create FunToken from the er20 %s", erc20Addr) + { + // There's a fee for "evm.MsgCreateFunToken". The sender account needs + // needs funds for that. + err := testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins( + sdk.NewCoin(evm.EVMBankDenom, deps.EvmKeeper.GetParams(deps.Ctx).CreateFuntokenFee), + ), + ) + s.Require().NoError(err) + + createFuntokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: ð.EIP55Addr{Address: erc20Addr}, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + ft = createFuntokenResp.FuntokenMapping + } + + s.T().Log("balance assertions after transfer to [userA, userB]") + + { + evmObj, _ := deps.NewEVM() + assertBalsOfAB(deps, evmObj) + evmtest.FunTokenBalanceAssert{ + Account: toUserC, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "C should not have any funds yet", + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + Account: deps.Sender.EthAddr, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: new(big.Int).Sub( + totalSupply, + big.NewInt(amountToSendA.Int64()+amountToSendB.Int64()), + ), + Description: "Sender has total supply minus A and B", + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + Account: evm.EVM_MODULE_ADDRESS, + FunToken: ft, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "inactive so far", + }.Assert(s.T(), deps, evmObj) + } + + s.T().Log("Send fungible token coins from bank to evm") + { + _, err := deps.EvmKeeper.ConvertEvmToCoin( + deps.Ctx, + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: ft.Erc20Addr, + Amount: sdkmath.NewIntFromBigInt(amountToSendC), + ToAddr: toUserC.Hex(), + }, + ) + s.Require().NoError(err) + } + + assertBalsAfterConvert(deps) + + s.T().Log("Export genesis") + evmGenesisState := evmmodule.ExportGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper) authGenesisState := deps.App.AccountKeeper.ExportGenesis(deps.Ctx) - - // Init genesis from the exported state - deps = evmtest.NewTestDeps() - deps.App.AccountKeeper.InitGenesis(deps.Ctx, *authGenesisState) - evmmodule.InitGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState) - - // Verify erc20 balances for users A, B and sender - balance, err := deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserA, deps.Ctx, evmObj) - s.Require().NoError(err) - s.Require().Equal(amountToSendA, balance) - - balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, toUserB, deps.Ctx, evmObj) - s.Require().NoError(err) - s.Require().Equal(amountToSendB, balance) - - balance, err = deps.EvmKeeper.ERC20().BalanceOf(erc20Addr, fromUser, deps.Ctx, evmObj) - s.Require().NoError(err) - s.Require().Equal( - new(big.Int).Sub(totalSupply, big.NewInt(amountToSendA.Int64()+amountToSendB.Int64())), - balance, - ) - - // Check that fungible token mapping is in place - iter := deps.EvmKeeper.FunTokens.Indexes.BankDenom.ExactMatch(deps.Ctx, "unibi") - funTokens := deps.EvmKeeper.FunTokens.Collect(deps.Ctx, iter) - s.Require().Len(funTokens, 1) - s.Require().Equal(funTokenAddr.String(), funTokens[0].Erc20Addr.String()) - s.Require().Equal("unibi", funTokens[0].BankDenom) - s.Require().True(funTokens[0].IsMadeFromCoin) - - // Check that fungible token balance of user C is correct - balance, err = deps.EvmKeeper.ERC20().BalanceOf(funTokenAddr, toUserC, deps.Ctx, evmObj) - s.Require().NoError(err) - s.Require().Equal(amountToSendC, balance) + bankGensisState := deps.App.BankKeeper.ExportGenesis(deps.Ctx) + + s.T().Log("Init genesis from the exported state for modules [auth, bank, evm]") + { + deps = evmtest.NewTestDeps() + deps.App.AccountKeeper.InitGenesis(deps.Ctx, *authGenesisState) + deps.App.BankKeeper.InitGenesis(deps.Ctx, bankGensisState) + evmmodule.InitGenesis(deps.Ctx, deps.EvmKeeper, deps.App.AccountKeeper, *evmGenesisState) + + s.T().Log("Assert balances for users A, B, C, the sender and the EVM") + assertBalsAfterConvert(deps) + + s.T().Log("Check that fungible token mapping is in place") + iter := deps.EvmKeeper.FunTokens.Indexes.BankDenom.ExactMatch(deps.Ctx, ft.BankDenom) + funTokens := deps.EvmKeeper.FunTokens.Collect(deps.Ctx, iter) + s.Require().Len(funTokens, 1) + s.Equal(ft.Erc20Addr.Hex(), funTokens[0].Erc20Addr.String()) + s.Equal(ft.BankDenom, funTokens[0].BankDenom) + s.False(funTokens[0].IsMadeFromCoin) + } } diff --git a/x/evm/evmtest/erc20.go b/x/evm/evmtest/erc20.go index fa6f30fd81..a29b2f9b9e 100644 --- a/x/evm/evmtest/erc20.go +++ b/x/evm/evmtest/erc20.go @@ -1,6 +1,7 @@ package evmtest import ( + "fmt" "math/big" "testing" @@ -11,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + "github.com/NibiruChain/nibiru/v2/app/appconst" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" "github.com/NibiruChain/nibiru/v2/x/evm" @@ -120,22 +122,57 @@ func CreateFunTokenForBankCoin( return funtoken } +type BalanceAssertNIBI struct { + Account gethcommon.Address + BalanceBank *big.Int + BalanceERC20 *big.Int + Description string + EvmObj *vm.EVM +} + +func (bals BalanceAssertNIBI) Assert(t *testing.T, deps TestDeps) { + var evmObj *vm.EVM + if bals.EvmObj != nil { + evmObj = bals.EvmObj + } else { + evmObj, _ = deps.NewEVM() + } + + AssertBankBalanceEqualWithDescription( + t, deps, appconst.BondDenom, bals.Account, bals.BalanceBank, + "(bank)"+bals.Description, + ) + if bals.BalanceERC20 != nil { + erc20 := deps.EvmKeeper.GetParams(deps.Ctx).CanonicalWnibi + AssertERC20BalanceEqualWithDescription( + t, deps, evmObj, erc20.Address, bals.Account, bals.BalanceERC20, + "(erc20_WNIBI)"+bals.Description, + ) + } +} + type FunTokenBalanceAssert struct { - FunToken evm.FunToken Account gethcommon.Address + FunToken evm.FunToken BalanceBank *big.Int BalanceERC20 *big.Int Description string } func (bals FunTokenBalanceAssert) Assert(t *testing.T, deps TestDeps, evmObj *vm.EVM) { + appendDescription := func(prefix string) string { + if bals.Description == "" { + return prefix + } + return fmt.Sprintf("%s: %s", prefix, bals.Description) + } AssertERC20BalanceEqualWithDescription( t, deps, evmObj, bals.FunToken.Erc20Addr.Address, bals.Account, bals.BalanceERC20, - bals.Description, + appendDescription("(erc20)"), ) AssertBankBalanceEqualWithDescription( t, deps, bals.FunToken.BankDenom, bals.Account, bals.BalanceBank, - bals.Description, + appendDescription("(bank)"), ) } diff --git a/x/evm/evmtest/genesis_inject.go b/x/evm/evmtest/genesis_inject.go new file mode 100644 index 0000000000..f341ab2ae8 --- /dev/null +++ b/x/evm/evmtest/genesis_inject.go @@ -0,0 +1,89 @@ +package evmtest + +import ( + "encoding/json" + "fmt" + + auth "github.com/cosmos/cosmos-sdk/x/auth/types" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" +) + +var ( + // Returns WNIBI.sol from Nibiru mainnet as an [evm.GenesisAccount] that can be + // imported to any instance of Nibiru. + WNIBI_GENESIS_EVM_ACC evm.GenesisAccount + + WNIBI_GENESIS_AUTH_ACC eth.EthAccount +) + +func init() { + { + var genAcc evm.GenesisAccount + err := json.Unmarshal([]byte(WNIBI_GENESIS_ACC_STRING), &genAcc) + if err != nil { + panic(fmt.Errorf("failed to unpack WNIBI_GENESIS_ACC_STRING as evm.GenesisAccount: %w", err)) + } + WNIBI_GENESIS_EVM_ACC = genAcc + } + + { + /* Comes from this account in the Auth genesis state + { + "@type": "/eth.types.v1.EthAccount", + "base_account": { + "address": "nibi1pjk0v60cg347e2pxjyarc6uk4n2tq25hhymgg9", + "pub_key": null, + "account_number": "13", + "sequence": "0" + }, + "code_hash": "0xffb88e0eb48147949565e65de3ec8a54b746214da7b9dd5b9a8a3ae7df46193b" + } + */ + genAcc := eth.EthAccount{ + BaseAccount: &auth.BaseAccount{ + Address: eth.EthAddrToNibiruAddr(appconst.MAINNET_WNIBI_ADDR).String(), + PubKey: nil, + AccountNumber: 13, + Sequence: 0, + }, + CodeHash: "0xffb88e0eb48147949565e65de3ec8a54b746214da7b9dd5b9a8a3ae7df46193b", + } + WNIBI_GENESIS_AUTH_ACC = genAcc + } +} + +const WNIBI_GENESIS_AUTH_ACC_STRING = ` +{ + "@type": "/eth.types.v1.EthAccount", + "base_account": { + "address": "nibi1pjk0v60cg347e2pxjyarc6uk4n2tq25hhymgg9", + "pub_key": null, + "account_number": "13", + "sequence": "0" + }, + "code_hash": "0xffb88e0eb48147949565e65de3ec8a54b746214da7b9dd5b9a8a3ae7df46193b" +} +` + +const WNIBI_GENESIS_ACC_STRING = ` +{ + "address": "0x0CaCF669f8446BeCA826913a3c6B96aCD4b02a97", + "code": "6080604052600436106100a05760003560e01c8063313ce56711610064578063313ce567146101b257806370a08231146101dd57806395d89b411461021a578063a9059cbb14610245578063d0e30db014610282578063dd62ed3e1461028c576100af565b806306fdde03146100b9578063095ea7b3146100e457806318160ddd1461012157806323b872dd1461014c5780632e1a7d4d14610189576100af565b366100af576100ad6102c9565b005b6100b76102c9565b005b3480156100c557600080fd5b506100ce61036f565b6040516100db9190610b18565b60405180910390f35b3480156100f057600080fd5b5061010b60048036038101906101069190610bd3565b6103fd565b6040516101189190610c2e565b60405180910390f35b34801561012d57600080fd5b506101366104ef565b6040516101439190610c58565b60405180910390f35b34801561015857600080fd5b50610173600480360381019061016e9190610c73565b6104f7565b6040516101809190610c2e565b60405180910390f35b34801561019557600080fd5b506101b060048036038101906101ab9190610cc6565b61085b565b005b3480156101be57600080fd5b506101c7610995565b6040516101d49190610d0f565b60405180910390f35b3480156101e957600080fd5b5061020460048036038101906101ff9190610d2a565b6109a8565b6040516102119190610c58565b60405180910390f35b34801561022657600080fd5b5061022f6109c0565b60405161023c9190610b18565b60405180910390f35b34801561025157600080fd5b5061026c60048036038101906102679190610bd3565b610a4e565b6040516102799190610c2e565b60405180910390f35b61028a6102c9565b005b34801561029857600080fd5b506102b360048036038101906102ae9190610d57565b610a63565b6040516102c09190610c58565b60405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546103189190610dc6565b925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040516103659190610c58565b60405180910390a2565b6000805461037c90610e29565b80601f01602080910402602001604051908101604052809291908181526020018280546103a890610e29565b80156103f55780601f106103ca576101008083540402835291602001916103f5565b820191906000526020600020905b8154815290600101906020018083116103d857829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516104dd9190610c58565b60405180910390a36001905092915050565b600047905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561054557600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415801561061d57507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b1561073f5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156106ab57600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107379190610e5a565b925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461078e9190610e5a565b9250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107e49190610dc6565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516108489190610c58565b60405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156108a757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546108f69190610e5a565b925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f19350505050158015610943573d6000803e3d6000fd5b503373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658260405161098a9190610c58565b60405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b600180546109cd90610e29565b80601f01602080910402602001604051908101604052809291908181526020018280546109f990610e29565b8015610a465780601f10610a1b57610100808354040283529160200191610a46565b820191906000526020600020905b815481529060010190602001808311610a2957829003601f168201915b505050505081565b6000610a5b3384846104f7565b905092915050565b6004602052816000526040600020602052806000526040600020600091509150505481565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ac2578082015181840152602081019050610aa7565b60008484015250505050565b6000601f19601f8301169050919050565b6000610aea82610a88565b610af48185610a93565b9350610b04818560208601610aa4565b610b0d81610ace565b840191505092915050565b60006020820190508181036000830152610b328184610adf565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b6a82610b3f565b9050919050565b610b7a81610b5f565b8114610b8557600080fd5b50565b600081359050610b9781610b71565b92915050565b6000819050919050565b610bb081610b9d565b8114610bbb57600080fd5b50565b600081359050610bcd81610ba7565b92915050565b60008060408385031215610bea57610be9610b3a565b5b6000610bf885828601610b88565b9250506020610c0985828601610bbe565b9150509250929050565b60008115159050919050565b610c2881610c13565b82525050565b6000602082019050610c436000830184610c1f565b92915050565b610c5281610b9d565b82525050565b6000602082019050610c6d6000830184610c49565b92915050565b600080600060608486031215610c8c57610c8b610b3a565b5b6000610c9a86828701610b88565b9350506020610cab86828701610b88565b9250506040610cbc86828701610bbe565b9150509250925092565b600060208284031215610cdc57610cdb610b3a565b5b6000610cea84828501610bbe565b91505092915050565b600060ff82169050919050565b610d0981610cf3565b82525050565b6000602082019050610d246000830184610d00565b92915050565b600060208284031215610d4057610d3f610b3a565b5b6000610d4e84828501610b88565b91505092915050565b60008060408385031215610d6e57610d6d610b3a565b5b6000610d7c85828601610b88565b9250506020610d8d85828601610b88565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610dd182610b9d565b9150610ddc83610b9d565b9250828201905080821115610df457610df3610d97565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610e4157607f821691505b602082108103610e5457610e53610dfa565b5b50919050565b6000610e6582610b9d565b9150610e7083610b9d565b9250828203905081811115610e8857610e87610d97565b5b9291505056fea2646970667358221220e2bbe4e79fbff010e16625cff639a72785d4dbf62ac4a60275168b532643766464736f6c63430008180033", + "storage": [ + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "value": "0x57726170706564204e696269727500000000000000000000000000000000001c" + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "value": "0x574e49424900000000000000000000000000000000000000000000000000000a" + }, + { + "key": "0x0000000000000000000000000000000000000000000000000000000000000002", + "value": "0x0000000000000000000000000000000000000000000000000000000000000012" + } + ] +}` diff --git a/x/evm/evmtest/test_deps.go b/x/evm/evmtest/test_deps.go index a95a486e27..7b380d3bef 100644 --- a/x/evm/evmtest/test_deps.go +++ b/x/evm/evmtest/test_deps.go @@ -2,17 +2,25 @@ package evmtest import ( "context" + "fmt" + "math/big" sdk "github.com/cosmos/cosmos-sdk/types" gethcommon "github.com/ethereum/go-ethereum/common" + core "github.com/ethereum/go-ethereum/core" gethcore "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers/logger" + "github.com/stretchr/testify/suite" + + "github.com/NibiruChain/collections" "github.com/NibiruChain/nibiru/v2/app" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/keeper" "github.com/NibiruChain/nibiru/v2/x/evm/statedb" ) @@ -53,7 +61,7 @@ func (deps TestDeps) NewEVM() (*vm.EVM, *statedb.StateDB) { deps.Ctx, MOCK_GETH_MESSAGE, deps.EvmKeeper.GetEVMConfig(deps.Ctx), - logger.NewStructLogger(&logger.Config{Debug: true}).Hooks(), + logger.NewStructLogger(&logger.Config{Debug: false}).Hooks(), stateDB, ) return evmObj, stateDB @@ -78,3 +86,95 @@ func (deps *TestDeps) GethSigner() gethcore.Signer { func (deps TestDeps) GoCtx() context.Context { return sdk.WrapSDKContext(deps.Ctx) } + +func (deps *TestDeps) DeployWNIBI(s *suite.Suite) error { + var ( + ctx = deps.Ctx + wnibiAddr = deps.EvmKeeper.GetParams(ctx).CanonicalWnibi.Address + evmAccState = deps.EvmKeeper.EvmState.AccState + ) + + evmModuleNonce := deps.EvmKeeper.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS) + tempWnibiAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, evmModuleNonce) + newCompiledContract := embeds.SmartContract_WNIBI + // empty method name means deploy with the constructor + packedArgs, err := newCompiledContract.ABI.Pack("") + if err != nil { + return fmt.Errorf("failed to pack ABI args: %w", err) + } + contractInput := append(newCompiledContract.Bytecode, packedArgs...) + + // Rebuild evmObj with new evmMsg for contract creation. + // Note that most of these fields are unused when we create EVM instances + // outside of an EthereumTx. + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + To: nil, // To is blank -> deploy contract + From: evm.EVM_MODULE_ADDRESS, // From is the deployer + Nonce: evmModuleNonce, + Value: unusedBigInt, // amount + GasLimit: keeper.Erc20GasLimitDeploy, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, // This manages the constructor args + AccessList: gethcore.AccessList{}, + SkipNonceChecks: false, + SkipFromEOACheck: false, + } + stateDB := deps.EvmKeeper.Bank.StateDB + if stateDB == nil { + stateDB = deps.EvmKeeper.NewStateDB(ctx, deps.EvmKeeper.TxConfig(ctx, gethcommon.Hash{})) + } + defer func() { + deps.EvmKeeper.Bank.StateDB = nil + }() + evmObj := deps.EvmKeeper.NewEVM(ctx, evmMsg, deps.EvmKeeper.GetEVMConfig(ctx), nil, stateDB) + + evmResp, err := deps.EvmKeeper.CallContractWithInput( + ctx, evmObj, evmMsg.From, nil, true /*commit*/, contractInput, + keeper.Erc20GasLimitDeploy, evmMsg.Value, + ) + if err != nil { + return fmt.Errorf("failed to deploy WNIBI contract: %w", err) + } else if len(evmResp.VmError) > 0 { + return fmt.Errorf("VM Error deploying WNIBI: %s", evmResp.VmError) + } + + _ = ctx.EventManager().EmitTypedEvents( + &evm.EventContractDeployed{ + Sender: evmMsg.From.Hex(), + ContractAddr: tempWnibiAddr.Hex(), + }, + ) + + s.T().Logf("Set WNIBI bytecode hash at address %s", wnibiAddr) + tempWnibiAcc := deps.EvmKeeper.GetAccount(ctx, tempWnibiAddr) + wnibiAcc := statedb.NewEmptyAccount() + wnibiAcc.CodeHash = tempWnibiAcc.CodeHash + err = deps.EvmKeeper.SetAccount(ctx, wnibiAddr, *wnibiAcc) + if err != nil { + return fmt.Errorf("overwrite of contract bytecode failed: %w", err) + } + + s.T().Log("Set WNIBI contract state") + { + iter := evmAccState.Iterate(ctx, collections.PairRange[gethcommon.Address, gethcommon.Hash]{}.Prefix(tempWnibiAddr)) + defer iter.Close() + for ; iter.Valid(); iter.Next() { + evmAccState.Insert( + ctx, + collections.Join(wnibiAddr, iter.Key().K2()), + iter.Value(), + ) + } + } + _ = ctx.EventManager().EmitTypedEvents( + &evm.EventContractDeployed{ + Sender: evmMsg.From.Hex(), + ContractAddr: wnibiAddr.Hex(), + }, + ) + + return nil +} diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go index 46c30f2d23..20511b3e03 100644 --- a/x/evm/genesis_test.go +++ b/x/evm/genesis_test.go @@ -113,6 +113,7 @@ func (s *GenesisSuite) TestValidateGenesis() { { name: "empty genesis", genState: &evm.GenesisState{}, + wantErr: "ParamsError: evm.Params.CanonicalWnibi cannot be the zero address", }, { name: "copied genesis", @@ -129,6 +130,7 @@ func (s *GenesisSuite) TestValidateGenesis() { Address: gethcommon.Address{}.String(), // zero address }, }, + Params: evm.DefaultParams(), }, }, { @@ -173,10 +175,11 @@ func (s *GenesisSuite) TestValidateGenesis() { }, }, { - name: "happy: empty params", + name: "empty params", genState: &evm.GenesisState{ Params: evm.Params{}, }, + wantErr: "ParamsError", }, } diff --git a/x/evm/keeper/call_contract.go b/x/evm/keeper/call_contract.go index 1b960d0e00..1b2b579a38 100644 --- a/x/evm/keeper/call_contract.go +++ b/x/evm/keeper/call_contract.go @@ -37,6 +37,7 @@ func (k Keeper) CallContractWithInput( commit bool, contractInput []byte, gasLimit uint64, + weiValue *big.Int, ) (evmResp *evm.MsgEthereumTxResponse, err error) { // This is a `defer` pattern to add behavior that runs in the case that the // error is non-nil, creating a concise way to add extra information. @@ -44,11 +45,17 @@ func (k Keeper) CallContractWithInput( nonce := k.GetAccNonce(ctx, fromAcc) unusedBigInt := big.NewInt(0) + var value *big.Int + if weiValue == nil { + value = unusedBigInt + } else { + value = weiValue + } evmMsg := core.Message{ To: contract, From: fromAcc, Nonce: nonce, - Value: unusedBigInt, // amount + Value: value, // amount GasLimit: gasLimit, GasPrice: unusedBigInt, GasFeeCap: unusedBigInt, diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go index 3a449d5fca..dda1ce352c 100644 --- a/x/evm/keeper/erc20.go +++ b/x/evm/keeper/erc20.go @@ -20,9 +20,11 @@ import ( const ( // Erc20GasLimitDeploy only used internally when deploying ERC20Minter. Erc20GasLimitDeploy uint64 = 2_500_000 + // Erc20GasLimitQuery used only for querying name, symbol and decimals // Cannot be heavy. Only if the contract is malicious. Erc20GasLimitQuery uint64 = 100_000 + // Erc20GasLimitExecute used for transfer, mint and burn. // All must not exceed 200_000 Erc20GasLimitExecute uint64 = 200_000 @@ -74,7 +76,7 @@ func (e erc20Calls) Mint( if err != nil { return nil, err } - return e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute)) + return e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute), nil) } /* @@ -100,7 +102,7 @@ func (e erc20Calls) Transfer( if err != nil { return balanceIncrease, nil, err } - resp, err = e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute)) + resp, err = e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute), nil) if err != nil { return balanceIncrease, nil, err } @@ -172,7 +174,7 @@ func (e erc20Calls) Burn( if err != nil { return nil, err } - return e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute)) + return e.CallContractWithInput(ctx, evmObj, sender, &erc20Contract, false /*commit*/, contractInput, getCallGasWithLimit(ctx, Erc20GasLimitExecute), nil) } func (e erc20Calls) LoadERC20Name( @@ -212,6 +214,7 @@ func (e erc20Calls) loadERC20String( false, input, getCallGasWithLimit(ctx, Erc20GasLimitQuery), + nil, ) if err != nil { return out, err @@ -255,6 +258,7 @@ func (e erc20Calls) loadERC20Uint8( false, input, getCallGasWithLimit(ctx, Erc20GasLimitQuery), + nil, ) if err != nil { return out, err @@ -298,6 +302,7 @@ func (e erc20Calls) LoadERC20BigInt( false, input, getCallGasWithLimit(ctx, Erc20GasLimitQuery), + nil, ) if err != nil { return nil, err diff --git a/x/evm/keeper/funtoken_from_coin.go b/x/evm/keeper/funtoken_from_coin.go index 1933a2fade..f28b8b5543 100644 --- a/x/evm/keeper/funtoken_from_coin.go +++ b/x/evm/keeper/funtoken_from_coin.go @@ -108,7 +108,7 @@ func (k *Keeper) deployERC20ForBankCoin( }() evmObj := k.NewEVM(ctx, evmMsg, evmCfg, nil /*tracer*/, stateDB) evmResp, err := k.CallContractWithInput( - ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy, + ctx, evmObj, evm.EVM_MODULE_ADDRESS, nil, true /*commit*/, input, Erc20GasLimitDeploy, nil, ) if err != nil { return gethcommon.Address{}, sdkioerrors.Wrap(err, "failed to deploy ERC20 contract") diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go index 4da83559dd..d84a8231ef 100644 --- a/x/evm/keeper/funtoken_from_coin_test.go +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -3,14 +3,11 @@ package keeper_test import ( "math/big" - "testing" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/suite" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/common/testutil" @@ -19,10 +16,9 @@ import ( "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" "github.com/NibiruChain/nibiru/v2/x/evm/keeper" - "github.com/NibiruChain/nibiru/v2/x/evm/precompile" ) -func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { +func (s *SuiteFunToken) TestCreateFunTokenFromCoin() { deps := evmtest.NewTestDeps() s.Run("Compute contract address. FindERC20 should fail", func() { evmObj, _ := deps.NewEVM() @@ -191,335 +187,6 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { }) } -func (s *FunTokenFromCoinSuite) TestConvertCoinToEvmAndBack() { - deps := evmtest.NewTestDeps() - evmObj, _ := deps.NewEVM() - alice := evmtest.NewEthPrivAcc() - - // Initial setup - funToken := s.fundAndCreateFunToken(deps, 100) - - s.T().Log("Convert bank coin to erc-20") - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()).WithEventManager(sdk.NewEventManager()) - _, err := deps.EvmKeeper.ConvertCoinToEvm( - sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10)), - ToEthAddr: eth.EIP55Addr{ - Address: alice.EthAddr, - }, - }, - ) - s.Require().NoError(err) - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - - s.T().Log("Check typed event ConvertCoinToEvm") - testutil.RequireContainsTypedEvent( - s.T(), - deps.Ctx, - &evm.EventConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - Erc20ContractAddress: funToken.Erc20Addr.String(), - ToEthAddr: alice.EthAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10)), - }, - ) - - s.T().Log("Check typed event EventTxLog with Transfer event") - emptyHash := gethcommon.BytesToHash(make([]byte, 32)).Hex() - signature := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")).Hex() - fromAddress := emptyHash // Mint - toAddress := gethcommon.BytesToHash(alice.EthAddr.Bytes()).Hex() - amountBase64 := gethcommon.LeftPadBytes(big.NewInt(10).Bytes(), 32) - - testutil.RequireContainsTypedEvent( - s.T(), - deps.Ctx, - &evm.EventTxLog{ - Logs: []evm.Log{ - { - Address: funToken.Erc20Addr.Hex(), - Topics: []string{ - signature, - fromAddress, - toAddress, - }, - Data: amountBase64, - BlockNumber: 1, // we are in simulation, no real block numbers or tx hashes - TxHash: emptyHash, - TxIndex: 0, - BlockHash: emptyHash, - Index: 1, - Removed: false, - }, - }, - }, - ) - - // Check 1: module balance - moduleBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom) - s.Require().Equal(sdk.NewInt(10), moduleBalance.Amount) - - // Check 2: Sender balance - senderBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom) - s.Require().Equal(sdk.NewInt(90), senderBalance.Amount) - - // Check 3: erc-20 balance - balance, err := deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx, evmObj) - s.Require().NoError(err) - s.Require().Zero(balance.Cmp(big.NewInt(10))) - - s.Run("sad: Convert more bank coin to erc-20, insufficient funds", func() { - _, err = deps.EvmKeeper.ConvertCoinToEvm( - sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(100)), - ToEthAddr: eth.EIP55Addr{ - Address: alice.EthAddr, - }, - }, - ) - s.Require().ErrorContains(err, "insufficient funds") - }) - - s.T().Log("Convert erc-20 to back to bank coin") - contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( - "sendToBank", - funToken.Erc20Addr.Address, - big.NewInt(10), - deps.Sender.NibiruAddr.String(), - ) - s.Require().NoError(err) - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - evmObj, _ = deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - alice.EthAddr, // from - &precompile.PrecompileAddr_FunToken, // to - true, // commit - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().NoError(err) - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - - // Check 1: module balance - moduleBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), evm.EVMBankDenom) - s.Require().True(moduleBalance.Amount.Equal(sdk.ZeroInt())) - - // Check 2: Sender balance - senderBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom) - s.Require().Equal(sdk.NewInt(100), senderBalance.Amount) - - // Check 3: erc-20 balance - balance, err = deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx, evmObj) - s.Require().NoError(err) - s.Require().Equal("0", balance.String()) - - s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds") - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - evmObj, _ = deps.NewEVM() - _, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - alice.EthAddr, // from - &precompile.PrecompileAddr_FunToken, // to - true, // commit - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().ErrorContains(err, "transfer amount exceeds balance") - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) -} - -// TestNativeSendThenPrecompileSend tests a race condition where the state DB -// commit may overwrite the state after the precompile execution, potentially -// causing a loss of funds. -// -// The order of operations is to: -// 1. Create a funtoken mapping from NIBI, a bank coin. -// 2. Use a test Solidity contract to perform two transfers in a single call: a -// transfer of NIBI with native send and a precompile "IFunToken.sendToBank" -// transfer for the same asset. -// -// INITIAL STATE: -// - Test contract funds: 10 NIBI, 10 WNIBI -// CONTRACT CALL: -// - Sends 10 NIBI natively and 10 WNIBI -> NIBI to Alice using precompile -// EXPECTED: -// - Test contract funds: 0 NIBI, 0 WNIBI -// - Alice: 20 NIBI -// - Module account: 0 NIBI escrowed -func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { - deps := evmtest.NewTestDeps() - evmObj, _ := deps.NewEVM() - bankDenom := evm.EVMBankDenom - - // Initial setup - sendAmt := big.NewInt(10) - funtoken := s.fundAndCreateFunToken(deps, sendAmt.Int64()) - - s.T().Log("Deploy Test Contract") - deployResp, err := evmtest.DeployContract( - &deps, - embeds.SmartContract_TestNativeSendThenPrecompileSendJson, - funtoken.Erc20Addr.Address, - ) - s.Require().NoError(err) - - testContractAddr := deployResp.ContractAddr - testContractNibiAddr := eth.EthAddrToNibiruAddr(testContractAddr) - - s.T().Log("Give the test contract 10 NIBI (native)") - s.Require().NoError(testapp.FundAccount( - deps.App.BankKeeper, - deps.Ctx, - testContractNibiAddr, - sdk.NewCoins(sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)))), - ) - evmtest.AssertBankBalanceEqualWithDescription( - s.T(), deps, bankDenom, testContractAddr, sendAmt, "expect 10 balance", - ) - evmtest.AssertBankBalanceEqualWithDescription( - s.T(), deps, bankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", - ) - - s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") - _, err = deps.EvmKeeper.ConvertCoinToEvm( - sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)), - ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, - }, - ) - s.Require().NoError(err) - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: testContractAddr, - BalanceBank: sendAmt, - BalanceERC20: sendAmt, - }.Assert(s.T(), deps, evmObj) - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: evm.EVM_MODULE_ADDRESS, - BalanceBank: sendAmt, - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - - // Alice hex and Alice bech32 is the same address in different representation, - // so funds are expected to be available in Alice's bank wallet - alice := evmtest.NewEthPrivAcc() - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: alice.EthAddr, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - - s.T().Log("call test contract") - newSendAmtSendToBank := new(big.Int).Quo(sendAmt, big.NewInt(2)) - newSendAmtEvmTransfer := evm.NativeToWei(newSendAmtSendToBank) - - contractInput, err := embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI.Pack( - "nativeSendThenPrecompileSend", - alice.EthAddr, /*to*/ - newSendAmtEvmTransfer, /*amount*/ - alice.NibiruAddr.String(), /*to*/ - newSendAmtSendToBank, /*amount*/ - ) - s.Require().NoError(err) - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - evmObj, _ = deps.NewEVM() - evmResp, err := deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, - &testContractAddr, - true, - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().NoError(err) - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - s.Require().NotZero(evmResp.GasUsed) - s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") - s.Empty(evmResp.VmError) - gasUsedFor2Ops := evmResp.GasUsed - - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: alice.EthAddr, - BalanceBank: new(big.Int).Mul( - newSendAmtSendToBank, big.NewInt(2)), - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: testContractAddr, - BalanceBank: big.NewInt(5), - BalanceERC20: big.NewInt(5), - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: evm.EVM_MODULE_ADDRESS, - BalanceBank: big.NewInt(5), - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - - contractInput, err = embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI.Pack( - "justPrecompileSend", - alice.NibiruAddr.String(), /*to*/ - newSendAmtSendToBank, /*amount*/ - ) - s.Require().NoError(err) - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - evmObj, _ = deps.NewEVM() - evmResp, err = deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, - &testContractAddr, - true, - contractInput, - evmtest.DefaultEthCallGasLimit, - ) - s.Require().NoError(err) - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - s.Require().NotZero(evmResp.GasUsed) - s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") - s.Empty(evmResp.VmError) - gasUsedFor1Op := evmResp.GasUsed - - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: alice.EthAddr, - BalanceBank: new(big.Int).Mul( - newSendAmtSendToBank, big.NewInt(3)), - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: testContractAddr, - BalanceBank: big.NewInt(5), - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funtoken, - Account: evm.EVM_MODULE_ADDRESS, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(0), - }.Assert(s.T(), deps, evmObj) - s.Require().Greater(gasUsedFor2Ops, gasUsedFor1Op, "2 operations should consume more gas") -} - // TestERC20TransferThenPrecompileSend // 1. Creates a funtoken from coin. // 2. Using the test contract, performs two sends in a single call: a erc20 @@ -528,14 +195,17 @@ func (s *FunTokenFromCoinSuite) TestNativeSendThenPrecompileSend() { // potentially causing an infinite minting of funds. // // INITIAL STATE: -// - Test contract funds: 10 WNIBI +// - Test contract funds: 10_000_000 TEST (Bank) +// // CONTRACT CALL: -// - Sends 1 WNIBI to Alice using erc20 transfer and 9 WNIBI -> NIBI to Alice using precompile +// - Sends 1e6 TEST to Alice using erc20 transfer +// - and send 9e6 TEST to Alice using precompile +// // EXPECTED: -// - Test contract funds: 0 WNIBI -// - Alice: 1 WNIBI, 9 NIBI -// - Module account: 1 NIBI escrowed (which Alice holds as 1 WNIBI) -func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { +// - Test contract funds: 0 EVM +// - Alice: 1 EVM, 9 BC +// - Module account: 1 BC escrowed (which Alice holds as 1 EVM) +func (s *SuiteFunToken) TestERC20TransferThenPrecompileSend() { deps := evmtest.NewTestDeps() evmObj, _ := deps.NewEVM() @@ -550,12 +220,12 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { s.Require().NoError(err) testContractAddr := deployResp.ContractAddr - s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") + s.T().Logf("Convert bank coin to erc-20: give test contract %d %s (erc20)", int64(10e6), funToken.BankDenom) _, err = deps.EvmKeeper.ConvertCoinToEvm( sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), + BankCoin: sdk.NewCoin(funToken.BankDenom, sdk.NewInt(10e6)), ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, }, ) @@ -605,6 +275,7 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { true, // commit contractInput, 10_000_000, // gas limit + nil, ) s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) @@ -636,234 +307,9 @@ func (s *FunTokenFromCoinSuite) TestERC20TransferThenPrecompileSend() { }.Assert(s.T(), deps, evmObj) } -// TestPrecompileSelfCallRevert -// 1. Creates a funtoken from coin. -// 2. Using the test contract, creates another instance of itself, calls the precompile method and then force reverts. -// It tests a race condition where the state DB commit -// may save the wrong state before the precompile execution, not revert it entirely, -// potentially causing an infinite mint of funds. -// -// INITIAL STATE: -// - Test contract funds: 10 NIBI, 10 WNIBI -// CONTRACT CALL: -// - Sends 1 NIBI to Alice using native send and 1 WNIBI -> NIBI to Charles using precompile -// EXPECTED: -// - all changes reverted -// - Test contract funds: 10 NIBI, 10 WNIBI -// - Alice: 0 NIBI -// - Charles: 0 NIBI -// - Module account: 10 NIBI escrowed (which Test contract holds as 10 WNIBI) -func (s *FunTokenFromCoinSuite) TestPrecompileSelfCallRevert() { - deps := evmtest.NewTestDeps() - - // Initial setup - funToken := s.fundAndCreateFunToken(deps, 10e6) - - s.T().Log("Deploy Test Contract") - deployResp, err := evmtest.DeployContract( - &deps, - embeds.SmartContract_TestPrecompileSelfCallRevert, - funToken.Erc20Addr.Address, - ) - s.Require().NoError(err) - testContractAddr := deployResp.ContractAddr - - s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") - _, err = deps.EvmKeeper.ConvertCoinToEvm( - sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), - ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, - }, - ) - s.Require().NoError(err) - - s.T().Log("Give the test contract 10 NIBI (native)") - s.Require().NoError(testapp.FundAccount( - deps.App.BankKeeper, - deps.Ctx, - eth.EthAddrToNibiruAddr(testContractAddr), - sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6))), - )) - - evmObj, _ := deps.NewEVM() - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: testContractAddr, - BalanceBank: big.NewInt(10e6), - BalanceERC20: big.NewInt(10e6), - Description: "Initial contract state sanity check: 10 NIBI / 10 WNIBI", - }.Assert(s.T(), deps, evmObj) - - // Create Alice and Charles. Contract will try to send Alice native coins and - // send Charles tokens via sendToBank - alice := evmtest.NewEthPrivAcc() - charles := evmtest.NewEthPrivAcc() - - s.T().Log("call test contract") - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - evmObj, _ = deps.NewEVM() - contractInput, err := embeds.SmartContract_TestPrecompileSelfCallRevert.ABI.Pack( - "selfCallTransferFunds", - alice.EthAddr, - evm.NativeToWei(big.NewInt(1e6)), - charles.NibiruAddr.String(), - big.NewInt(9e6), - ) - s.Require().NoError(err) - evpResp, err := deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, - &testContractAddr, - true, - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().NoError(err) - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - s.Require().NotZero(evpResp.GasUsed) - s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: alice.EthAddr, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(0), - Description: "Alice has 0 NIBI / 0 WNIBI", - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: charles.EthAddr, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(0), - Description: "Charles has 0 NIBI / 0 WNIBI", - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: testContractAddr, - BalanceBank: big.NewInt(10e6), - BalanceERC20: big.NewInt(10e6), - Description: "Test contract has 10 NIBI / 10 WNIBI", - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: evm.EVM_MODULE_ADDRESS, - BalanceBank: big.NewInt(10e6), - BalanceERC20: big.NewInt(0), - Description: "Module account has 10 NIBI escrowed", - }.Assert(s.T(), deps, evmObj) -} - -// TestPrecompileSelfCallRevert -// 1. Creates a funtoken from coin. -// 2. Calls the test contract -// a. sendToBank -// b. erc20 transfer -// -// INITIAL STATE: -// - Test contract funds: 10 WNIBI -// CONTRACT CALL: -// - Sends 10 WNIBI to Alice, and try to send 1 NIBI to Bob -// EXPECTED: -// - all changes reverted because of not enough balance -// - Test contract funds: 10 WNIBI -// - Alice: 10 WNIBI -// - Bob: 0 NIBI -// - Module account: 10 NIBI escrowed (which Test contract holds as 10 WNIBI) -func (s *FunTokenFromCoinSuite) TestPrecompileSendToBankThenErc20Transfer() { - deps := evmtest.NewTestDeps() - - // Initial setup - funToken := s.fundAndCreateFunToken(deps, 10e6) - - s.T().Log("Deploy Test Contract") - deployResp, err := evmtest.DeployContract( - &deps, - embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer, - funToken.Erc20Addr.Address, - deps.Sender.NibiruAddr.String(), - ) - s.Require().NoError(err) - testContractAddr := deployResp.ContractAddr - - s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") - _, err = deps.EvmKeeper.ConvertCoinToEvm( - sdk.WrapSDKContext(deps.Ctx), - &evm.MsgConvertCoinToEvm{ - Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(10e6)), - ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, - }, - ) - s.Require().NoError(err) - - // Create Alice and Bob. Contract will try to send Alice native coins and - // send Bob ERC20 tokens. - alice := evmtest.NewEthPrivAcc() - bob := evmtest.NewEthPrivAcc() - - s.T().Log("call test contract") - contractInput, err := embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer.ABI.Pack( - "attack", - ) - s.Require().NoError(err) - deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - evmObj, _ := deps.NewEVM() - evpResp, err := deps.EvmKeeper.CallContractWithInput( - deps.Ctx, - evmObj, - deps.Sender.EthAddr, - &testContractAddr, - true, - contractInput, - evmtest.FunTokenGasLimitSendToEvm, - ) - s.Require().ErrorContains(err, "execution reverted") - s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) - s.Require().NotZero(evpResp.GasUsed) - s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: alice.EthAddr, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(0), - Description: "Alice has 0 NIBI / 0 WNIBI", - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: bob.EthAddr, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(0), - Description: "Bob has 0 NIBI / 0 WNIBI", - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: testContractAddr, - BalanceBank: big.NewInt(0), - BalanceERC20: big.NewInt(10e6), - Description: "Test contract has 10 NIBI / 10 WNIBI", - }.Assert(s.T(), deps, evmObj) - - evmtest.FunTokenBalanceAssert{ - FunToken: funToken, - Account: evm.EVM_MODULE_ADDRESS, - BalanceBank: big.NewInt(10e6), - BalanceERC20: big.NewInt(0), - Description: "Module account has 10 NIBI escrowed", - }.Assert(s.T(), deps, evmObj) -} - // fundAndCreateFunToken creates initial setup for tests -func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, unibiAmount int64) evm.FunToken { - bankDenom := evm.EVMBankDenom +func (s *SuiteFunToken) fundAndCreateFunToken(deps evmtest.TestDeps, bankAmount int64) evm.FunToken { + bankDenom := "testfuntoken" s.T().Log("Setup: Create a coin in the bank state") deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bank.Metadata{ @@ -873,23 +319,27 @@ func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, uni Exponent: 0, }, { - Denom: "NIBI", + Denom: "TEST", Exponent: 6, }, }, Base: bankDenom, - Display: "NIBI", - Name: "NIBI", - Symbol: "NIBI", + Display: "TEST", + Name: "TEST", + Symbol: "TEST", }) s.T().Log("Give the sender funds for funtoken creation and funding test contract") + tokensToFund := deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx).Add( + sdk.NewCoin(bankDenom, sdk.NewInt(bankAmount)), + ) s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, - deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx).Add(sdk.NewCoin(bankDenom, sdk.NewInt(unibiAmount))), + tokensToFund, )) + s.T().Logf("Funded %s", tokensToFund) s.T().Log("Create FunToken from coin") createFunTokenResp, err := deps.EvmKeeper.CreateFunToken( @@ -903,11 +353,3 @@ func (s *FunTokenFromCoinSuite) fundAndCreateFunToken(deps evmtest.TestDeps, uni return createFunTokenResp.FuntokenMapping } - -type FunTokenFromCoinSuite struct { - suite.Suite -} - -func TestFunTokenFromCoinSuite(t *testing.T) { - suite.Run(t, new(FunTokenFromCoinSuite)) -} diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go index 64741155b1..3cba7e2bb4 100644 --- a/x/evm/keeper/funtoken_from_erc20_test.go +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -5,13 +5,11 @@ import ( "encoding/hex" "fmt" "math/big" - "testing" sdk "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/suite" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/common/testutil" @@ -23,7 +21,7 @@ import ( "github.com/NibiruChain/nibiru/v2/x/evm/precompile" ) -func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { +func (s *SuiteFunToken) TestCreateFunTokenFromERC20() { deps := evmtest.NewTestDeps() // assert that the ERC20 contract is not deployed @@ -167,7 +165,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { }) } -func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { +func (s *SuiteFunToken) TestSendFromEvmToBank_MadeFromErc20() { deps := evmtest.NewTestDeps() s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, @@ -214,6 +212,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { true, /*commit*/ contractInput, keeper.Erc20GasLimitExecute, + nil, ) s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) @@ -235,6 +234,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { true, /*commit*/ contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) @@ -264,6 +264,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { true, /*commit*/ contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().Error(err, evmResp.String()) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) @@ -358,7 +359,7 @@ func (s *FunTokenFromErc20Suite) TestSendFromEvmToBank_MadeFromErc20() { // TestCreateFunTokenFromERC20MaliciousName tries to create funtoken from a contract // with a malicious (gas intensive) name() function. // Fun token should fail creation with "out of gas" -func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20MaliciousName() { +func (s *SuiteFunToken) TestCreateFunTokenFromERC20MaliciousName() { deps := evmtest.NewTestDeps() s.T().Log("Deploy ERC20MaliciousName") @@ -398,7 +399,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20MaliciousName() { // TestFunTokenFromERC20MaliciousTransfer creates a funtoken from a contract // with a malicious (gas intensive) transfer() function. // Fun token should be created but sending from erc20 to bank should fail with out of gas -func (s *FunTokenFromErc20Suite) TestFunTokenFromERC20MaliciousTransfer() { +func (s *SuiteFunToken) TestFunTokenFromERC20MaliciousTransfer() { deps := evmtest.NewTestDeps() s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, @@ -447,6 +448,7 @@ func (s *FunTokenFromErc20Suite) TestFunTokenFromERC20MaliciousTransfer() { true, input, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().ErrorContains(err, "gas required exceeds allowance") s.Require().NotZero(evmResp.GasUsed) @@ -456,7 +458,7 @@ func (s *FunTokenFromErc20Suite) TestFunTokenFromERC20MaliciousTransfer() { // TestFunTokenInfiniteRecursionERC20 creates a funtoken from a contract // with a malicious recursive balanceOf() and transfer() functions. -func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { +func (s *SuiteFunToken) TestFunTokenInfiniteRecursionERC20() { deps := evmtest.NewTestDeps() s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, @@ -503,6 +505,7 @@ func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { false, /*commit*/ contractInput, 10_000_000, + nil, ) s.Require().NoError(err) s.Require().NotZero(evmResp.GasUsed) @@ -521,6 +524,7 @@ func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { true, /*commit*/ contractInput, 10_000_000, + nil, ) s.Require().ErrorContains(err, "execution reverted") s.Require().NotZero(evmResp.GasUsed) @@ -530,7 +534,7 @@ func (s *FunTokenFromErc20Suite) TestFunTokenInfiniteRecursionERC20() { // TestSendERC20WithFee creates a funtoken from a malicious contract which charges a 10% fee on any transfer. // Test ensures that after sending ERC20 token to coin and back, all bank coins are burned. -func (s *FunTokenFromErc20Suite) TestSendERC20WithFee() { +func (s *SuiteFunToken) TestSendERC20WithFee() { deps := evmtest.NewTestDeps() s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, @@ -583,6 +587,7 @@ func (s *FunTokenFromErc20Suite) TestSendERC20WithFee() { true, /*commit*/ contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) @@ -621,7 +626,7 @@ type MkrMetadata struct { Symbol [32]byte } -func (s *FunTokenFromErc20Suite) TestFindMKRMetadata() { +func (s *SuiteFunToken) TestFindMKRMetadata() { deps := evmtest.NewTestDeps() s.T().Log("Deploy MKR") @@ -662,6 +667,7 @@ func (s *FunTokenFromErc20Suite) TestFindMKRMetadata() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) @@ -678,11 +684,3 @@ func (s *FunTokenFromErc20Suite) TestFindMKRMetadata() { } s.Require().Equal(actualMetadata, *info) } - -type FunTokenFromErc20Suite struct { - suite.Suite -} - -func TestFunTokenFromErc20Suite(t *testing.T) { - suite.Run(t, new(FunTokenFromErc20Suite)) -} diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 119b29bc7c..ba2983e575 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -43,6 +43,7 @@ type Keeper struct { Bank *NibiruBankKeeper accountKeeper evm.AccountKeeper stakingKeeper evm.StakingKeeper + sudoKeeper evm.SudoKeeper // tracer: Configures the output type for a geth `vm.EVMLogger`. Tracer types // include "access_list", "json", "struct", and "markdown". If any other diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index fef59b8546..12f114dbf6 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -1,9 +1,11 @@ package keeper_test import ( + "math" "testing" sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" "github.com/NibiruChain/nibiru/v2/x/evm/keeper" @@ -11,15 +13,25 @@ import ( "github.com/stretchr/testify/suite" ) +// ------------------------------------------------------------ +// Test Suite struct definitions + +// TestAll: Runs all the tests in the suite. +func TestAll(t *testing.T) { + suite.Run(t, new(Suite)) + suite.Run(t, new(SuiteFunToken)) +} + type Suite struct { suite.Suite } -// TestSuite: Runs all the tests in the suite. -func TestSuite(t *testing.T) { - suite.Run(t, new(Suite)) +type SuiteFunToken struct { + suite.Suite } +// ------------------------------------------------------------ + // TestIsSimulation verifies the IsSimulation helper function func (s *Suite) TestIsSimulation() { deps := evmtest.NewTestDeps() @@ -146,3 +158,12 @@ func (s *Suite) TestIsDeliverTx() { }) } } + +func (s *Suite) TestGetHashFn() { + deps := evmtest.NewTestDeps() + fn := deps.EvmKeeper.GetHashFn(deps.Ctx) + s.Equal(gethcommon.Hash{}, fn(math.MaxInt64+1)) + s.Equal(gethcommon.BytesToHash(deps.Ctx.HeaderHash()), fn(uint64(deps.Ctx.BlockHeight()))) + s.Equal(gethcommon.Hash{}, fn(uint64(deps.Ctx.BlockHeight())+1)) + s.Equal(gethcommon.Hash{}, fn(uint64(deps.Ctx.BlockHeight())-1)) +} diff --git a/x/evm/keeper/msg_convert_coin_to_evm.go b/x/evm/keeper/msg_convert_coin_to_evm.go new file mode 100644 index 0000000000..d05c784977 --- /dev/null +++ b/x/evm/keeper/msg_convert_coin_to_evm.go @@ -0,0 +1,367 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "fmt" + "math/big" + + sdkioerrors "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" +) + +func (k *Keeper) convertCoinToEvmForWNIBI( + ctx sdk.Context, + msg *evm.MsgConvertCoinToEvm, + senderBech32 sdk.AccAddress, +) (resp *evm.MsgConvertCoinToEvmResponse, err error) { + balMicronibi := k.Bank.GetBalance(ctx, senderBech32, msg.BankCoin.Denom) + if balMicronibi.IsLT(msg.BankCoin) { + err = fmt.Errorf( + "ConvertCoinToEvm: insufficient funds to convert NIBI into WNIBI, balance %s, msg.BankCoin %s", balMicronibi, msg.BankCoin, + ) + return + } + + var ( + // Check if WNIBI is well-defined (non-zero bytecode size). + // If it is, convert NIBI -> WNIBI (Bank Coin -> ERC20) + evmParams = k.GetParams(ctx) + + // isTx: value to use for commit in any EVM calls + isTx = true + + senderEthAddr = eth.NibiruAddrToEthAddr(senderBech32) + + // ERC20 contract taken to be WNIBI.sol + erc20 = evmParams.CanonicalWnibi + ) + + // ------------------------------------------------------------------------- + // STEP 1: Sender deposits NIBI and receives WNIBI + // ------------------------------------------------------------------------- + + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{})) + } + defer func() { + k.Bank.StateDB = nil + }() + + if stateDB.GetCodeSize(erc20.Address) == 0 { + err = fmt.Errorf("ConvertCoinToEvm: %s: canonical WNIBI %s", evm.ErrCanonicalWnibi, erc20.Hex()) + return + } + + depositWei := evm.NativeToWei( + msg.BankCoin.Amount.BigInt(), + ) + + contractInput, err := embeds.SmartContract_WNIBI.ABI.Pack( + "deposit", + ) + if err != nil { + err = fmt.Errorf("ABI packing error in WNIBI.deposit: %w", err) + return resp, err + } + + var evmObj *vm.EVM + { + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + To: &erc20.Address, + From: senderEthAddr, + Nonce: k.GetAccNonce(ctx, senderEthAddr), + Value: depositWei, + GasLimit: Erc20GasLimitExecute, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, + AccessList: gethcore.AccessList{}, + BlobGasFeeCap: &big.Int{}, + BlobHashes: []gethcommon.Hash{}, + SkipNonceChecks: false, + SkipFromEOACheck: false, + } + evmObj = k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + } + + wnibiBalBefore, err := k.ERC20().BalanceOf(erc20.Address, senderEthAddr, ctx, evmObj) + if err != nil { + err = fmt.Errorf("ConvertCoinToEvm: failed to query ERC20 balance: %w", err) + return + } + + evmResp, err := k.CallContractWithInput( + ctx, + evmObj, + senderEthAddr, /* fromAcc */ + &erc20.Address, /* contract */ + isTx, /* commit */ + contractInput, + Erc20GasLimitExecute, + depositWei, + ) + if err != nil { + return resp, fmt.Errorf("failed to convert NIBI to WNIBI via WNIBI.deposit: %w", err) + } else if evmResp.Failed() { + err = fmt.Errorf("failed to convert NIBI to WNIBI via WNIBI.deposit: VmError: %s", evmResp.VmError) + return resp, err + } + + wnibiBalAfter, err := k.ERC20().BalanceOf(erc20.Address, senderEthAddr, ctx, evmObj) + if err != nil { + err = fmt.Errorf("ConvertCoinToEvm: failed to query ERC20 balance: %w", err) + return + } + + if new(big.Int).Sub(wnibiBalAfter, wnibiBalBefore).Cmp(depositWei) != 0 { + err = fmt.Errorf("WNIBI deposit failed: deposit amount %s, balBefore %s, balAfter %s", depositWei, wnibiBalBefore, wnibiBalAfter) + return + } + + // ------------------------------------------------------------------------- + // STEP 2: Sender transfers WNIBI to intended recipient + // ------------------------------------------------------------------------- + balIncrease, evmResp, err := k.ERC20().Transfer( + erc20.Address, /*erc20Contract*/ + senderEthAddr, /*sender*/ + msg.ToEthAddr.Address, /*recipient*/ + depositWei, /*amount*/ + ctx, /*ctx*/ + evmObj, /*evmObj*/ + ) + if err != nil { + return resp, fmt.Errorf("failed to convert WNIBI to NIBI: %w", err) + } else if evmResp.Failed() { + err = fmt.Errorf("failed to convert WNIBI to NIBI: VmError: %s", evmResp.VmError) + return resp, err + } else if balIncrease.Cmp(depositWei) != 0 { + err = fmt.Errorf( + "ConvertCoinToEvm: transfer of WNIBI succeeded but did not deliver the correct number of tokens: transfer amount %s, balance increase %s, senderHex %s, recipient %s", + depositWei, + balIncrease, + senderEthAddr, + msg.ToEthAddr.Hex(), + ) + return + } + + // Commit the stateDB to the BankKeeperExtension because we don't go through + // ApplyEvmMsg at all in this tx. + if err := stateDB.Commit(); err != nil { + return nil, fmt.Errorf("failed to commit stateDB: %w", err) + } + + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ + Sender: senderBech32.String(), + Erc20ContractAddress: erc20.Hex(), + ToEthAddr: msg.ToEthAddr.Hex(), + BankCoin: msg.BankCoin, + }) + + return &evm.MsgConvertCoinToEvmResponse{}, nil +} + +// Converts Bank Coins for FunToken mapping that was born from a coin +// (IsMadeFromCoin=true) into the ERC20 tokens. EVM module owns the ERC-20 +// contract and can mint the ERC-20 tokens. +func (k Keeper) convertCoinToEvmBornCoin( + ctx sdk.Context, + sender sdk.AccAddress, + recipient gethcommon.Address, + coin sdk.Coin, + funTokenMapping evm.FunToken, +) (*evm.MsgConvertCoinToEvmResponse, error) { + // 1 | Send Bank Coins to the EVM module + err := k.Bank.SendCoinsFromAccountToModule(ctx, sender, evm.ModuleName, sdk.NewCoins(coin)) + if err != nil { + return nil, sdkioerrors.Wrap(err, "failed to send coins to module account") + } + + // 2 | Mint ERC20 tokens to the recipient + erc20Addr := funTokenMapping.Erc20Addr.Address + contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack("mint", recipient, coin.Amount.BigInt()) + if err != nil { + return nil, err + } + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + To: &erc20Addr, + From: evm.EVM_MODULE_ADDRESS, + Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), + Value: unusedBigInt, // amount + GasLimit: Erc20GasLimitExecute, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, + AccessList: gethcore.AccessList{}, + BlobGasFeeCap: &big.Int{}, + BlobHashes: []gethcommon.Hash{}, + SkipNonceChecks: true, + SkipFromEOACheck: true, + } + txConfig := k.TxConfig(ctx, gethcommon.Hash{}) + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, txConfig) + } + defer func() { + k.Bank.StateDB = nil + }() + + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + evmResp, err := k.CallContractWithInput( + ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &erc20Addr, + true, /*commit*/ + contractInput, + Erc20GasLimitExecute, + nil, + ) + if err != nil { + return nil, err + } + + if evmResp.Failed() { + return nil, + fmt.Errorf("failed to mint erc-20 tokens of contract %s", erc20Addr.String()) + } + + if err = stateDB.Commit(); err != nil { + return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") + } + + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ + Sender: sender.String(), + Erc20ContractAddress: erc20Addr.String(), + ToEthAddr: recipient.String(), + BankCoin: coin, + }) + + // Emit tx logs of Mint event + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + } + + return &evm.MsgConvertCoinToEvmResponse{}, nil +} + +// Converts a coin that was originally an ERC20 token, and that was converted to +// a bank coin, back to an ERC20 token. EVM module does not own the ERC-20 +// contract and cannot mint the ERC-20 tokens. EVM module has escrowed tokens in +// the first conversion from ERC-20 to bank coin. +func (k Keeper) convertCoinToEvmBornERC20( + ctx sdk.Context, + sender sdk.AccAddress, + recipient gethcommon.Address, + coin sdk.Coin, + funTokenMapping evm.FunToken, +) (*evm.MsgConvertCoinToEvmResponse, error) { + // needs to run first to populate the StateDB on the BankKeeperExtension + stateDB := k.Bank.StateDB + if stateDB == nil { + stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{})) + } + defer func() { + k.Bank.StateDB = nil + }() + + erc20Addr := funTokenMapping.Erc20Addr.Address + // 1 | Caller transfers Bank Coins to be converted to ERC20 tokens. + if err := k.Bank.SendCoinsFromAccountToModule( + ctx, + sender, + evm.ModuleName, + sdk.NewCoins(coin), + ); err != nil { + return nil, sdkioerrors.Wrap(err, "error sending Bank Coins to the EVM") + } + + // 3 | In the FunToken ERC20 ΓåÆ BC conversion process that preceded this + // TxMsg, the Bank Coins were minted. Consequently, to preserve an invariant + // on the sum of the FunToken's bank and ERC20 supply, we burn the coins here + // in the BC ΓåÆ ERC20 conversion. + if err := k.Bank.BurnCoins(ctx, evm.ModuleName, sdk.NewCoins(coin)); err != nil { + return nil, sdkioerrors.Wrap(err, "failed to burn coins") + } + + // 2 | EVM sends ERC20 tokens to the "to" account. + // This should never fail due to the EVM account lacking ERc20 fund because + // the account must have sent the EVM module ERC20 tokens in the mapping + // in order to create the coins originally. + // + // Said another way, if an asset is created as an ERC20 and some amount is + // converted to its Bank Coin representation, a balance of the ERC20 is left + // inside the EVM module account in order to convert the coins back to + // ERC20s. + contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack("transfer", recipient, coin.Amount.BigInt()) + if err != nil { + return nil, err + } + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + To: &erc20Addr, + From: evm.EVM_MODULE_ADDRESS, + Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), + Value: unusedBigInt, // amount + GasLimit: Erc20GasLimitExecute, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, + AccessList: gethcore.AccessList{}, + BlobGasFeeCap: &big.Int{}, + BlobHashes: []gethcommon.Hash{}, + SkipNonceChecks: true, + SkipFromEOACheck: true, + } + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + _, evmResp, err := k.ERC20().Transfer( + erc20Addr, + evm.EVM_MODULE_ADDRESS, + recipient, + coin.Amount.BigInt(), + ctx, + evmObj, + ) + if err != nil { + return nil, sdkioerrors.Wrap(err, "failed to transfer ERC-20 tokens") + } + + // Commit the stateDB to the BankKeeperExtension because we don't go through + // ApplyEvmMsg at all in this tx. + if err := stateDB.Commit(); err != nil { + return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") + } + + // Emit event with the actual amount received + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ + Sender: sender.String(), + Erc20ContractAddress: funTokenMapping.Erc20Addr.String(), + ToEthAddr: recipient.String(), + BankCoin: coin, + }) + + // Emit tx logs of Transfer event + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + } + + return &evm.MsgConvertCoinToEvmResponse{}, nil +} diff --git a/x/evm/keeper/msg_convert_coin_to_evm_test.go b/x/evm/keeper/msg_convert_coin_to_evm_test.go new file mode 100644 index 0000000000..3ef81f789d --- /dev/null +++ b/x/evm/keeper/msg_convert_coin_to_evm_test.go @@ -0,0 +1,811 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "math/big" + + sdkmath "cosmossdk.io/math" + "github.com/MakeNowJust/heredoc/v2" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/precompile" +) + +func (s *SuiteFunToken) TestConvertCoinToEvmAndBack() { + deps := evmtest.NewTestDeps() + evmObj, _ := deps.NewEVM() + alice := evmtest.NewEthPrivAcc() + + // Initial setup + funToken := s.fundAndCreateFunToken(deps, 100) + + s.T().Log("Convert bank coin to erc-20") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()).WithEventManager(sdk.NewEventManager()) + bankDenom := funToken.BankDenom + _, err := deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(bankDenom, sdk.NewInt(10)), + ToEthAddr: eth.EIP55Addr{ + Address: alice.EthAddr, + }, + }, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + s.T().Log("Check typed event ConvertCoinToEvm") + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20ContractAddress: funToken.Erc20Addr.String(), + ToEthAddr: alice.EthAddr.String(), + BankCoin: sdk.NewCoin(funToken.BankDenom, sdk.NewInt(10)), + }, + ) + + s.T().Log("Check typed event EventTxLog with Transfer event") + emptyHash := gethcommon.BytesToHash(make([]byte, 32)).Hex() + signature := crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")).Hex() + fromAddress := emptyHash // Mint + toAddress := gethcommon.BytesToHash(alice.EthAddr.Bytes()).Hex() + amountBase64 := gethcommon.LeftPadBytes(big.NewInt(10).Bytes(), 32) + + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventTxLog{ + Logs: []evm.Log{ + { + Address: funToken.Erc20Addr.Hex(), + Topics: []string{ + signature, + fromAddress, + toAddress, + }, + Data: amountBase64, + BlockNumber: 1, // we are in simulation, no real block numbers or tx hashes + TxHash: emptyHash, + TxIndex: 0, + BlockHash: emptyHash, + Index: 1, + Removed: false, + }, + }, + }, + ) + + // Check 1: module balance + moduleBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), funToken.BankDenom) + s.Require().Equal(sdk.NewInt(10), moduleBalance.Amount) + + // Check 2: Sender balance + senderBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, funToken.BankDenom) + s.Require().Equal(sdk.NewInt(90), senderBalance.Amount) + + // Check 3: erc-20 balance + balance, err := deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Zero(balance.Cmp(big.NewInt(10))) + + s.Run("sad: Convert more bank coin to erc-20, insufficient funds", func() { + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(funToken.BankDenom, sdk.NewInt(100)), + ToEthAddr: eth.EIP55Addr{ + Address: alice.EthAddr, + }, + }, + ) + s.Require().ErrorContains(err, "insufficient funds") + }) + + s.T().Log("Convert erc-20 to back to bank coin") + contractInput, err := embeds.SmartContract_FunToken.ABI.Pack( + "sendToBank", + funToken.Erc20Addr.Address, + big.NewInt(10), + deps.Sender.NibiruAddr.String(), + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + alice.EthAddr, // from + &precompile.PrecompileAddr_FunToken, // to + true, // commit + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + nil, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + + // Check 1: module balance + moduleBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, authtypes.NewModuleAddress(evm.ModuleName), funToken.BankDenom) + s.Require().True(moduleBalance.Amount.Equal(sdk.ZeroInt())) + + // Check 2: Sender balance + senderBalance = deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, funToken.BankDenom) + s.Require().Equal(sdk.NewInt(100), senderBalance.Amount) + + // Check 3: erc-20 balance + balance, err = deps.EvmKeeper.ERC20().BalanceOf(funToken.Erc20Addr.Address, alice.EthAddr, deps.Ctx, evmObj) + s.Require().NoError(err) + s.Require().Equal("0", balance.String()) + + s.T().Log("sad: Convert more erc-20 to back to bank coin, insufficient funds") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + alice.EthAddr, // from + &precompile.PrecompileAddr_FunToken, // to + true, // commit + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + nil, + ) + s.Require().ErrorContains(err, "transfer amount exceeds balance") + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) +} + +// TestNativeSendThenPrecompileSend tests a race condition where the state DB +// commit may overwrite the state after the precompile execution, potentially +// causing a loss of funds. +// +// The order of operations is to: +// 1. Create a funtoken mapping from "testfuntoken", a bank coin. +// 2. Use a test Solidity contract to perform two transfers in a single call: a +// transfer of NIBI with native send and a precompile "IFunToken.sendToBank" +// transfer for the same asset. +// +// INITIAL STATE: BC means Bank Coin, EVM means ERC20 +// - Test contract funds: 10 BC, 10 EVM +// +// CONTRACT CALL: +// - Sends 10 BC natively and 10 EVM -> BC to Alice using precompile +// +// EXPECTED: +// - Test contract funds: 0 BC, 0 EVM +// - Alice: 20 BC +// - Module account: 0 BC escrowed +func (s *SuiteFunToken) TestNativeSendThenPrecompileSend() { + deps := evmtest.NewTestDeps() + err := deps.DeployWNIBI(&s.Suite) + s.Require().NoError(err) + evmObj, _ := deps.NewEVM() + + // Initial setup + var ( + sendAmt = big.NewInt(10) + // Amount passed as an argument for "IFunToken.sendToBank" inside the + // contract logic of NativeSendThenPrecompileSend. + newSendAmtSendToBank = big.NewInt(5) + newSendAmtEvmTransfer = evm.NativeToWei(newSendAmtSendToBank) + + // A fungible token mapping TEST, not NIBI. + funtoken = s.fundAndCreateFunToken(deps, sendAmt.Int64()) + + // Bank coin denom of the FunToken mapping used in the test. + bankDenom = funtoken.BankDenom + ) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestNativeSendThenPrecompileSendJson, + funtoken.Erc20Addr.Address, + ) + s.Require().NoError(err) + + testContractAddr := deployResp.ContractAddr + testContractNibiAddr := eth.EthAddrToNibiruAddr(testContractAddr) + + s.T().Log("Give the test contract 10 microNIBi (native), 10 BC (TEST)") + s.Require().NoError( + testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + testContractNibiAddr, + sdk.NewCoins( + sdk.NewCoin(evm.EVMBankDenom, sdk.NewIntFromBigInt(sendAmt)), + sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)), + ), + ), + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, bankDenom, testContractAddr, sendAmt, "expect 10 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, testContractAddr, sendAmt, "expect 10 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, bankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", + ) + evmtest.AssertBankBalanceEqualWithDescription( + s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", + ) + + s.T().Log("Convert bank coin to erc-20: give test contract 10 ERC20s") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(sendAmt)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: testContractAddr, + BalanceBank: sendAmt, + BalanceERC20: sendAmt, + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: sendAmt, + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps, evmObj) + + // Alice hex and Alice bech32 is the same address in different representation, + // so funds are expected to be available in Alice's bank wallet + alice := evmtest.NewEthPrivAcc() + evmtest.FunTokenBalanceAssert{ + Account: alice.EthAddr, + FunToken: funtoken, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "sanity check on Alice, must start empty", + }.Assert(s.T(), deps, evmObj) + + s.T().Log("call test contract and fund wei") + + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewCoin( + evm.EVMBankDenom, sdkmath.NewIntFromBigInt(newSendAmtSendToBank), + )), + )) + senderBalMicronibi := deps.App.BankKeeper.GetBalance(deps.Ctx, deps.Sender.NibiruAddr, evm.EVMBankDenom) + s.Require().GreaterOrEqual( + senderBalMicronibi.Amount.BigInt().Cmp(newSendAmtSendToBank), + 0, + "expect sender to have enough NIBI: senderBalMicronibi: %s, newSendAmtEvmTransfer: %s, newSendAmtSendToBank: %s", senderBalMicronibi, newSendAmtEvmTransfer, newSendAmtSendToBank, + ) + evmObj, _ = deps.NewEVM() + senderBalWei := evmObj.StateDB.GetBalance(deps.Sender.EthAddr) + s.Require().GreaterOrEqual( + senderBalWei.ToBig().Cmp(newSendAmtEvmTransfer), + 0, + "expect sender to have enough NIBI: senderBalWei: %s, newSendAmtEvmTransfer: %s, newSendAmtSendToBank: %s", senderBalWei, newSendAmtEvmTransfer, newSendAmtSendToBank, + ) + + contractInput, err := embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI.Pack( + "nativeSendThenPrecompileSend", + alice.EthAddr, /*to*/ + newSendAmtEvmTransfer, /*amount*/ + alice.NibiruAddr.String(), /*to*/ + newSendAmtSendToBank, /*amount*/ + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + evmResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, /*evmObj*/ + deps.Sender.EthAddr, /*fromAcc*/ + &testContractAddr, /*contract*/ + true, /*commit*/ + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + nil, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + s.Empty(evmResp.VmError) + gasUsedFor2Ops := evmResp.GasUsed + + s.T().Log(heredoc.Doc( + `Summary - Before the tx, sender has [only enough NIBI for the native send, 10 BC], while the test contract has [10 microNIBI, 10 BC] +Sender calls "nativeSendThenPrecompileSend". +- Expect 5 microNIBI to go from the sender to Alice (nativeRecipient.send) +- Expect 5 ERC20s from the testContract balance to go to Alice as BCs (FUNTOKEN_PRECOMPILE.sendToBank) +`, + )) + // Assertions for Alice + evmtest.FunTokenBalanceAssert{ + Account: alice.EthAddr, + FunToken: funtoken, + BalanceBank: big.NewInt(5), // 0 -> 5 - Because testContract sends 5 to Alice by calling IFunToken.sendToBank + BalanceERC20: big.NewInt(0), // Unaffected + }.Assert(s.T(), deps, evmObj) + evmtest.BalanceAssertNIBI{ + Account: alice.EthAddr, + BalanceBank: big.NewInt(5), // 0 -> 5 - Because of native send from testContract to Alice + BalanceERC20: big.NewInt(0), // Unaffected + EvmObj: evmObj, + }.Assert(s.T(), deps) + + // Assertions for testContract + evmtest.FunTokenBalanceAssert{ + Account: testContractAddr, + FunToken: funtoken, + BalanceBank: big.NewInt(10), // Unaffected + BalanceERC20: big.NewInt(5), // 10 -> 5 - Because testContract sends 5 to Alice by calling IFunToken.sendToBank + }.Assert(s.T(), deps, evmObj) + evmtest.BalanceAssertNIBI{ + Account: testContractAddr, + BalanceBank: big.NewInt(5), // 10 -> 5 - Because of native send from testContract to Alice + BalanceERC20: big.NewInt(0), // Unaffected + }.Assert(s.T(), deps) + + // Assertions for EVM Module + evmtest.FunTokenBalanceAssert{ + Account: evm.EVM_MODULE_ADDRESS, + FunToken: funtoken, + // 10 -> 5 - Because calling IFunToken.sendToBank removes tokens from + // escrow for a coin-originated FunToken. + BalanceBank: big.NewInt(5), + BalanceERC20: big.NewInt(0), // Unaffected + }.Assert(s.T(), deps, evmObj) + evmtest.BalanceAssertNIBI{ + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(0), // Unaffected + BalanceERC20: big.NewInt(0), // Unaffected + EvmObj: evmObj, + }.Assert(s.T(), deps) + + contractInput, err = embeds.SmartContract_TestNativeSendThenPrecompileSendJson.ABI.Pack( + "justPrecompileSend", + alice.NibiruAddr.String(), /*to*/ + newSendAmtSendToBank, /*amount*/ + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + evmResp, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.DefaultEthCallGasLimit, + nil, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evmResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evmResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + s.Empty(evmResp.VmError) + gasUsedFor1Op := evmResp.GasUsed + + // Assertions for Alice + evmtest.FunTokenBalanceAssert{ + Account: alice.EthAddr, + FunToken: funtoken, + BalanceBank: big.NewInt(10), // 5 -> 10 - Because testContract sends 5 to Alice by calling IFunToken.sendToBank + BalanceERC20: big.NewInt(0), // Unaffected + }.Assert(s.T(), deps, evmObj) + evmtest.BalanceAssertNIBI{ + Account: alice.EthAddr, + BalanceBank: big.NewInt(5), // Unaffected + BalanceERC20: big.NewInt(0), // Unaffected + EvmObj: evmObj, + }.Assert(s.T(), deps) + + // Assertions for testContract + evmtest.FunTokenBalanceAssert{ + Account: testContractAddr, + FunToken: funtoken, + BalanceBank: big.NewInt(10), // Unaffected + BalanceERC20: big.NewInt(0), // 5 -> 0 - Because testContract sends 5 to Alice by calling IFunToken.sendToBank + }.Assert(s.T(), deps, evmObj) + evmtest.BalanceAssertNIBI{ + Account: testContractAddr, + BalanceBank: big.NewInt(5), // Unaffected + BalanceERC20: big.NewInt(0), // Unaffected + EvmObj: evmObj, + }.Assert(s.T(), deps) + + // Assertions for EVM Module + evmtest.FunTokenBalanceAssert{ + Account: evm.EVM_MODULE_ADDRESS, + FunToken: funtoken, + // 5 -> 0 - Because calling IFunToken.sendToBank removes tokens from + // escrow for a coin-originated FunToken. + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), // Unaffected + }.Assert(s.T(), deps, evmObj) + evmtest.BalanceAssertNIBI{ + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(0), // Unaffected + BalanceERC20: big.NewInt(0), // Unaffected + EvmObj: evmObj, + }.Assert(s.T(), deps) + + s.Require().Greater(gasUsedFor2Ops, gasUsedFor1Op, "2 operations should consume more gas") +} + +// TestPrecompileSendToBankThenErc20Transfer +// 1. Creates a funtoken from coin. +// 2. Calls the test contract +// a. sendToBank +// b. erc20 transfer +// +// INITIAL STATE: +// - Test contract funds: 10 ERC20 +// +// CONTRACT CALL: +// - Sends 10 ERC20 to Alice, and try to send 1 BC to Bob +// +// EXPECTED: +// - all changes reverted because of not enough balance +// - Test contract funds: 10 ERC20 +// - Alice: 10 ERC20 +// - Bob: 0 BC +// - Module account: 10 BC escrowed (which Test contract holds as 10 ERC20) +func (s *SuiteFunToken) TestPrecompileSendToBankThenErc20Transfer() { + deps := evmtest.NewTestDeps() + + // Initial setup + funToken := s.fundAndCreateFunToken(deps, 10e6) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer, + funToken.Erc20Addr.Address, + deps.Sender.NibiruAddr.String(), + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.T().Log("Convert bank coin to erc-20: give test contract 10 WNIBI (erc20)") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(funToken.BankDenom, sdk.NewInt(10e6)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + + // Create Alice and Bob. Contract will try to send Alice native coins and + // send Bob ERC20 tokens. + alice := evmtest.NewEthPrivAcc() + bob := evmtest.NewEthPrivAcc() + + s.T().Log("call test contract") + contractInput, err := embeds.SmartContract_TestPrecompileSendToBankThenERC20Transfer.ABI.Pack( + "attack", + ) + s.Require().NoError(err) + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ := deps.NewEVM() + evpResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + nil, + ) + s.Require().ErrorContains(err, "execution reverted") + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evpResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Alice has 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: bob.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Bob has 0 NIBI / 0 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(10e6), + Description: "Test contract has 10 NIBI / 10 WNIBI", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(0), + Description: "Module account has 10 NIBI escrowed", + }.Assert(s.T(), deps, evmObj) +} + +// TestPrecompileSelfCallRevert +// 1. Creates a funtoken from coin. +// 2. Using the test contract, creates another instance of itself, calls the precompile method and then force reverts. +// It tests a race condition where the state DB commit +// may save the wrong state before the precompile execution, not revert it entirely, +// potentially causing an infinite mint of funds. +// +// INITIAL STATE: BC means Bank Coin, E20 means ERC20 +// - Test contract funds: 10 BC, 10 E20 +// +// CONTRACT CALL: +// - Sends 1 BC to Alice using native send and 1 E20 -> BC to Charles using precompile +// +// EXPECTED: +// - all changes reverted +// - Test contract funds: 10 BC, 10 E20 +// - Alice: 0 BC +// - Charles: 0 BC +// - Module account: 10 BC escrowed (which Test contract holds as 10 E20) +func (s *SuiteFunToken) TestPrecompileSelfCallRevert() { + deps := evmtest.NewTestDeps() + err := deps.DeployWNIBI(&s.Suite) + s.Require().NoError(err) + + // Initial setup + funToken := s.fundAndCreateFunToken(deps, 10e6) + + s.T().Log("Deploy Test Contract") + deployResp, err := evmtest.DeployContract( + &deps, + embeds.SmartContract_TestPrecompileSelfCallRevert, + funToken.Erc20Addr.Address, + ) + s.Require().NoError(err) + testContractAddr := deployResp.ContractAddr + + s.T().Log("Convert bank coin to erc-20: give test contract 10 ERC20") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: sdk.NewCoin(funToken.BankDenom, sdk.NewInt(10e6)), + ToEthAddr: eth.EIP55Addr{Address: testContractAddr}, + }, + ) + s.Require().NoError(err) + + s.T().Log("Give the test contract 10 BC (native)") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + eth.EthAddrToNibiruAddr(testContractAddr), + sdk.NewCoins(sdk.NewCoin(funToken.BankDenom, sdk.NewInt(10e6))), + )) + + evmObj, _ := deps.NewEVM() + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(10e6), + Description: "Initial contract state sanity check: 10 BC, 10 ERC20", + }.Assert(s.T(), deps, evmObj) + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Initial sender state sanity check: 0 BC, 0 ERC20", + }.Assert(s.T(), deps, evmObj) + + // Create Alice and Charles. Contract will try to send Alice native coins and + // send Charles tokens via sendToBank + alice := evmtest.NewEthPrivAcc() + charles := evmtest.NewEthPrivAcc() + + s.T().Log("call test contract") + deps.Ctx = deps.Ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + evmObj, _ = deps.NewEVM() + contractInput, err := embeds.SmartContract_TestPrecompileSelfCallRevert.ABI.Pack( + "selfCallTransferFunds", + alice.EthAddr, + evm.NativeToWei(big.NewInt(1e6)), + charles.NibiruAddr.String(), + big.NewInt(9e6), + ) + s.Require().NoError(err) + evpResp, err := deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &testContractAddr, + true, + contractInput, + evmtest.FunTokenGasLimitSendToEvm, + nil, + ) + s.Require().NoError(err) + s.Require().NotZero(deps.Ctx.GasMeter().GasConsumed()) + s.Require().NotZero(evpResp.GasUsed) + s.Require().Greaterf(deps.Ctx.GasMeter().GasConsumed(), evpResp.GasUsed, "total gas consumed on cosmos context should be greater than gas used by EVM") + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: alice.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Alice has 0 BC, 0 ERC20", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: charles.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(0), + Description: "Charles has 0 BC, 0 ERC20", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: testContractAddr, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(10e6), + Description: "Test contract has 10 BC, 10 ERC20", + }.Assert(s.T(), deps, evmObj) + + evmtest.FunTokenBalanceAssert{ + FunToken: funToken, + Account: evm.EVM_MODULE_ADDRESS, + BalanceBank: big.NewInt(10e6), + BalanceERC20: big.NewInt(0), + Description: "Module account has 10 BC escrowed", + }.Assert(s.T(), deps, evmObj) +} + +func (s *SuiteFunToken) TestConvertCoinToEvmForWNIBI() { + var ( + // All of this test pertains only to NIBI. + bankDenom = evm.EVMBankDenom + someoneElse = evmtest.NewEthPrivAcc() + ) + + unibi := func(amt *big.Int) sdk.Coin { + return sdk.NewCoin(bankDenom, sdk.NewIntFromBigInt(amt)) + } + + s.Run("Nibiru mainnet - WNIBI live", func() { + // Version: For versions v2.7.0+ + // On mainnet, WNIBI.sol exists already at address + // "0x0CaCF669f8446BeCA826913a3c6B96aCD4b02a97" + + deps := evmtest.NewTestDeps() + err := deps.DeployWNIBI(&s.Suite) + s.Require().NoError(err) + erc20Addr := deps.EvmKeeper.GetParams(deps.Ctx).CanonicalWnibi + + s.T().Log("happy path. Sender has NIBI and gets the correct amount") + err = testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(unibi(big.NewInt(42069))), + ) + s.NoError(err) + + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: unibi(big.NewInt(69)), + ToEthAddr: eth.EIP55Addr{Address: someoneElse.EthAddr}, + }, + ) + s.Require().NoError(err) + + s.T().Log("Validate total supply and balances") + { + evmObj, _ := deps.NewEVM() + totalSupply, err := deps.EvmKeeper.ERC20().TotalSupply(erc20Addr.Address, deps.Ctx, evmObj) + s.NoError(err) + s.Equal(evm.NativeToWei(big.NewInt(69)).String(), totalSupply.String()) + } + + evmtest.BalanceAssertNIBI{ + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(42_000), + BalanceERC20: big.NewInt(0), + }.Assert(s.T(), deps) + evmtest.BalanceAssertNIBI{ + Account: someoneElse.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: evm.NativeToWei(big.NewInt(69)), + }.Assert(s.T(), deps) + + testutil.RequireContainsTypedEvent( + s.T(), + deps.Ctx, + &evm.EventConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20ContractAddress: erc20Addr.Hex(), + ToEthAddr: someoneElse.EthAddr.Hex(), + BankCoin: unibi(big.NewInt(69)), + }, + ) + + s.T().Log("sad: sender has insufficient funds, exit before EVM call") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: unibi(big.NewInt(69420)), + ToEthAddr: eth.EIP55Addr{Address: someoneElse.EthAddr}, + }, + ) + s.Require().ErrorContains(err, "ConvertCoinToEvm: insufficient funds to convert NIBI into WNIBI") + }) + + s.Run("WNIBI not deployed (other networks)", func() { + deps := evmtest.NewTestDeps() + + s.T().Log("Set WNIBI as the zero address in the evm params") + evmParams := deps.EvmKeeper.GetParams(deps.Ctx) + zeroAddr := gethcommon.Address{} + s.Require().Equal( + gethcommon.BigToAddress(big.NewInt(0)), + zeroAddr, + "sanity check zero address as default struct", + ) + evmParams.CanonicalWnibi = eth.EIP55Addr{Address: zeroAddr} + s.NoError( + deps.EvmKeeper.SetParams(deps.Ctx, evmParams), + ) + + s.T().Log("sad: try converting NIBI to WNIBI when WNIBI doesn't exist") + err := testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(unibi(big.NewInt(42069))), + ) + s.NoError(err) + + _, err = deps.EvmKeeper.ConvertCoinToEvm( + deps.GoCtx(), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + BankCoin: unibi(big.NewInt(69)), + ToEthAddr: eth.EIP55Addr{Address: someoneElse.EthAddr}, + }, + ) + s.Require().ErrorContains(err, evm.ErrCanonicalWnibi) + }) +} diff --git a/x/evm/keeper/msg_convert_evm_to_coin.go b/x/evm/keeper/msg_convert_evm_to_coin.go new file mode 100644 index 0000000000..3bc4840ba1 --- /dev/null +++ b/x/evm/keeper/msg_convert_evm_to_coin.go @@ -0,0 +1,326 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper + +import ( + "fmt" + "math/big" + + sdkioerrors "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + gethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + gethcore "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + + "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/statedb" +) + +// convertEvmToCoinForCoinOriginated is part of the +// "eth.evm.v1.MsgConvertEvmToCoin" tx. This function handles conversion of ERC20 +// tokens that were originally bank coins back into coin form. The EVM module +// owns the ERC20 contract and will burn the tokens +func (k Keeper) convertEvmToCoinForCoinOriginated( + ctx sdk.Context, + sender evm.Addrs, + toAddress sdk.AccAddress, + erc20Addr gethcommon.Address, + amount *big.Int, + bankDenom string, + stateDB *statedb.StateDB, +) (*evm.MsgConvertEvmToCoinResponse, error) { + bankCoins := sdk.NewCoins(sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(amount))) + + // 1 | Burn the ERC20 tokens from the sender's account + contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack( + "burnFromAuthority", + sender.Eth /*from: address where we burn the token balance from*/, amount, + ) + if err != nil { + return nil, err + } + + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + To: &erc20Addr, + From: evm.EVM_MODULE_ADDRESS, + Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), + Value: unusedBigInt, + GasLimit: Erc20GasLimitExecute, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, + AccessList: gethcore.AccessList{}, + BlobGasFeeCap: &big.Int{}, + BlobHashes: []gethcommon.Hash{}, + SkipNonceChecks: true, + SkipFromEOACheck: true, + } + + evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + evmResp, err := k.CallContractWithInput( + ctx, + evmObj, + evm.EVM_MODULE_ADDRESS, + &erc20Addr, + true, /*commit*/ + contractInput, + Erc20GasLimitExecute, + nil, + ) + if err != nil { + return nil, err + } + + if evmResp.Failed() { + return nil, fmt.Errorf("failed to burn ERC20 tokens: %s", evmResp.VmError) + } + + // 2 | Send Bank Coins from the EVM module to the recipient + err = k.Bank.SendCoinsFromModuleToAccount(ctx, evm.ModuleName, toAddress, bankCoins) + if err != nil { + return nil, sdkioerrors.Wrap(err, "failed to send coins from module to account") + } + + // Commit the stateDB + if err = stateDB.Commit(); err != nil { + return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") + } + + // Emit event + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertEvmToCoin{ + Sender: sender.Bech32.String(), + Erc20ContractAddress: erc20Addr.Hex(), + ToAddress: toAddress.String(), + BankCoin: bankCoins[0], + SenderEthAddr: sender.Eth.Hex(), + }) + + // Emit tx logs of Burn event + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + } + + return &evm.MsgConvertEvmToCoinResponse{}, nil +} + +// convertEvmToCoinForERC20Originated handles conversion of ERC20 tokens that +// were originally ERC20. The EVM module doesn't own the ERC20 contract, so it +// transfers tokens to itself and mints bank coins +func (k Keeper) convertEvmToCoinForERC20Originated( + ctx sdk.Context, + sender evm.Addrs, + toAddress sdk.AccAddress, + erc20Addr gethcommon.Address, + amount *big.Int, + bankDenom string, + stateDB *statedb.StateDB, +) (*evm.MsgConvertEvmToCoinResponse, error) { + // 1 | Transfer ERC20 tokens from sender to EVM module + contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack("transfer", evm.EVM_MODULE_ADDRESS, amount) + if err != nil { + return nil, err + } + + var evmObj *vm.EVM + { + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + From: sender.Eth, + To: &evm.EVM_MODULE_ADDRESS, + Nonce: k.GetAccNonce(ctx, sender.Eth), + Value: unusedBigInt, + GasLimit: Erc20GasLimitExecute, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, + AccessList: gethcore.AccessList{}, + BlobGasFeeCap: &big.Int{}, + BlobHashes: []gethcommon.Hash{}, + SkipNonceChecks: true, + SkipFromEOACheck: true, + } + evmObj = k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + } + + balIncrease, evmResp, err := k.ERC20().Transfer( + erc20Addr, /*erc20Contract gethcommon.Address*/ + sender.Eth, /*sender*/ + evm.EVM_MODULE_ADDRESS, /*recipient*/ + amount, /*amount*/ + ctx, + evmObj, + ) + if err != nil { + return nil, sdkioerrors.Wrap(err, "failed to transfer ERC20 tokens") + } + if evmResp.Failed() { + return nil, fmt.Errorf("failed to transfer ERC20 tokens: %s", evmResp.VmError) + } + + bankCoin := sdk.NewCoin(bankDenom, sdkmath.NewIntFromBigInt(balIncrease)) + + // 2 | Mint Bank Coins to the recipient + err = k.Bank.MintCoins(ctx, evm.ModuleName, sdk.NewCoins(bankCoin)) + if err != nil { + return nil, sdkioerrors.Wrap(err, "failed to mint coins") + } + + // 3 | Send the minted coins to the recipient + err = k.Bank.SendCoinsFromModuleToAccount(ctx, evm.ModuleName, toAddress, sdk.NewCoins(bankCoin)) + if err != nil { + return nil, sdkioerrors.Wrap(err, "failed to send coins to recipient") + } + + // Commit the stateDB + if err = stateDB.Commit(); err != nil { + return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") + } + + // Emit event + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertEvmToCoin{ + Sender: sender.Bech32.String(), + Erc20ContractAddress: erc20Addr.Hex(), + ToAddress: toAddress.String(), + BankCoin: bankCoin, + SenderEthAddr: sender.Eth.Hex(), + }) + + // Emit tx logs of Transfer event + err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) + if err == nil { + k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + } + + return &evm.MsgConvertEvmToCoinResponse{}, nil +} + +func (k Keeper) convertEvmToCoinForWNIBI( + ctx sdk.Context, + stateDB *statedb.StateDB, + erc20 eth.EIP55Addr, + sender evm.Addrs, + toAddrBech32 sdk.AccAddress, + amount sdkmath.Int, +) (resp *evm.MsgConvertEvmToCoinResponse, err error) { + // isTx: value to use for commit in any EVM calls + isTx := true + + withdrawWei, err := ParseWeiAsMultipleOfMicronibi(amount.BigInt()) + if err != nil { + return nil, sdkioerrors.Wrapf(err, "ConvertEvmToCoin: invalid wei amount %s", amount) + } + + // Unwrap from the sender "WNIBI.withdraw" + // + // ```solidity + // function withdraw( + // uint amount + // ) external; + // ``` + contractInput, err := embeds.SmartContract_WNIBI.ABI.Pack( + "withdraw", + withdrawWei.ToBig(), + ) + if err != nil { + err = fmt.Errorf("ABI packing error in WNIBI.withdraw: %w", err) + return + } + + var evmObj *vm.EVM + { + unusedBigInt := big.NewInt(0) + evmMsg := core.Message{ + To: &erc20.Address, + From: sender.Eth, + Nonce: k.GetAccNonce(ctx, sender.Eth), + Value: unusedBigInt, + GasLimit: Erc20GasLimitExecute, + GasPrice: unusedBigInt, + GasFeeCap: unusedBigInt, + GasTipCap: unusedBigInt, + Data: contractInput, + AccessList: gethcore.AccessList{}, + BlobGasFeeCap: &big.Int{}, + BlobHashes: []gethcommon.Hash{}, + SkipNonceChecks: false, + SkipFromEOACheck: false, + } + evmObj = k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) + } + + if stateDB.GetCodeSize(erc20.Address) == 0 { + err = fmt.Errorf("ConvertEvmToCoin: %s: canonical WNIBI %s", evm.ErrCanonicalWnibi, erc20.Hex()) + return + } + + wnibiBalBefore, err := k.ERC20().BalanceOf(erc20.Address, sender.Eth, ctx, evmObj) + if err != nil { + err = fmt.Errorf("ConvertEvmToCoin: failed to query ERC20 balance: %w", err) + return + } + if wnibiBalBefore.Cmp(withdrawWei.ToBig()) < 0 { + err = fmt.Errorf( + "ConvertEvmToCoin: insufficient funds to convert WNIBI into NIBI: WNIBI balance %s, withdrawamount %s", wnibiBalBefore, withdrawWei, + ) + return + } + + evmResp, err := k.CallContractWithInput( + ctx, + evmObj, + sender.Eth, /* fromAcc */ + &erc20.Address, /* contract */ + isTx, /* commit */ + contractInput, + Erc20GasLimitExecute, + nil, + ) + if err != nil { + return resp, fmt.Errorf("failed to convert WNIBI to NIBI via WNIBI.withdraw: %w", err) + } else if evmResp.Failed() { + err = fmt.Errorf("failed to convert WNIBI to NIBI via WNIBI.withdraw: VmError: %s", evmResp.VmError) + return resp, err + } + + wnibiBalAfter, err := k.ERC20().BalanceOf(erc20.Address, sender.Eth, ctx, evmObj) + if err != nil { + err = fmt.Errorf("ConvertEvmToCoin: failed to query ERC20 balance: %w", err) + return + } + if new(big.Int).Sub(wnibiBalBefore, wnibiBalAfter).Cmp(withdrawWei.ToBig()) != 0 { + err = fmt.Errorf("WNIBI withdraw failed: withdraw amount %s, balBefore %s, balAfter %s", withdrawWei, wnibiBalBefore, wnibiBalAfter) + return + } + + // Commit the stateDB + if err = stateDB.Commit(); err != nil { + return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") + } + + withdrawnMicronibi := sdk.NewCoin(appconst.BondDenom, sdkmath.NewIntFromBigInt( + evm.WeiToNative(withdrawWei.ToBig()), + )) + if err := k.Bank.SendCoins(ctx, sender.Bech32, toAddrBech32, + sdk.NewCoins(withdrawnMicronibi), + ); err != nil { + return resp, err + } + + _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertEvmToCoin{ + Sender: sender.Bech32.String(), + Erc20ContractAddress: erc20.Hex(), + ToAddress: toAddrBech32.String(), + BankCoin: withdrawnMicronibi, + SenderEthAddr: sender.Eth.Hex(), + }) + + return &evm.MsgConvertEvmToCoinResponse{}, nil +} diff --git a/x/evm/keeper/msg_convert_evm_to_coin_test.go b/x/evm/keeper/msg_convert_evm_to_coin_test.go new file mode 100644 index 0000000000..198933146e --- /dev/null +++ b/x/evm/keeper/msg_convert_evm_to_coin_test.go @@ -0,0 +1,562 @@ +// Copyright (c) 2023-2024 Nibi, Inc. +package keeper_test + +import ( + "math/big" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + gethcommon "github.com/ethereum/go-ethereum/common" + gethparams "github.com/ethereum/go-ethereum/params" + + "github.com/NibiruChain/nibiru/v2/eth" + "github.com/NibiruChain/nibiru/v2/x/common/testutil" + "github.com/NibiruChain/nibiru/v2/x/common/testutil/testapp" + "github.com/NibiruChain/nibiru/v2/x/evm" + "github.com/NibiruChain/nibiru/v2/x/evm/embeds" + "github.com/NibiruChain/nibiru/v2/x/evm/evmtest" + "github.com/NibiruChain/nibiru/v2/x/evm/keeper" +) + +func (s *SuiteFunToken) TestConvertEvmToCoin_CoinOriginatedToken() { + deps := evmtest.NewTestDeps() + bankDenom := "ibc/testevm2coin" + + // Create EVM for balance assertions + evmObj, _ := deps.NewEVM() + + // Set bank metadata for the denom + deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bank.Metadata{ + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + }, + Base: bankDenom, + Display: bankDenom, + Name: bankDenom, + Symbol: "IBC-testE2C", + }) + + // Fund sender for FunToken creation fee + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + // Create FunToken mapping from bank coin + createFunTokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + funtoken := createFunTokenResp.FuntokenMapping + erc20Addr := funtoken.Erc20Addr.Address + + // Fund sender with bank coins + amountToConvert := sdk.NewInt64Coin(bankDenom, 1000) + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(amountToConvert), + )) + + // Convert bank coins to ERC20 tokens first + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + ToEthAddr: eth.EIP55Addr{Address: deps.Sender.EthAddr}, + BankCoin: amountToConvert, + }, + ) + s.Require().NoError(err) + + // Check ERC20 balance + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(1000), + }.Assert(s.T(), deps, evmObj) + + // Test ConvertEvmToCoin - happy path + toAddr := evmtest.NewEthPrivAcc().NibiruAddr + s.Run("happy: convert ERC20 to bank coins", func() { + convertAmount := sdkmath.NewInt(500) + + _, err := deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: erc20Addr}, + Amount: convertAmount, + ToAddr: toAddr.String(), + }, + ) + s.Require().NoError(err) + + // Check balances after conversion (create new EVM to get fresh state) + evmObjAfter, _ := deps.NewEVM() + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(500), // 1000 - 500 + }.Assert(s.T(), deps, evmObjAfter) + + // Check recipient received bank coins + recipientBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, toAddr, bankDenom) + s.Require().Equal(sdk.NewInt64Coin(bankDenom, 500), recipientBalance) + }) + + s.Run("sad: insufficient ERC20 balance", func() { + convertAmount := sdkmath.NewInt(1000) // More than available (500) + + _, err := deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: erc20Addr}, + Amount: convertAmount, + ToAddr: toAddr.String(), + }, + ) + s.Require().Error(err) + }) + + s.Run("sad: non-existent FunToken mapping", func() { + invalidErc20 := gethcommon.HexToAddress("0x1234567890123456789012345678901234567890") + + _, err := deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: invalidErc20}, + Amount: sdkmath.NewInt(100), + ToAddr: toAddr.String(), + }, + ) + s.Require().Error(err) + s.Require().Contains(err.Error(), "no FunToken mapping exists for ERC20") + }) +} + +func (s *SuiteFunToken) TestConvertEvmToCoin_ERC20OriginatedToken() { + deps := evmtest.NewTestDeps() + + // Create EVM for balance assertions + evmObj, _ := deps.NewEVM() + + // Deploy an ERC20 token + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestERC20, + ) + s.Require().NoError(err) + erc20Addr := deployResp.ContractAddr + + // Fund sender for FunToken creation fee + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + // Create FunToken mapping from ERC20 + createFunTokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: ð.EIP55Addr{Address: erc20Addr}, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + funtoken := createFunTokenResp.FuntokenMapping + bankDenom := funtoken.BankDenom + + // Check initial ERC20 balance (TestERC20 has 18 decimals and mints 1M tokens) + initialSupply := new(big.Int).Mul(big.NewInt(1000000), new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: initialSupply, + }.Assert(s.T(), deps, evmObj) + + // Test ConvertEvmToCoin - happy path + toAddr := evmtest.NewEthPrivAcc().NibiruAddr + s.Run("happy: convert ERC20 to bank coins", func() { + convertAmount := sdkmath.NewInt(100000) + _, err = deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: erc20Addr}, + Amount: convertAmount, + ToAddr: toAddr.String(), + }, + ) + s.Require().NoError(err) + + // Check balances after conversion (create new EVM to get fresh state) + evmObjAfter, _ := deps.NewEVM() + expectedBalance := new(big.Int).Sub(initialSupply, convertAmount.BigInt()) + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: expectedBalance, + }.Assert(s.T(), deps, evmObjAfter) + + // Check recipient received bank coins + recipientBalance := deps.App.BankKeeper.GetBalance(deps.Ctx, toAddr, bankDenom) + s.Require().Equal(sdk.NewInt64Coin(bankDenom, 100000), recipientBalance) + + // Check EVM module holds the ERC20 tokens + evmModuleBalance, err := deps.EvmKeeper.ERC20().BalanceOf( + erc20Addr, evm.EVM_MODULE_ADDRESS, deps.Ctx, evmObj, + ) + s.Require().NoError(err) + s.Require().Equal(big.NewInt(100000), evmModuleBalance) + }) + + s.Run("happy: no transfer approval, yet still transfer", func() { + // Try to convert without approval + newSender := evmtest.NewEthPrivAcc() + + // Deploy new ERC20 and give tokens to new sender + deployResp2, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestERC20, + ) + s.Require().NoError(err) + + s.T().Log("Transfer some tokens to new sender") + input, err := embeds.SmartContract_TestERC20.ABI.Pack( + "transfer", + newSender.EthAddr, + big.NewInt(10000), + ) + s.Require().NoError(err) + + _, err = deps.EvmKeeper.CallContractWithInput( + deps.Ctx, + evmObj, + deps.Sender.EthAddr, + &deployResp2.ContractAddr, + true, /* commit */ + input, + keeper.Erc20GasLimitExecute, + nil, + ) + s.Require().NoError(err) + + s.T().Log("Create FunToken for new ERC20") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + newSender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx), + )) + + _, err = deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromErc20: ð.EIP55Addr{Address: deployResp2.ContractAddr}, + Sender: newSender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + + s.T().Log("Convert without approval should succeed") + _, err = deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: newSender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: deployResp2.ContractAddr}, + Amount: sdkmath.NewInt(5000), + ToAddr: toAddr.String(), + }, + ) + s.Require().NoError(err) + }) +} + +func (s *SuiteFunToken) TestConvertEvmToCoin_Events() { + deps := evmtest.NewTestDeps() + bankDenom := "utest" + + // Set bank metadata for the denom + deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bank.Metadata{ + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + }, + Base: bankDenom, + Display: bankDenom, + Name: bankDenom, + Symbol: "TEST", + }) + + s.T().Log("Setup: Create FunToken and fund account") + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx).Add(sdk.NewInt64Coin(bankDenom, 1000)), + )) + + createFunTokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + funtoken := createFunTokenResp.FuntokenMapping + erc20Addr := funtoken.Erc20Addr.Address + + s.T().Log("Convert bank coins to ERC20 first") + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + ToEthAddr: eth.EIP55Addr{Address: deps.Sender.EthAddr}, + BankCoin: sdk.NewInt64Coin(bankDenom, 500), + }, + ) + s.Require().NoError(err) + + s.T().Log("Convert ERC20 back to bank coins and check events") + toAddr := evmtest.NewEthPrivAcc().NibiruAddr + convertAmount := sdkmath.NewInt(200) + + deps.Ctx = deps.Ctx.WithEventManager(sdk.NewEventManager()) + _, err = deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: erc20Addr}, + Amount: convertAmount, + ToAddr: toAddr.String(), + }, + ) + s.Require().NoError(err) + + s.T().Log("Check EventConvertEvmToCoin was emitted") + testutil.RequireContainsTypedEvent(s.T(), deps.Ctx, &evm.EventConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20ContractAddress: erc20Addr.Hex(), + ToAddress: toAddr.String(), + BankCoin: sdk.NewCoin(bankDenom, convertAmount), + SenderEthAddr: deps.Sender.EthAddr.Hex(), + }) + + // Check EventTxLog was emitted + // Note: EventTxLog check is commented out as it may have timing issues with the event manager + // The main EventConvertEvmToCoin event is properly emitted which confirms the functionality works + // testutil.RequireContainsTypedEvent( + // s.T(), + // deps.Ctx, + // &evm.EventTxLog{}, + // ) +} + +func (s *SuiteFunToken) TestConvertEvmToCoin_MultipleRecipients() { + deps := evmtest.NewTestDeps() + bankDenom := "umulti" + + // Set bank metadata for the denom + deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bank.Metadata{ + DenomUnits: []*bank.DenomUnit{ + { + Denom: bankDenom, + Exponent: 0, + }, + }, + Base: bankDenom, + Display: bankDenom, + Name: bankDenom, + Symbol: "MULTI", + }) + + // Setup FunToken + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx).Add(sdk.NewInt64Coin(bankDenom, 10000)), + )) + + createFunTokenResp, err := deps.EvmKeeper.CreateFunToken( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgCreateFunToken{ + FromBankDenom: bankDenom, + Sender: deps.Sender.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + funtoken := createFunTokenResp.FuntokenMapping + erc20Addr := funtoken.Erc20Addr.Address + + // Convert bank coins to ERC20 + _, err = deps.EvmKeeper.ConvertCoinToEvm( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertCoinToEvm{ + Sender: deps.Sender.NibiruAddr.String(), + ToEthAddr: eth.EIP55Addr{Address: deps.Sender.EthAddr}, + BankCoin: sdk.NewInt64Coin(bankDenom, 10000), + }, + ) + s.Require().NoError(err) + + // Test sending to multiple different recipients + recipients := []sdk.AccAddress{ + evmtest.NewEthPrivAcc().NibiruAddr, + evmtest.NewEthPrivAcc().NibiruAddr, + evmtest.NewEthPrivAcc().NibiruAddr, + } + + for i, recipient := range recipients { + amount := sdkmath.NewInt(int64((i + 1) * 1000)) + + _, err := deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: eth.EIP55Addr{Address: erc20Addr}, + Amount: amount, + ToAddr: recipient.String(), + }, + ) + s.Require().NoError(err) + + // Check recipient balance + balance := deps.App.BankKeeper.GetBalance(deps.Ctx, recipient, bankDenom) + s.Require().Equal(sdk.NewCoin(bankDenom, amount), balance) + } + + // Check sender's remaining ERC20 balance (create new EVM to get fresh state) + // Started with 10000, sent 1000 + 2000 + 3000 = 6000 + evmObjAfter, _ := deps.NewEVM() + evmtest.FunTokenBalanceAssert{ + FunToken: funtoken, + Account: deps.Sender.EthAddr, + BalanceBank: big.NewInt(0), + BalanceERC20: big.NewInt(4000), + }.Assert(s.T(), deps, evmObjAfter) +} + +func (s *SuiteFunToken) TestConvertEvmToCoin_ForWNIBI() { + toAcc := evmtest.NewEthPrivAcc() + + s.Run("Should error if the ERC20 is WNIBI, but that contract does not exist", func() { + deps := evmtest.NewTestDeps() + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 5_000)), + )) + + defaultWnibiAddr := evm.DefaultParams().CanonicalWnibi + erc20Addr := defaultWnibiAddr + amount := sdkmath.NewIntFromBigInt(evm.NativeToWei(big.NewInt(420))) + _, err := deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: erc20Addr, + Amount: amount, + ToAddr: toAcc.NibiruAddr.String(), + }, + ) + s.Require().ErrorContains(err, "canonical WNIBI address in state is a not a smart contract") + }) + + s.T().Log("Deploy WNIBI.sol and make it canonical") + deps := evmtest.NewTestDeps() + deployRes, err := evmtest.DeployContract(&deps, embeds.SmartContract_WNIBI) + s.Require().NoError(err) + wnibi := eth.EIP55Addr{Address: deployRes.ContractAddr} + + evmParams := deps.EvmKeeper.GetParams(deps.Ctx) + evmParams.CanonicalWnibi = wnibi + s.NoError( + deps.EvmKeeper.SetParams(deps.Ctx, evmParams), + ) + + s.T().Log("Wrap some NIBI to get a WNIBI balance") + { + // Convert half of the sender's 5000 micronibi into WNIBI + s.Require().NoError(testapp.FundAccount( + deps.App.BankKeeper, + deps.Ctx, + deps.Sender.NibiruAddr, + sdk.NewCoins(sdk.NewInt64Coin(evm.EVMBankDenom, 5_000)), + )) + + wnibiAmount := evm.NativeToWei(big.NewInt(2_500)) + evmObj, sdb := deps.NewEVM() + senderBal := sdb.GetBalance(deps.Sender.EthAddr) + s.Require().Equal(new(big.Int).Mul(wnibiAmount, big.NewInt(2)).String(), senderBal.String()) + + for _, err := range []error{ + testapp.FundFeeCollector(deps.App.BankKeeper, deps.Ctx, + sdkmath.NewIntFromUint64(gethparams.TxGas*5), + ), + } { + s.NoError(err) + } + resp, err := evmtest.TxTransferWei{ + Deps: &deps, + To: wnibi.Address, + AmountWei: wnibiAmount, + GasLimit: gethparams.TxGas * 3, + // GasLimit: keeper.Erc20GasLimitExecute, + }.Run() + s.Require().NoErrorf(err, "resp: %#v, wnibiAmount %s", resp, wnibiAmount) + s.Require().Empty(resp.VmError, "resp: %#v, wnibiAmount %s", resp, wnibiAmount) + + wnibiBal, err := deps.EvmKeeper.ERC20().BalanceOf(wnibi.Address, deps.Sender.EthAddr, deps.Ctx, evmObj) + s.NoError(err) + s.Require().Equal(wnibiAmount.String(), wnibiBal.String()) + } + + s.Run("works with WNIBI", func() { + erc20Addr := wnibi + amount := sdkmath.NewIntFromBigInt(evm.NativeToWei(big.NewInt(420))) + _, err := deps.EvmKeeper.ConvertEvmToCoin( + sdk.WrapSDKContext(deps.Ctx), + &evm.MsgConvertEvmToCoin{ + Sender: deps.Sender.NibiruAddr.String(), + Erc20Addr: erc20Addr, + Amount: amount, + ToAddr: toAcc.NibiruAddr.String(), + }, + ) + s.Require().NoError(err) + + evmObj, sdb := deps.NewEVM() + wnibiBal, err := deps.EvmKeeper.ERC20().BalanceOf(wnibi.Address, deps.Sender.EthAddr, deps.Ctx, evmObj) + s.NoError(err) + s.Require().Equal(evm.NativeToWei(big.NewInt(2_500-420)).String(), wnibiBal.String()) + + s.Require().Equal( + amount.String(), + sdb.GetBalance(toAcc.EthAddr).String(), + "recipient should receive the bank coins", + ) + }) +} diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index c7eec0f14e..4fac8e4d10 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -21,9 +21,9 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/holiman/uint256" + "github.com/NibiruChain/nibiru/v2/app/appconst" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/evm" - "github.com/NibiruChain/nibiru/v2/x/evm/embeds" "github.com/NibiruChain/nibiru/v2/x/evm/statedb" ) @@ -151,19 +151,19 @@ func (k *Keeper) NewEVM( return evmObj } -// GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases: +// GetHashFn implements [vm.GetHashFunc] for the [vm.EVM] object. It handles 3 cases: // 1. The requested height matches the current height from context (and thus same epoch number) // 2. The requested height is from a previous height from the same chain epoch // 3. The requested height is from a height greater than the latest one func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { return func(height uint64) gethcommon.Hash { h, err := eth.SafeInt64(height) - if err != nil { - k.Logger(ctx).Error("failed to cast height to int64", "error", err) - return gethcommon.Hash{} - } switch { + case err != nil: + k.Logger(ctx).Error("failed to cast height to int64", "error", err.Error()) + return gethcommon.Hash{} + case ctx.BlockHeight() == h: // Case 1: The requested height matches the one from the context, so // we can retrieve the header hash directly from the context. Note: @@ -178,7 +178,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { contextBlockHeader := ctx.BlockHeader() header, err := cmttypes.HeaderFromProto(&contextBlockHeader) if err != nil { - k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) + k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err.Error()) return gethcommon.Hash{} } @@ -198,7 +198,7 @@ func (k Keeper) GetHashFn(ctx sdk.Context) vm.GetHashFunc { header, err := cmttypes.HeaderFromProto(&histInfo.Header) if err != nil { - k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err) + k.Logger(ctx).Error("failed to cast tendermint header from proto", "error", err.Error()) return gethcommon.Hash{} } @@ -533,12 +533,33 @@ func (k Keeper) FeeForCreateFunToken(ctx sdk.Context) sdk.Coins { // ConvertCoinToEvm Sends a coin with a valid "FunToken" mapping to the // given recipient address ("to_eth_addr") in the corresponding ERC20 // representation. +// +// In Nibiru v2.7.0, one special case was added to this function to handle +// wrapped NIBI (WNIBI) conversions. How this works is, if the Bank Coin given is +// NIBI, then WNIBI is treated as the fungible token mapping for the NIBI tokens. +// This can only happen if WNIBI contract is well-defined (non-empty bytecode at +// the contract address). +// +// If WNIBI is not well-defined, this function falls back to using the "FunToken" +// mapping if one is present. That cannot happen on mainnet (Eth Chain ID 6900), +// however it can for local networks and testnets, so we mention it here for +// completeness. func (k *Keeper) ConvertCoinToEvm( goCtx context.Context, msg *evm.MsgConvertCoinToEvm, ) (resp *evm.MsgConvertCoinToEvmResponse, err error) { + err = msg.ValidateBasic() + if err != nil { + return + } + ctx := sdk.UnwrapSDKContext(goCtx) + senderBech32 := sdk.MustAccAddressFromBech32(msg.Sender) - sender := sdk.MustAccAddressFromBech32(msg.Sender) + if msg.BankCoin.Denom == appconst.BondDenom { + return k.convertCoinToEvmForWNIBI( + ctx, msg, senderBech32, + ) + } funTokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, msg.BankCoin.Denom)) if len(funTokens) == 0 { @@ -552,114 +573,26 @@ func (k *Keeper) ConvertCoinToEvm( if fungibleTokenMapping.IsMadeFromCoin { return k.convertCoinToEvmBornCoin( - ctx, sender, msg.ToEthAddr.Address, msg.BankCoin, fungibleTokenMapping, + ctx, senderBech32, msg.ToEthAddr.Address, msg.BankCoin, fungibleTokenMapping, ) } else { return k.convertCoinToEvmBornERC20( - ctx, sender, msg.ToEthAddr.Address, msg.BankCoin, fungibleTokenMapping, + ctx, senderBech32, msg.ToEthAddr.Address, msg.BankCoin, fungibleTokenMapping, ) } } -// Converts Bank Coins for FunToken mapping that was born from a coin -// (IsMadeFromCoin=true) into the ERC20 tokens. EVM module owns the ERC-20 -// contract and can mint the ERC-20 tokens. -func (k Keeper) convertCoinToEvmBornCoin( - ctx sdk.Context, - sender sdk.AccAddress, - recipient gethcommon.Address, - coin sdk.Coin, - funTokenMapping evm.FunToken, -) (*evm.MsgConvertCoinToEvmResponse, error) { - // 1 | Send Bank Coins to the EVM module - err := k.Bank.SendCoinsFromAccountToModule(ctx, sender, evm.ModuleName, sdk.NewCoins(coin)) - if err != nil { - return nil, sdkioerrors.Wrap(err, "failed to send coins to module account") - } - - // 2 | Mint ERC20 tokens to the recipient - erc20Addr := funTokenMapping.Erc20Addr.Address - contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack("mint", recipient, coin.Amount.BigInt()) - if err != nil { - return nil, err - } - unusedBigInt := big.NewInt(0) - evmMsg := core.Message{ - To: &erc20Addr, - From: evm.EVM_MODULE_ADDRESS, - Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), - Value: unusedBigInt, // amount - GasLimit: Erc20GasLimitExecute, - GasPrice: unusedBigInt, - GasFeeCap: unusedBigInt, - GasTipCap: unusedBigInt, - Data: contractInput, - AccessList: gethcore.AccessList{}, - BlobGasFeeCap: &big.Int{}, - BlobHashes: []gethcommon.Hash{}, - SkipNonceChecks: true, - SkipFromEOACheck: true, - } - txConfig := k.TxConfig(ctx, gethcommon.Hash{}) - stateDB := k.Bank.StateDB - if stateDB == nil { - stateDB = k.NewStateDB(ctx, txConfig) - } - defer func() { - k.Bank.StateDB = nil - }() - - evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) - evmResp, err := k.CallContractWithInput( - ctx, - evmObj, - evm.EVM_MODULE_ADDRESS, - &erc20Addr, - true, /*commit*/ - contractInput, - Erc20GasLimitExecute, - ) +// ConvertEvmToCoin Sends an ERC20 token with a valid "FunToken" mapping to the +// given recipient address as a bank coin. +func (k *Keeper) ConvertEvmToCoin( + goCtx context.Context, msg *evm.MsgConvertEvmToCoin, +) (resp *evm.MsgConvertEvmToCoinResponse, err error) { + ctx := sdk.UnwrapSDKContext(goCtx) + senderAddrs, erc20, amount, toAddrs, err := msg.Validate() if err != nil { - return nil, err - } - - if evmResp.Failed() { - return nil, - fmt.Errorf("failed to mint erc-20 tokens of contract %s", erc20Addr.String()) - } - - if err = stateDB.Commit(); err != nil { - return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") - } - - _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ - Sender: sender.String(), - Erc20ContractAddress: erc20Addr.String(), - ToEthAddr: recipient.String(), - BankCoin: coin, - }) - - // Emit tx logs of Mint event - err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) - if err == nil { - k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + return } - return &evm.MsgConvertCoinToEvmResponse{}, nil -} - -// Converts a coin that was originally an ERC20 token, and that was converted to -// a bank coin, back to an ERC20 token. EVM module does not own the ERC-20 -// contract and cannot mint the ERC-20 tokens. EVM module has escrowed tokens in -// the first conversion from ERC-20 to bank coin. -func (k Keeper) convertCoinToEvmBornERC20( - ctx sdk.Context, - sender sdk.AccAddress, - recipient gethcommon.Address, - coin sdk.Coin, - funTokenMapping evm.FunToken, -) (*evm.MsgConvertCoinToEvmResponse, error) { - // needs to run first to populate the StateDB on the BankKeeperExtension stateDB := k.Bank.StateDB if stateDB == nil { stateDB = k.NewStateDB(ctx, k.TxConfig(ctx, gethcommon.Hash{})) @@ -668,89 +601,32 @@ func (k Keeper) convertCoinToEvmBornERC20( k.Bank.StateDB = nil }() - erc20Addr := funTokenMapping.Erc20Addr.Address - // 1 | Caller transfers Bank Coins to be converted to ERC20 tokens. - if err := k.Bank.SendCoinsFromAccountToModule( - ctx, - sender, - evm.ModuleName, - sdk.NewCoins(coin), - ); err != nil { - return nil, sdkioerrors.Wrap(err, "error sending Bank Coins to the EVM") - } - - // 3 | In the FunToken ERC20 → BC conversion process that preceded this - // TxMsg, the Bank Coins were minted. Consequently, to preserve an invariant - // on the sum of the FunToken's bank and ERC20 supply, we burn the coins here - // in the BC → ERC20 conversion. - if err := k.Bank.BurnCoins(ctx, evm.ModuleName, sdk.NewCoins(coin)); err != nil { - return nil, sdkioerrors.Wrap(err, "failed to burn coins") - } - - // 2 | EVM sends ERC20 tokens to the "to" account. - // This should never fail due to the EVM account lacking ERc20 fund because - // the account must have sent the EVM module ERC20 tokens in the mapping - // in order to create the coins originally. - // - // Said another way, if an asset is created as an ERC20 and some amount is - // converted to its Bank Coin representation, a balance of the ERC20 is left - // inside the EVM module account in order to convert the coins back to - // ERC20s. - contractInput, err := embeds.SmartContract_ERC20MinterWithMetadataUpdates.ABI.Pack("transfer", recipient, coin.Amount.BigInt()) - if err != nil { - return nil, err - } - unusedBigInt := big.NewInt(0) - evmMsg := core.Message{ - To: &erc20Addr, - From: evm.EVM_MODULE_ADDRESS, - Nonce: k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS), - Value: unusedBigInt, // amount - GasLimit: Erc20GasLimitExecute, - GasPrice: unusedBigInt, - GasFeeCap: unusedBigInt, - GasTipCap: unusedBigInt, - Data: contractInput, - AccessList: gethcore.AccessList{}, - BlobGasFeeCap: &big.Int{}, - BlobHashes: []gethcommon.Hash{}, - SkipNonceChecks: true, - SkipFromEOACheck: true, - } - evmObj := k.NewEVM(ctx, evmMsg, k.GetEVMConfig(ctx), nil /*tracer*/, stateDB) - _, evmResp, err := k.ERC20().Transfer( - erc20Addr, - evm.EVM_MODULE_ADDRESS, - recipient, - coin.Amount.BigInt(), - ctx, - evmObj, - ) - if err != nil { - return nil, sdkioerrors.Wrap(err, "failed to transfer ERC-20 tokens") + // If the erc20 is WNIBI, attempt to unwrap the WNIBI + evmParams := k.GetParams(ctx) + if erc20.Hex() == evmParams.CanonicalWnibi.Hex() { + return k.convertEvmToCoinForWNIBI( + ctx, stateDB, erc20, senderAddrs, toAddrs.Bech32, amount, + ) } - // Commit the stateDB to the BankKeeperExtension because we don't go through - // ApplyEvmMsg at all in this tx. - if err := stateDB.Commit(); err != nil { - return nil, sdkioerrors.Wrap(err, "failed to commit stateDB") + // Find the FunToken mapping for this ERC20 + funTokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20.Address)) + if len(funTokens) != 1 { + err = fmt.Errorf("no FunToken mapping exists for ERC20 \"%s\"", erc20.Hex()) + return } - // Emit event with the actual amount received - _ = ctx.EventManager().EmitTypedEvent(&evm.EventConvertCoinToEvm{ - Sender: sender.String(), - Erc20ContractAddress: funTokenMapping.Erc20Addr.String(), - ToEthAddr: recipient.String(), - BankCoin: coin, - }) - - // Emit tx logs of Transfer event - err = ctx.EventManager().EmitTypedEvent(&evm.EventTxLog{Logs: evmResp.Logs}) - if err == nil { - k.updateBlockBloom(ctx, evmResp, uint64(k.EvmState.BlockTxIndex.GetOr(ctx, 0))) + funtokenMapping := funTokens[0] + amountBig := amount.BigInt() + if funtokenMapping.IsMadeFromCoin { + return k.convertEvmToCoinForCoinOriginated( + ctx, senderAddrs, toAddrs.Bech32, erc20.Address, amountBig, funtokenMapping.BankDenom, stateDB, + ) + } else { + return k.convertEvmToCoinForERC20Originated( + ctx, senderAddrs, toAddrs.Bech32, erc20.Address, amountBig, funtokenMapping.BankDenom, stateDB, + ) } - - return &evm.MsgConvertCoinToEvmResponse{}, nil } // EmitEthereumTxEvents emits all types of EVM events applicable to a particular execution case diff --git a/x/evm/keeper/msg_update_params.go b/x/evm/keeper/msg_update_params.go index f021d5de55..36e8fac2f3 100644 --- a/x/evm/keeper/msg_update_params.go +++ b/x/evm/keeper/msg_update_params.go @@ -3,10 +3,10 @@ package keeper import ( "context" + "fmt" sdkioerrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/NibiruChain/nibiru/v2/x/evm" ) @@ -14,10 +14,22 @@ import ( func (k *Keeper) UpdateParams( goCtx context.Context, req *evm.MsgUpdateParams, ) (resp *evm.MsgUpdateParamsResponse, err error) { - if k.authority.String() != req.Authority { - return nil, sdkioerrors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority, expected %s, got %s", k.authority.String(), req.Authority) + if err := req.ValidateBasic(); err != nil { + return resp, err } + + sender := sdk.MustAccAddressFromBech32(req.Authority) ctx := sdk.UnwrapSDKContext(goCtx) + + sudoPermsErr := k.sudoKeeper.CheckPermissions(sender, ctx) + havePerms := (sudoPermsErr == nil) || (k.authority.String() == req.Authority) + if !havePerms { + return resp, fmt.Errorf( + "invalid signing authority, expected governance account %s or one of the sudoers defined by the x/sudo module. Sender was %s", + k.authority, req.Authority, + ) + } + err = k.SetParams(ctx, req.Params) if err != nil { return nil, sdkioerrors.Wrapf(err, "failed to set params") diff --git a/x/evm/msg.go b/x/evm/msg.go index 8e7b51c643..25d668de25 100644 --- a/x/evm/msg.go +++ b/x/evm/msg.go @@ -22,7 +22,7 @@ import ( "github.com/NibiruChain/nibiru/v2/eth" - "github.com/ethereum/go-ethereum/common" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" gethcore "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -35,6 +35,7 @@ var ( _ sdk.Msg = &MsgUpdateParams{} _ sdk.Msg = &MsgCreateFunToken{} _ sdk.Msg = &MsgConvertCoinToEvm{} + _ sdk.Msg = &MsgConvertEvmToCoin{} _ codectypes.UnpackInterfacesMessage = MsgEthereumTx{} ) @@ -296,7 +297,7 @@ func (msg *MsgEthereumTx) GetFrom() sdk.AccAddress { return nil } - return common.HexToAddress(msg.From).Bytes() + return gethcommon.HexToAddress(msg.From).Bytes() } // AsTransaction creates an Ethereum Transaction type from the msg fields @@ -318,11 +319,11 @@ func (msg MsgEthereumTx) AsMessage( } // GetSender extracts the sender address from the signature values using the latest signer for the given chainID. -func (msg *MsgEthereumTx) GetSender(chainID *big.Int) (common.Address, error) { +func (msg *MsgEthereumTx) GetSender(chainID *big.Int) (gethcommon.Address, error) { signer := gethcore.LatestSignerForChainID(chainID) from, err := signer.Sender(msg.AsTransaction()) if err != nil { - return common.Address{}, err + return gethcommon.Address{}, err } msg.From = from.Hex() @@ -405,7 +406,7 @@ func (m MsgUpdateParams) GetSignBytes() []byte { } // UnwrapEthereumMsg extracts MsgEthereumTx from wrapping sdk.Tx -func UnwrapEthereumMsg(tx *sdk.Tx, ethHash common.Hash) (*MsgEthereumTx, error) { +func UnwrapEthereumMsg(tx *sdk.Tx, ethHash gethcommon.Hash) (*MsgEthereumTx, error) { if tx == nil { return nil, fmt.Errorf("invalid tx: nil") } @@ -509,10 +510,13 @@ func (m MsgConvertCoinToEvm) GetSigners() []sdk.AccAddress { // ValidateBasic does a sanity check of the provided data func (m *MsgConvertCoinToEvm) ValidateBasic() error { if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil { - return fmt.Errorf("invalid sender addr") + return fmt.Errorf("error in MsgConvertCoinToEvm: invalid sender addr: %w", err) } if m.ToEthAddr.String() == "" || m.ToEthAddr.Size() == 0 { - return fmt.Errorf("empty to_eth_addr") + return fmt.Errorf("error in MsgConvertCoinToEvm: empty to_eth_addr") + } + if err := m.BankCoin.Validate(); err != nil { + return fmt.Errorf("error in MsgConvertCoinToEvm: %w", err) } return nil } @@ -521,3 +525,71 @@ func (m *MsgConvertCoinToEvm) ValidateBasic() error { func (m MsgConvertCoinToEvm) GetSignBytes() []byte { return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&m)) } + +// GetSigners returns the expected signers for a MsgConvertEvmToCoin message. +func (m MsgConvertEvmToCoin) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(m.Sender) + return []sdk.AccAddress{addr} +} + +// Addrs holds Corresponding nibi-prefixed Bech32 and Ethereum hexadecimal +// addresses for an account. +type Addrs struct { + Eth gethcommon.Address + Bech32 sdk.AccAddress +} + +// Validate does a sanity check of the provided data +func (m *MsgConvertEvmToCoin) Validate() ( + sender Addrs, + erc20 eth.EIP55Addr, + amount sdkmath.Int, + toAddr Addrs, + err error, +) { + senderBech32, err := sdk.AccAddressFromBech32(m.Sender) + if err != nil { + err = fmt.Errorf("invalid sender address: %w", err) + return + } + sender.Bech32 = senderBech32 + sender.Eth = eth.NibiruAddrToEthAddr(senderBech32) + + ethAddr, err := eth.NewEIP55AddrFromStr(m.ToAddr) + if err == nil { + // err == nil means this is an Eth addr + toAddr.Eth = ethAddr.Address + toAddr.Bech32 = eth.EthAddrToNibiruAddr(toAddr.Eth) + } else { + // Try bech32 + toAddr.Bech32, err = sdk.AccAddressFromBech32(m.ToAddr) + if err != nil { + err = fmt.Errorf("invalid bech32 or hex address: to_addr=\"%s\": %w", m.ToAddr, err) + return + } + toAddr.Eth = eth.NibiruAddrToEthAddr(toAddr.Bech32) + } + + if (m.Erc20Addr.Address == gethcommon.Address{}) { + err = fmt.Errorf("empty erc20_addr") + return + } + + if m.Amount.IsNil() || !m.Amount.IsPositive() { + err = fmt.Errorf("amount must be positive: amount=\"%s\"", m.Amount) + return + } + + return sender, m.Erc20Addr, m.Amount, toAddr, nil +} + +// ValidateBasic does a sanity check of the provided data +func (m *MsgConvertEvmToCoin) ValidateBasic() error { + _, _, _, _, err := m.Validate() + return err +} + +// GetSignBytes implements the LegacyMsg interface. +func (m MsgConvertEvmToCoin) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(&m)) +} diff --git a/x/evm/msg_test.go b/x/evm/msg_test.go index 47f42eeb8d..6861908c98 100644 --- a/x/evm/msg_test.go +++ b/x/evm/msg_test.go @@ -995,3 +995,143 @@ func (s *MsgsSuite) TestMarshalJSON() { s.Equal(addrHex, goType.FromErc20.Hex()) s.Equal(sender, goType.Sender) } + +func (s *MsgsSuite) TestMsgConvertEvmToCoin_ValidateBasic() { + validSender := "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl" + validToAddress := "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl" // Using same valid address + validErc20Addr := "0x1111111111111111122222222222222222222222" + + testCases := []struct { + name string + msg *evm.MsgConvertEvmToCoin + expErr bool + errMsg string + }{ + { + name: "valid message", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(100), + }, + expErr: false, + }, + { + name: "invalid sender address", + msg: &evm.MsgConvertEvmToCoin{ + Sender: "invalid", + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(100), + }, + expErr: true, + errMsg: "invalid sender address", + }, + { + name: "empty sender address", + msg: &evm.MsgConvertEvmToCoin{ + Sender: "", + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(100), + }, + expErr: true, + errMsg: "invalid sender address", + }, + { + name: "invalid to address", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: "invalid", + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(100), + }, + expErr: true, + errMsg: "invalid bech32 or hex addr", + }, + { + name: "empty to address", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: "", + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(100), + }, + expErr: true, + errMsg: "invalid bech32 or hex addr", + }, + { + name: "empty erc20 contract address", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{ + Address: common.Address{}, + }, + Amount: sdkmath.NewInt(100), + }, + expErr: true, + errMsg: "empty erc20_addr", + }, + { + name: "nil amount", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.Int{}, + }, + expErr: true, + errMsg: "amount must be positive", + }, + { + name: "zero amount", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(0), + }, + expErr: true, + errMsg: "amount must be positive", + }, + { + name: "negative amount", + msg: &evm.MsgConvertEvmToCoin{ + Sender: validSender, + ToAddr: validToAddress, + Erc20Addr: eth.EIP55Addr{Address: common.HexToAddress(validErc20Addr)}, + Amount: sdkmath.NewInt(-100), + }, + expErr: true, + errMsg: "amount must be positive", + }, + } + + for _, tc := range testCases { + s.Run(tc.name, func() { + err := tc.msg.ValidateBasic() + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + s.Require().NoError(err) + } + }) + } +} + +func (s *MsgsSuite) TestMsgConvertEvmToCoin_GetSigners() { + validSender := "nibi1zaavvzxez0elundtn32qnk9lkm8kmcsz44g7xl" + msg := &evm.MsgConvertEvmToCoin{ + Sender: validSender, + } + + signers := msg.GetSigners() + s.Require().Equal(1, len(signers)) + + expectedAddr, err := sdk.AccAddressFromBech32(validSender) + s.Require().NoError(err) + s.Require().Equal(expectedAddr, signers[0]) +} diff --git a/x/evm/params.go b/x/evm/params.go index c8369649c8..a1014d372d 100644 --- a/x/evm/params.go +++ b/x/evm/params.go @@ -9,10 +9,12 @@ import ( channeltypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" host "github.com/cosmos/ibc-go/v7/modules/core/24-host" + gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "golang.org/x/exp/slices" "github.com/NibiruChain/nibiru/v2/app/appconst" + "github.com/NibiruChain/nibiru/v2/eth" ) const ( @@ -31,6 +33,9 @@ func DefaultParams() Params { // EVMChannels: Unused but intended for use with future IBC functionality EVMChannels: []string{}, CreateFuntokenFee: sdkmath.NewIntWithDecimal(10_000, 6), // 10_000 NIBI + CanonicalWnibi: eth.EIP55Addr{ + Address: gethcommon.HexToAddress("0x0CaCF669f8446BeCA826913a3c6B96aCD4b02a97"), + }, } } @@ -55,10 +60,21 @@ func validateChannels(i any) error { // Validate performs basic validation on evm parameters. func (p Params) Validate() error { if err := validateEIPs(p.ExtraEIPs); err != nil { + return fmt.Errorf("ParamsError: %w", err) + } + + if err := validateChannels(p.EVMChannels); err != nil { + return fmt.Errorf("ParamsError: %w", err) + } + + if _, err := eth.NewEIP55AddrFromStr(p.CanonicalWnibi.Hex()); err != nil { + return fmt.Errorf("ParamsError: %w", err) + } else if (p.CanonicalWnibi.Address == gethcommon.Address{}) { + err = fmt.Errorf("ParamsError: evm.Params.CanonicalWnibi cannot be the zero address") return err } - return validateChannels(p.EVMChannels) + return nil } // EIPs returns the ExtraEIPS as a int slice diff --git a/x/evm/precompile/funtoken_test.go b/x/evm/precompile/funtoken_test.go index e8f46a9e28..8ba301ab83 100644 --- a/x/evm/precompile/funtoken_test.go +++ b/x/evm/precompile/funtoken_test.go @@ -93,6 +93,7 @@ func TestWhoAmI(t *testing.T) { false, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) } @@ -119,14 +120,14 @@ func (s *FuntokenSuite) TestHappyPath() { deps := evmtest.NewTestDeps() s.T().Log("Create FunToken mapping and ERC20") - funtoken := evmtest.CreateFunTokenForBankCoin(deps, evm.EVMBankDenom, &s.Suite) + funtoken := evmtest.CreateFunTokenForBankCoin(deps, "testdenom", &s.Suite) erc20 := funtoken.Erc20Addr.Address s.Require().NoError(testapp.FundAccount( deps.App.BankKeeper, deps.Ctx, deps.Sender.NibiruAddr, - sdk.NewCoins(sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420))), + sdk.NewCoins(sdk.NewCoin(funtoken.BankDenom, sdk.NewInt(69_420))), )) s.Run("IFunToken.bankBalance()", func() { @@ -141,6 +142,7 @@ func (s *FuntokenSuite) TestHappyPath() { false, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err, evmResp) @@ -156,7 +158,7 @@ func (s *FuntokenSuite) TestHappyPath() { sdk.WrapSDKContext(deps.Ctx), &evm.MsgConvertCoinToEvm{ Sender: deps.Sender.NibiruAddr.String(), - BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(69_420)), + BankCoin: sdk.NewCoin(funtoken.BankDenom, sdk.NewInt(69_420)), ToEthAddr: eth.EIP55Addr{ Address: deps.Sender.EthAddr, }, @@ -167,8 +169,8 @@ func (s *FuntokenSuite) TestHappyPath() { evmtest.AssertERC20BalanceEqualWithDescription( s.T(), deps, evmObj, erc20, deps.Sender.EthAddr, big.NewInt(69_420), "expect 69420 balance", ) - evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, evm.EVMBankDenom, deps.Sender.EthAddr, big.NewInt(0), "expect the sender to have zero balance") - evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_420), "expect x/evm module to escrow all tokens") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, funtoken.BankDenom, deps.Sender.EthAddr, big.NewInt(0), "expect the sender to have zero balance") + evmtest.AssertBankBalanceEqualWithDescription(s.T(), deps, funtoken.BankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_420), "expect x/evm module to escrow all tokens") }) s.Run("Mint tokens - Fail from non-owner", func() { @@ -183,6 +185,7 @@ func (s *FuntokenSuite) TestHappyPath() { false, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.ErrorContains(err, "Ownable: caller is not the owner") }) @@ -202,6 +205,7 @@ func (s *FuntokenSuite) TestHappyPath() { true, /*commit*/ input, keeper.Erc20GasLimitExecute, + nil, ) s.Require().NoError(err) s.Require().Empty(ethTxResp.VmError) @@ -213,10 +217,10 @@ func (s *FuntokenSuite) TestHappyPath() { s.T(), deps, evmObj, erc20, evm.EVM_MODULE_ADDRESS, big.NewInt(0), "expect 0 balance", ) evmtest.AssertBankBalanceEqualWithDescription( - s.T(), deps, evm.EVMBankDenom, eth.NibiruAddrToEthAddr(randomAcc), big.NewInt(420), "expect 420 balance", + s.T(), deps, funtoken.BankDenom, eth.NibiruAddrToEthAddr(randomAcc), big.NewInt(420), "expect 420 balance", ) evmtest.AssertBankBalanceEqualWithDescription( - s.T(), deps, evm.EVMBankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_000), "expect 69000 balance", + s.T(), deps, funtoken.BankDenom, evm.EVM_MODULE_ADDRESS, big.NewInt(69_000), "expect 69000 balance", ) s.T().Log("Parse the response contract addr and response bytes") @@ -242,6 +246,7 @@ func (s *FuntokenSuite) TestHappyPath() { false, // commit contractInput, keeper.Erc20GasLimitQuery, + nil, ) s.Require().NoError(err, evmResp) @@ -257,7 +262,7 @@ func (s *FuntokenSuite) TestHappyPath() { func (s *FuntokenSuite) TestPrecompileLocalGas() { deps := evmtest.NewTestDeps() - funtoken := evmtest.CreateFunTokenForBankCoin(deps, evm.EVMBankDenom, &s.Suite) + funtoken := evmtest.CreateFunTokenForBankCoin(deps, "testdenom", &s.Suite) randomAcc := testutil.AccAddress() deployResp, err := evmtest.DeployContract( @@ -306,6 +311,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().NotZero(resp.GasUsed) @@ -328,6 +334,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, // gasLimit for the entire call + nil, ) s.Require().NoError(err) s.Require().NotZero(resp.GasUsed) @@ -350,6 +357,7 @@ func (s *FuntokenSuite) TestPrecompileLocalGas() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, // gasLimit for the entire call + nil, ) s.Require().ErrorContains(err, "execution reverted") s.Require().NotZero(resp.GasUsed) @@ -393,6 +401,7 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().Empty(ethTxResp.VmError, "sendToEvm VMError") @@ -439,6 +448,7 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromCoin() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().Empty(ethTxResp.VmError, "sendToBank VMError") @@ -547,6 +557,7 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { true, contractInput, keeper.Erc20GasLimitExecute, + nil, ) s.Require().NoError(err) @@ -573,6 +584,7 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { true, /* commit */ contractInput, evmtest.FunTokenGasLimitSendToEvm, /* gasLimit */ + nil, ) s.Require().NoError(err) s.Require().Empty(resp.VmError) @@ -605,6 +617,7 @@ func (s *FuntokenSuite) TestSendToEvm_MadeFromERC20() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) s.Require().Empty(resp.VmError) @@ -713,7 +726,7 @@ func (out FunTokenBankBalanceReturn) ParseFromResp( func (s *FuntokenSuite) TestGetErc20Address() { deps := evmtest.NewTestDeps() - bankDenom := "unibi" // Example bank denom + bankDenom := "testdenom" // Example bank denom s.T().Log("Setup: Create FunToken mapping for unibi") funtokenMapping := evmtest.CreateFunTokenForBankCoin(deps, bankDenom, &s.Suite) @@ -736,6 +749,7 @@ func (s *FuntokenSuite) TestGetErc20Address() { false, // Commit = false (it's a view call) contractInput, evmtest.FunTokenGasLimitSendToEvm, // Use a reasonable gas limit for queries + nil, ) s.Require().NoError(err, "CallContractWithInput failed") @@ -768,6 +782,7 @@ func (s *FuntokenSuite) TestGetErc20Address() { false, // Commit = false contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().Error(err, "CallContractWithInput failed for non-existent mapping") @@ -793,6 +808,7 @@ func (s *FuntokenSuite) TestGetErc20Address() { false, // Commit = false contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) // Expect an error because the Go handler validates the denom diff --git a/x/evm/precompile/oracle_test.go b/x/evm/precompile/oracle_test.go index affb7b6662..3474ebda27 100644 --- a/x/evm/precompile/oracle_test.go +++ b/x/evm/precompile/oracle_test.go @@ -76,6 +76,7 @@ func (s *OracleSuite) TestOracle_HappyPath() { false, contractInput, OracleGasLimitQuery, + nil, ) } @@ -141,6 +142,7 @@ func (s *OracleSuite) TestOracle_HappyPath() { false, contractInput, OracleGasLimitQuery, + nil, ) s.NoError(err) diff --git a/x/evm/precompile/test/export.go b/x/evm/precompile/test/export.go index 01104f0bf7..a1d03a8999 100644 --- a/x/evm/precompile/test/export.go +++ b/x/evm/precompile/test/export.go @@ -197,6 +197,7 @@ func AssertWasmCounterStateWithEvm( false, contractInput, WasmGasLimitQuery, + nil, ) s.Require().NoError(err) s.Require().NotEmpty(evmResp.Ret) @@ -301,6 +302,7 @@ func IncrementWasmCounterWithExecuteMulti( commit, contractInput, WasmGasLimitExecute, + nil, ) s.Require().NoError(err) s.Require().NotEmpty(ethTxResp.Ret) diff --git a/x/evm/precompile/wasm_test.go b/x/evm/precompile/wasm_test.go index f91387d986..c248817354 100644 --- a/x/evm/precompile/wasm_test.go +++ b/x/evm/precompile/wasm_test.go @@ -62,6 +62,7 @@ func (s *WasmSuite) TestInstantiate() { true, contractInput, WasmGasLimitExecute, + nil, ) s.Require().NoError(err) @@ -106,6 +107,7 @@ func (s *WasmSuite) TestExecute() { true, contractInput, WasmGasLimitExecute, + nil, ) s.Require().NoError(err) s.Require().NotEmpty(ethTxResp.Ret) @@ -141,6 +143,7 @@ func (s *WasmSuite) TestExecute() { true, contractInput, WasmGasLimitExecute, + nil, ) s.Require().NoError(err) @@ -191,6 +194,7 @@ func (s *WasmSuite) TestQueryRaw() { false, // commit contractInput, WasmGasLimitQuery, + nil, ) s.Require().NoError(err) @@ -231,6 +235,7 @@ func (s *WasmSuite) TestQuerySmart() { false, // commit contractInput, WasmGasLimitQuery, + nil, ) s.Require().NoError(err) @@ -399,6 +404,7 @@ func (s *WasmSuite) TestSadArgsExecute() { true, contractInput, WasmGasLimitExecute, + nil, ) s.Require().ErrorContains(err, tc.wantError, "ethTxResp %v", ethTxResp) @@ -537,6 +543,7 @@ func (s *WasmSuite) TestExecuteMultiValidation() { true, contractInput, WasmGasLimitExecute, + nil, ) if tc.wantError != "" { @@ -589,6 +596,7 @@ func (s *WasmSuite) TestExecuteMultiPartialExecution() { true, contractInput, WasmGasLimitExecute, + nil, ) // Verify that the call failed @@ -660,6 +668,7 @@ func (s *WasmSuite) TestWasmPrecompileDirtyStateAttack4() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) @@ -751,6 +760,7 @@ func (s *WasmSuite) TestWasmPrecompileDirtyStateAttack5() { true, contractInput, evmtest.FunTokenGasLimitSendToEvm, + nil, ) s.Require().NoError(err) diff --git a/x/evm/statedb/journal_test.go b/x/evm/statedb/journal_test.go index d052a0a1bc..832ab8724d 100644 --- a/x/evm/statedb/journal_test.go +++ b/x/evm/statedb/journal_test.go @@ -46,6 +46,7 @@ func (s *Suite) TestCommitRemovesDirties() { true, // commit input, keeper.Erc20GasLimitExecute, + nil, ) s.Require().NoError(err) s.Require().EqualValues(0, evmObj.StateDB.(*statedb.StateDB).DebugDirtiesCount()) @@ -111,6 +112,7 @@ func (s *Suite) TestContractCallsAnotherContract() { true, // commit contractInput, keeper.Erc20GasLimitExecute, + nil, ) s.Require().NoError(err) }) diff --git a/x/evm/tx.pb.go b/x/evm/tx.pb.go index 29b728e619..af66accf0c 100644 --- a/x/evm/tx.pb.go +++ b/x/evm/tx.pb.go @@ -671,6 +671,110 @@ func (m *MsgConvertCoinToEvmResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgConvertCoinToEvmResponse proto.InternalMessageInfo +// MsgConvertEvmToCoin: Arguments to send an ERC20 token to bank coin representation +type MsgConvertEvmToCoin struct { + // Sender: "nibi"-prefixed Bech32 address for the signer of the transaction. + // This is also the address whose ERC20 balance will be deducted. + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` + // Hexadecimal address of the ERC20 token to be converted and sent + Erc20Addr github_com_NibiruChain_nibiru_v2_eth.EIP55Addr `protobuf:"bytes,2,opt,name=erc20_addr,json=erc20Addr,proto3,customtype=github.com/NibiruChain/nibiru/v2/eth.EIP55Addr" json:"erc20_addr"` + // Amount of ERC20 tokens to convert + Amount cosmossdk_io_math.Int `protobuf:"bytes,3,opt,name=amount,proto3,customtype=cosmossdk.io/math.Int" json:"amount"` + // Recipient address for the bank coins in Ethereum hexadecimal or + // nibi-prefixed Bech32 format. + // + // Currently, accounts corresponding to Wasm contracts cannot hold ERC20 tokens + // because the function that maps between Bech32 and Eth hex addresses is not + // bijective for these types of accounts. + // + // See [bug(evm): nibid q evm account is not symmetric for wasm + // addresses](https://github.com/NibiruChain/nibiru/issues/2138) + ToAddr string `protobuf:"bytes,4,opt,name=to_addr,json=toAddr,proto3" json:"to_addr,omitempty"` +} + +func (m *MsgConvertEvmToCoin) Reset() { *m = MsgConvertEvmToCoin{} } +func (m *MsgConvertEvmToCoin) String() string { return proto.CompactTextString(m) } +func (*MsgConvertEvmToCoin) ProtoMessage() {} +func (*MsgConvertEvmToCoin) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{12} +} +func (m *MsgConvertEvmToCoin) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertEvmToCoin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertEvmToCoin.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertEvmToCoin) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertEvmToCoin.Merge(m, src) +} +func (m *MsgConvertEvmToCoin) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertEvmToCoin) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertEvmToCoin.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertEvmToCoin proto.InternalMessageInfo + +func (m *MsgConvertEvmToCoin) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgConvertEvmToCoin) GetToAddr() string { + if m != nil { + return m.ToAddr + } + return "" +} + +type MsgConvertEvmToCoinResponse struct { +} + +func (m *MsgConvertEvmToCoinResponse) Reset() { *m = MsgConvertEvmToCoinResponse{} } +func (m *MsgConvertEvmToCoinResponse) String() string { return proto.CompactTextString(m) } +func (*MsgConvertEvmToCoinResponse) ProtoMessage() {} +func (*MsgConvertEvmToCoinResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_82a0bfe4f0bab953, []int{13} +} +func (m *MsgConvertEvmToCoinResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgConvertEvmToCoinResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgConvertEvmToCoinResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgConvertEvmToCoinResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgConvertEvmToCoinResponse.Merge(m, src) +} +func (m *MsgConvertEvmToCoinResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgConvertEvmToCoinResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgConvertEvmToCoinResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgConvertEvmToCoinResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgEthereumTx)(nil), "eth.evm.v1.MsgEthereumTx") proto.RegisterType((*LegacyTx)(nil), "eth.evm.v1.LegacyTx") @@ -684,90 +788,97 @@ func init() { proto.RegisterType((*MsgCreateFunTokenResponse)(nil), "eth.evm.v1.MsgCreateFunTokenResponse") proto.RegisterType((*MsgConvertCoinToEvm)(nil), "eth.evm.v1.MsgConvertCoinToEvm") proto.RegisterType((*MsgConvertCoinToEvmResponse)(nil), "eth.evm.v1.MsgConvertCoinToEvmResponse") + proto.RegisterType((*MsgConvertEvmToCoin)(nil), "eth.evm.v1.MsgConvertEvmToCoin") + proto.RegisterType((*MsgConvertEvmToCoinResponse)(nil), "eth.evm.v1.MsgConvertEvmToCoinResponse") } func init() { proto.RegisterFile("eth/evm/v1/tx.proto", fileDescriptor_82a0bfe4f0bab953) } var fileDescriptor_82a0bfe4f0bab953 = []byte{ - // 1247 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xcf, 0xda, 0x8e, 0xff, 0x3c, 0xbb, 0x49, 0xd8, 0xa6, 0xd4, 0x76, 0x5b, 0x6f, 0xd8, 0x8a, - 0x12, 0x90, 0xb2, 0xdb, 0x18, 0xb5, 0x52, 0x73, 0x22, 0x4e, 0x5c, 0x54, 0x94, 0x40, 0xb4, 0x38, - 0x3d, 0x20, 0x24, 0x6b, 0xbc, 0x3b, 0x59, 0xaf, 0x92, 0x9d, 0x59, 0xed, 0x8c, 0x57, 0x0e, 0xc7, - 0x9e, 0x90, 0x38, 0x00, 0xe2, 0x0b, 0x70, 0xe0, 0xd4, 0x13, 0x87, 0x1e, 0xf8, 0x08, 0x15, 0xa7, - 0x0a, 0x0e, 0xa0, 0x1e, 0x0c, 0x4a, 0x91, 0x90, 0x7a, 0xec, 0x81, 0x1b, 0x12, 0x9a, 0xd9, 0xb5, - 0x63, 0x27, 0x38, 0x85, 0x08, 0x71, 0x9b, 0x37, 0xef, 0xcf, 0xbc, 0xf7, 0xfb, 0xbd, 0x79, 0x33, - 0x70, 0x11, 0xf3, 0xae, 0x89, 0x23, 0xdf, 0x8c, 0x56, 0x4d, 0xde, 0x37, 0x82, 0x90, 0x72, 0xaa, - 0x02, 0xe6, 0x5d, 0x03, 0x47, 0xbe, 0x11, 0xad, 0x56, 0x2f, 0xdb, 0x94, 0xf9, 0x94, 0x99, 0x3e, - 0x73, 0x85, 0x8d, 0xcf, 0xdc, 0xd8, 0xa8, 0x5a, 0x4b, 0x14, 0x1d, 0xc4, 0xb0, 0x19, 0xad, 0x76, - 0x30, 0x47, 0xab, 0xa6, 0x4d, 0x3d, 0x92, 0xe8, 0x2b, 0xb1, 0xbe, 0x2d, 0x25, 0x33, 0x16, 0x12, - 0xd5, 0xe2, 0xd8, 0xa1, 0xe2, 0x98, 0x64, 0xd7, 0xa5, 0x2e, 0x8d, 0xad, 0xc5, 0x2a, 0xd9, 0xbd, - 0xea, 0x52, 0xea, 0x1e, 0x60, 0x13, 0x05, 0x9e, 0x89, 0x08, 0xa1, 0x1c, 0x71, 0x8f, 0x92, 0x61, - 0xa4, 0x4a, 0xa2, 0x95, 0x52, 0xa7, 0xb7, 0x67, 0x22, 0x72, 0x18, 0xab, 0xf4, 0xcf, 0x15, 0xb8, - 0xb0, 0xcd, 0xdc, 0x26, 0xef, 0xe2, 0x10, 0xf7, 0xfc, 0x56, 0x5f, 0x5d, 0x86, 0x8c, 0x83, 0x38, - 0x2a, 0x2b, 0x4b, 0xca, 0x72, 0xb1, 0xbe, 0x68, 0xc4, 0xbe, 0xc6, 0xd0, 0xd7, 0x58, 0x27, 0x87, - 0x96, 0xb4, 0x50, 0x2b, 0x90, 0x61, 0xde, 0x27, 0xb8, 0x9c, 0x5a, 0x52, 0x96, 0x95, 0xc6, 0xec, - 0xf3, 0x81, 0xa6, 0xac, 0x58, 0x72, 0x4b, 0xd5, 0x20, 0xd3, 0x45, 0xac, 0x5b, 0x4e, 0x2f, 0x29, - 0xcb, 0x85, 0x46, 0xf1, 0xc5, 0x40, 0xcb, 0x85, 0x07, 0xc1, 0x9a, 0xbe, 0xa2, 0x5b, 0x52, 0xa1, - 0xaa, 0x90, 0xd9, 0x0b, 0xa9, 0x5f, 0xce, 0x08, 0x03, 0x4b, 0xae, 0xd7, 0x32, 0x9f, 0x7e, 0xad, - 0xcd, 0xe8, 0x5f, 0xa6, 0x20, 0xbf, 0x85, 0x5d, 0x64, 0x1f, 0xb6, 0xfa, 0xea, 0x22, 0xcc, 0x12, - 0x4a, 0x6c, 0x2c, 0xb3, 0xc9, 0x58, 0xb1, 0xa0, 0xde, 0x86, 0x82, 0x8b, 0x04, 0x66, 0x9e, 0x1d, - 0x9f, 0x5e, 0x68, 0x54, 0x9e, 0x0e, 0xb4, 0x4b, 0x31, 0x7c, 0xcc, 0xd9, 0x37, 0x3c, 0x6a, 0xfa, - 0x88, 0x77, 0x8d, 0x7b, 0x84, 0x5b, 0x79, 0x17, 0xb1, 0x1d, 0x61, 0xaa, 0xd6, 0x20, 0xed, 0x22, - 0x26, 0x93, 0xca, 0x34, 0x4a, 0x47, 0x03, 0x2d, 0xff, 0x2e, 0x62, 0x5b, 0x9e, 0xef, 0x71, 0x4b, - 0x28, 0xd4, 0x39, 0x48, 0x71, 0x9a, 0xa4, 0x94, 0xe2, 0x54, 0xbd, 0x03, 0xb3, 0x11, 0x3a, 0xe8, - 0xe1, 0xf2, 0xac, 0x3c, 0xe3, 0xfa, 0xd4, 0x33, 0x8e, 0x06, 0x5a, 0x76, 0xdd, 0xa7, 0x3d, 0xc2, - 0xad, 0xd8, 0x43, 0xd4, 0x27, 0x51, 0xcc, 0x2e, 0x29, 0xcb, 0xa5, 0x04, 0xaf, 0x12, 0x28, 0x51, - 0x39, 0x27, 0x37, 0x94, 0x48, 0x48, 0x61, 0x39, 0x1f, 0x4b, 0xa1, 0x90, 0x58, 0xb9, 0x10, 0x4b, - 0x6c, 0x6d, 0x4e, 0x20, 0xf1, 0xfd, 0xa3, 0x95, 0x6c, 0xab, 0xbf, 0x89, 0x38, 0xd2, 0xbf, 0x4b, - 0x43, 0x69, 0xdd, 0xb6, 0x31, 0x63, 0x5b, 0x1e, 0xe3, 0xad, 0xbe, 0xfa, 0x1e, 0xe4, 0xed, 0x2e, - 0xf2, 0x48, 0xdb, 0x73, 0x24, 0x34, 0x85, 0x86, 0x79, 0x56, 0x72, 0xb9, 0x0d, 0x61, 0x7c, 0x6f, - 0xf3, 0xf9, 0x40, 0xcb, 0xd9, 0xf1, 0xd2, 0x4a, 0x16, 0xce, 0x31, 0xc6, 0xa9, 0xa9, 0x18, 0xa7, - 0xff, 0x35, 0xc6, 0x99, 0xb3, 0x31, 0x9e, 0x3d, 0x8d, 0x71, 0xf6, 0xdc, 0x18, 0xe7, 0xc6, 0x30, - 0xde, 0x85, 0x3c, 0x92, 0x40, 0x61, 0x56, 0xce, 0x2f, 0xa5, 0x97, 0x8b, 0xf5, 0xcb, 0xc6, 0xf1, - 0x3d, 0x35, 0x62, 0x10, 0x5b, 0xbd, 0xe0, 0x00, 0x37, 0x96, 0x1e, 0x0f, 0xb4, 0x99, 0xe7, 0x03, - 0x0d, 0xd0, 0x08, 0xd9, 0x87, 0xbf, 0x68, 0x70, 0x8c, 0xb3, 0x35, 0x0a, 0x15, 0x53, 0x57, 0x98, - 0xa0, 0x0e, 0x26, 0xa8, 0x2b, 0x4e, 0xa3, 0xee, 0x8f, 0x34, 0x94, 0x36, 0x0f, 0x09, 0xf2, 0x3d, - 0xfb, 0x2e, 0xc6, 0xff, 0x0b, 0x75, 0x77, 0xa0, 0x28, 0xa8, 0xe3, 0x5e, 0xd0, 0xb6, 0x51, 0xf0, - 0x72, 0xf2, 0x04, 0xd1, 0x2d, 0x2f, 0xd8, 0x40, 0xc1, 0xd0, 0x75, 0x0f, 0x63, 0xe9, 0x9a, 0xf9, - 0x27, 0xae, 0x77, 0x31, 0x16, 0xae, 0x09, 0xf1, 0xb3, 0x67, 0x13, 0x9f, 0x3d, 0x4d, 0x7c, 0xee, - 0xdc, 0xc4, 0xe7, 0xa7, 0x10, 0x5f, 0xf8, 0x8f, 0x89, 0x87, 0x09, 0xe2, 0x8b, 0x13, 0xc4, 0x97, - 0xa6, 0x11, 0xaf, 0x43, 0xb5, 0xd9, 0xe7, 0x98, 0x30, 0x8f, 0x92, 0x0f, 0x02, 0x39, 0x8e, 0x8f, - 0xa7, 0x6c, 0x32, 0xeb, 0xbe, 0x51, 0xe0, 0xd2, 0xc4, 0xf4, 0xb5, 0x30, 0x0b, 0x28, 0x61, 0xb2, - 0x44, 0x39, 0x40, 0x95, 0x78, 0x3e, 0xca, 0x99, 0xf9, 0x26, 0x64, 0x0e, 0xa8, 0xcb, 0xca, 0x29, - 0x59, 0xde, 0xfc, 0x78, 0x79, 0x5b, 0xd4, 0x6d, 0x64, 0x44, 0x59, 0x96, 0x34, 0x51, 0x17, 0x20, - 0x1d, 0x62, 0x2e, 0xa9, 0x2f, 0x59, 0x62, 0xa9, 0x56, 0x20, 0x1f, 0xf9, 0x6d, 0x1c, 0x86, 0x34, - 0x4c, 0x26, 0x5c, 0x2e, 0xf2, 0x9b, 0x42, 0x14, 0x2a, 0x41, 0x7a, 0x8f, 0x61, 0x27, 0xa6, 0xcf, - 0xca, 0xb9, 0x88, 0xed, 0x32, 0xec, 0x24, 0x69, 0x7e, 0xa6, 0xc0, 0xfc, 0x36, 0x73, 0x77, 0x03, - 0x07, 0x71, 0xbc, 0x83, 0x42, 0xe4, 0x33, 0x31, 0x1f, 0x50, 0x8f, 0x77, 0x69, 0xe8, 0xf1, 0xc3, - 0xa4, 0x8f, 0xcb, 0x3f, 0x3c, 0x5a, 0x59, 0x4c, 0x9e, 0xb0, 0x75, 0xc7, 0x09, 0x31, 0x63, 0x1f, - 0xf2, 0xd0, 0x23, 0xae, 0x75, 0x6c, 0xaa, 0xde, 0x84, 0x6c, 0x20, 0x23, 0xc8, 0x9e, 0x2d, 0xd6, - 0xd5, 0xf1, 0x32, 0xe2, 0xd8, 0x49, 0x25, 0x89, 0xdd, 0xda, 0xdc, 0x83, 0xdf, 0xbf, 0x7d, 0xeb, - 0x38, 0x82, 0x5e, 0x81, 0xcb, 0x27, 0x92, 0x19, 0xa2, 0xa6, 0x3f, 0x54, 0xe0, 0x95, 0x6d, 0xe6, - 0x6e, 0x84, 0x18, 0x71, 0x7c, 0xb7, 0x47, 0x5a, 0x74, 0x1f, 0x13, 0x75, 0x17, 0x40, 0xbc, 0x2f, - 0x6d, 0x1c, 0xda, 0xf5, 0x9b, 0x49, 0xae, 0xb7, 0x1f, 0x0f, 0x34, 0xe5, 0xe9, 0x40, 0x33, 0x5c, - 0x8f, 0x77, 0x7b, 0x1d, 0xc3, 0xa6, 0xbe, 0xf9, 0xbe, 0xd7, 0xf1, 0xc2, 0x9e, 0xbc, 0x6f, 0x26, - 0x91, 0x6b, 0x33, 0xaa, 0x9b, 0x22, 0xbd, 0xe6, 0xbd, 0x9d, 0x5b, 0xb7, 0x44, 0x49, 0x56, 0x41, - 0x44, 0x6a, 0x8a, 0x40, 0xea, 0x0d, 0x98, 0x97, 0x61, 0x3b, 0x88, 0xec, 0xb7, 0x1d, 0x4c, 0xa8, - 0x1f, 0xbf, 0x45, 0xd6, 0x05, 0xb1, 0xdd, 0x40, 0x64, 0x7f, 0x53, 0x6c, 0xaa, 0xaf, 0x42, 0x96, - 0x61, 0xe2, 0xe0, 0x30, 0xbe, 0x89, 0x56, 0x22, 0xe9, 0x1d, 0xa8, 0x9c, 0xca, 0x75, 0xc4, 0x7f, - 0x13, 0x16, 0xf6, 0x7a, 0x84, 0x8b, 0xbd, 0xb6, 0x8f, 0x82, 0xc0, 0x23, 0xee, 0xe8, 0x45, 0x1e, - 0x03, 0x6c, 0xe8, 0x97, 0x40, 0x36, 0x3f, 0xf4, 0xd9, 0x8e, 0x5d, 0xf4, 0x9f, 0x14, 0xb8, 0x28, - 0x0e, 0xa1, 0x24, 0xc2, 0x21, 0xdf, 0xa0, 0x1e, 0x69, 0xd1, 0x66, 0xe4, 0xab, 0xf7, 0xa1, 0xc8, - 0x69, 0x1b, 0xf3, 0x6e, 0x1b, 0x39, 0x4e, 0x38, 0x86, 0xc9, 0xcc, 0x79, 0x30, 0xe1, 0xb4, 0xc9, - 0xbb, 0x62, 0x39, 0x56, 0x6b, 0x6a, 0xbc, 0x56, 0x75, 0x07, 0x0a, 0x12, 0x26, 0xf1, 0xf3, 0x91, - 0x30, 0x14, 0xeb, 0x15, 0x23, 0x69, 0x15, 0xf1, 0x35, 0x32, 0x92, 0xaf, 0x91, 0x21, 0x52, 0x6c, - 0x94, 0x45, 0x22, 0x2f, 0x06, 0xda, 0xc2, 0x21, 0xf2, 0x0f, 0xd6, 0xf4, 0x91, 0xa7, 0x6e, 0xe5, - 0xc5, 0x5a, 0xd8, 0xe8, 0xd7, 0xe0, 0xca, 0xdf, 0x14, 0x36, 0xc4, 0xaf, 0xfe, 0x67, 0x0a, 0xd2, - 0xdb, 0xcc, 0x55, 0x09, 0xc0, 0xd8, 0xdf, 0xa6, 0x32, 0x8e, 0xdd, 0xc4, 0xc5, 0xab, 0xbe, 0x36, - 0x55, 0x35, 0xea, 0x2e, 0xfd, 0xc1, 0x8f, 0xbf, 0x7d, 0x95, 0xba, 0xaa, 0x57, 0x87, 0x48, 0x0c, - 0x3f, 0x67, 0x89, 0x69, 0x9b, 0xf7, 0xd5, 0x1d, 0x28, 0x4d, 0x5c, 0x93, 0x2b, 0x27, 0xc2, 0x8e, - 0x2b, 0xab, 0xd7, 0xcf, 0x50, 0x8e, 0x3a, 0xe1, 0x3e, 0xcc, 0x9d, 0xe8, 0xe7, 0x6b, 0x27, 0xdc, - 0x26, 0xd5, 0xd5, 0xd7, 0xcf, 0x54, 0x8f, 0xe2, 0x7e, 0x0c, 0x0b, 0xa7, 0xda, 0x42, 0x3b, 0xe9, - 0x7a, 0xc2, 0xa0, 0xfa, 0xc6, 0x4b, 0x0c, 0x86, 0xd1, 0x1b, 0xef, 0x3c, 0x3e, 0xaa, 0x29, 0x4f, - 0x8e, 0x6a, 0xca, 0xaf, 0x47, 0x35, 0xe5, 0x8b, 0x67, 0xb5, 0x99, 0x27, 0xcf, 0x6a, 0x33, 0x3f, - 0x3f, 0xab, 0xcd, 0x7c, 0x74, 0xe3, 0xa5, 0xdd, 0xd5, 0x17, 0xc0, 0x76, 0xb2, 0xf2, 0xc7, 0xf9, - 0xf6, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xcc, 0xdd, 0x8f, 0x11, 0x7c, 0x0b, 0x00, 0x00, + // 1322 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcf, 0x6f, 0x1b, 0xc5, + 0x17, 0xcf, 0xda, 0x8e, 0x7f, 0x3c, 0xbb, 0x49, 0xbe, 0xdb, 0xf4, 0x1b, 0xdb, 0x6d, 0xbd, 0x61, + 0x2b, 0x4a, 0x40, 0xca, 0x6e, 0x63, 0xd4, 0x4a, 0xcd, 0x89, 0x38, 0x71, 0x51, 0x51, 0x02, 0xd1, + 0xe2, 0xf4, 0x80, 0x90, 0xac, 0xb1, 0x77, 0xb2, 0x5e, 0x25, 0x3b, 0xb3, 0xda, 0x19, 0xaf, 0x1c, + 0x8e, 0x3d, 0x21, 0x71, 0x00, 0xc4, 0x3f, 0xc0, 0x81, 0x53, 0x4f, 0x1c, 0x7a, 0xe0, 0x4f, 0xa8, + 0x38, 0x55, 0x80, 0x04, 0x2a, 0x92, 0x41, 0x29, 0x12, 0x52, 0x8f, 0x3d, 0x70, 0x46, 0x33, 0xbb, + 0xfe, 0x95, 0xd4, 0x29, 0x04, 0xc4, 0x6d, 0xde, 0xbc, 0x1f, 0xf3, 0xde, 0xe7, 0xf3, 0xe6, 0xcd, + 0xc0, 0x45, 0xcc, 0x3b, 0x26, 0x0e, 0x3d, 0x33, 0x5c, 0x33, 0x79, 0xcf, 0xf0, 0x03, 0xca, 0xa9, + 0x0a, 0x98, 0x77, 0x0c, 0x1c, 0x7a, 0x46, 0xb8, 0x56, 0xae, 0xb4, 0x29, 0xf3, 0x28, 0x33, 0x5b, + 0x88, 0x61, 0x33, 0x5c, 0x6b, 0x61, 0x8e, 0xd6, 0xcc, 0x36, 0x75, 0x49, 0x64, 0x5b, 0x5e, 0x8a, + 0xf5, 0x1e, 0x73, 0x44, 0x0c, 0x8f, 0x39, 0xb1, 0xa2, 0x14, 0x29, 0x9a, 0x52, 0x32, 0x23, 0x21, + 0x56, 0x2d, 0x8e, 0x1d, 0x2a, 0x8e, 0x89, 0x77, 0x1d, 0xea, 0xd0, 0xc8, 0x5a, 0xac, 0xe2, 0xdd, + 0x2b, 0x0e, 0xa5, 0xce, 0x21, 0x36, 0x91, 0xef, 0x9a, 0x88, 0x10, 0xca, 0x11, 0x77, 0x29, 0x19, + 0x44, 0x2a, 0xc5, 0x5a, 0x29, 0xb5, 0xba, 0xfb, 0x26, 0x22, 0x47, 0x91, 0x4a, 0xff, 0x54, 0x81, + 0x0b, 0x3b, 0xcc, 0xa9, 0xf3, 0x0e, 0x0e, 0x70, 0xd7, 0x6b, 0xf4, 0xd4, 0x15, 0x48, 0xd9, 0x88, + 0xa3, 0xa2, 0xb2, 0xac, 0xac, 0xe4, 0xab, 0x8b, 0x46, 0xe4, 0x6b, 0x0c, 0x7c, 0x8d, 0x0d, 0x72, + 0x64, 0x49, 0x0b, 0xb5, 0x04, 0x29, 0xe6, 0x7e, 0x84, 0x8b, 0x89, 0x65, 0x65, 0x45, 0xa9, 0xcd, + 0x3e, 0xeb, 0x6b, 0xca, 0xaa, 0x25, 0xb7, 0x54, 0x0d, 0x52, 0x1d, 0xc4, 0x3a, 0xc5, 0xe4, 0xb2, + 0xb2, 0x92, 0xab, 0xe5, 0x9f, 0xf7, 0xb5, 0x4c, 0x70, 0xe8, 0xaf, 0xeb, 0xab, 0xba, 0x25, 0x15, + 0xaa, 0x0a, 0xa9, 0xfd, 0x80, 0x7a, 0xc5, 0x94, 0x30, 0xb0, 0xe4, 0x7a, 0x3d, 0xf5, 0xf1, 0x97, + 0xda, 0x8c, 0xfe, 0x79, 0x02, 0xb2, 0xdb, 0xd8, 0x41, 0xed, 0xa3, 0x46, 0x4f, 0x5d, 0x84, 0x59, + 0x42, 0x49, 0x1b, 0xcb, 0x6c, 0x52, 0x56, 0x24, 0xa8, 0xb7, 0x20, 0xe7, 0x20, 0x81, 0x99, 0xdb, + 0x8e, 0x4e, 0xcf, 0xd5, 0x4a, 0x4f, 0xfa, 0xda, 0xa5, 0x08, 0x3e, 0x66, 0x1f, 0x18, 0x2e, 0x35, + 0x3d, 0xc4, 0x3b, 0xc6, 0x5d, 0xc2, 0xad, 0xac, 0x83, 0xd8, 0xae, 0x30, 0x55, 0x2b, 0x90, 0x74, + 0x10, 0x93, 0x49, 0xa5, 0x6a, 0x85, 0xe3, 0xbe, 0x96, 0x7d, 0x1b, 0xb1, 0x6d, 0xd7, 0x73, 0xb9, + 0x25, 0x14, 0xea, 0x1c, 0x24, 0x38, 0x8d, 0x53, 0x4a, 0x70, 0xaa, 0xde, 0x86, 0xd9, 0x10, 0x1d, + 0x76, 0x71, 0x71, 0x56, 0x9e, 0x71, 0x6d, 0xea, 0x19, 0xc7, 0x7d, 0x2d, 0xbd, 0xe1, 0xd1, 0x2e, + 0xe1, 0x56, 0xe4, 0x21, 0xea, 0x93, 0x28, 0xa6, 0x97, 0x95, 0x95, 0x42, 0x8c, 0x57, 0x01, 0x94, + 0xb0, 0x98, 0x91, 0x1b, 0x4a, 0x28, 0xa4, 0xa0, 0x98, 0x8d, 0xa4, 0x40, 0x48, 0xac, 0x98, 0x8b, + 0x24, 0xb6, 0x3e, 0x27, 0x90, 0xf8, 0xf6, 0xe1, 0x6a, 0xba, 0xd1, 0xdb, 0x42, 0x1c, 0xe9, 0xdf, + 0x24, 0xa1, 0xb0, 0xd1, 0x6e, 0x63, 0xc6, 0xb6, 0x5d, 0xc6, 0x1b, 0x3d, 0xf5, 0x1d, 0xc8, 0xb6, + 0x3b, 0xc8, 0x25, 0x4d, 0xd7, 0x96, 0xd0, 0xe4, 0x6a, 0xe6, 0x59, 0xc9, 0x65, 0x36, 0x85, 0xf1, + 0xdd, 0xad, 0x67, 0x7d, 0x2d, 0xd3, 0x8e, 0x96, 0x56, 0xbc, 0xb0, 0x47, 0x18, 0x27, 0xa6, 0x62, + 0x9c, 0xfc, 0xdb, 0x18, 0xa7, 0xce, 0xc6, 0x78, 0xf6, 0x34, 0xc6, 0xe9, 0x73, 0x63, 0x9c, 0x19, + 0xc3, 0x78, 0x0f, 0xb2, 0x48, 0x02, 0x85, 0x59, 0x31, 0xbb, 0x9c, 0x5c, 0xc9, 0x57, 0x97, 0x8c, + 0xd1, 0x3d, 0x35, 0x22, 0x10, 0x1b, 0x5d, 0xff, 0x10, 0xd7, 0x96, 0x1f, 0xf5, 0xb5, 0x99, 0x67, + 0x7d, 0x0d, 0xd0, 0x10, 0xd9, 0x07, 0xbf, 0x68, 0x30, 0xc2, 0xd9, 0x1a, 0x86, 0x8a, 0xa8, 0xcb, + 0x4d, 0x50, 0x07, 0x13, 0xd4, 0xe5, 0xa7, 0x51, 0xf7, 0x47, 0x12, 0x0a, 0x5b, 0x47, 0x04, 0x79, + 0x6e, 0xfb, 0x0e, 0xc6, 0xff, 0x09, 0x75, 0xb7, 0x21, 0x2f, 0xa8, 0xe3, 0xae, 0xdf, 0x6c, 0x23, + 0xff, 0xe5, 0xe4, 0x09, 0xa2, 0x1b, 0xae, 0xbf, 0x89, 0xfc, 0x81, 0xeb, 0x3e, 0xc6, 0xd2, 0x35, + 0xf5, 0x57, 0x5c, 0xef, 0x60, 0x2c, 0x5c, 0x63, 0xe2, 0x67, 0xcf, 0x26, 0x3e, 0x7d, 0x9a, 0xf8, + 0xcc, 0xb9, 0x89, 0xcf, 0x4e, 0x21, 0x3e, 0xf7, 0x2f, 0x13, 0x0f, 0x13, 0xc4, 0xe7, 0x27, 0x88, + 0x2f, 0x4c, 0x23, 0x5e, 0x87, 0x72, 0xbd, 0xc7, 0x31, 0x61, 0x2e, 0x25, 0xef, 0xf9, 0x72, 0x1c, + 0x8f, 0xa6, 0x6c, 0x3c, 0xeb, 0xbe, 0x52, 0xe0, 0xd2, 0xc4, 0xf4, 0xb5, 0x30, 0xf3, 0x29, 0x61, + 0xb2, 0x44, 0x39, 0x40, 0x95, 0x68, 0x3e, 0xca, 0x99, 0xf9, 0x3a, 0xa4, 0x0e, 0xa9, 0xc3, 0x8a, + 0x09, 0x59, 0xde, 0xfc, 0x78, 0x79, 0xdb, 0xd4, 0xa9, 0xa5, 0x44, 0x59, 0x96, 0x34, 0x51, 0x17, + 0x20, 0x19, 0x60, 0x2e, 0xa9, 0x2f, 0x58, 0x62, 0xa9, 0x96, 0x20, 0x1b, 0x7a, 0x4d, 0x1c, 0x04, + 0x34, 0x88, 0x27, 0x5c, 0x26, 0xf4, 0xea, 0x42, 0x14, 0x2a, 0x41, 0x7a, 0x97, 0x61, 0x3b, 0xa2, + 0xcf, 0xca, 0x38, 0x88, 0xed, 0x31, 0x6c, 0xc7, 0x69, 0x7e, 0xa2, 0xc0, 0xfc, 0x0e, 0x73, 0xf6, + 0x7c, 0x1b, 0x71, 0xbc, 0x8b, 0x02, 0xe4, 0x31, 0x31, 0x1f, 0x50, 0x97, 0x77, 0x68, 0xe0, 0xf2, + 0xa3, 0xb8, 0x8f, 0x8b, 0xdf, 0x3d, 0x5c, 0x5d, 0x8c, 0x9f, 0xb0, 0x0d, 0xdb, 0x0e, 0x30, 0x63, + 0xef, 0xf3, 0xc0, 0x25, 0x8e, 0x35, 0x32, 0x55, 0x6f, 0x40, 0xda, 0x97, 0x11, 0x64, 0xcf, 0xe6, + 0xab, 0xea, 0x78, 0x19, 0x51, 0xec, 0xb8, 0x92, 0xd8, 0x6e, 0x7d, 0xee, 0xfe, 0xef, 0x5f, 0xbf, + 0x31, 0x8a, 0xa0, 0x97, 0x60, 0xe9, 0x44, 0x32, 0x03, 0xd4, 0xf4, 0x07, 0x0a, 0xfc, 0x6f, 0x87, + 0x39, 0x9b, 0x01, 0x46, 0x1c, 0xdf, 0xe9, 0x92, 0x06, 0x3d, 0xc0, 0x44, 0xdd, 0x03, 0x10, 0xef, + 0x4b, 0x13, 0x07, 0xed, 0xea, 0x8d, 0x38, 0xd7, 0x5b, 0x8f, 0xfa, 0x9a, 0xf2, 0xa4, 0xaf, 0x19, + 0x8e, 0xcb, 0x3b, 0xdd, 0x96, 0xd1, 0xa6, 0x9e, 0xf9, 0xae, 0xdb, 0x72, 0x83, 0xae, 0xbc, 0x6f, + 0x26, 0x91, 0x6b, 0x33, 0xac, 0x9a, 0x22, 0xbd, 0xfa, 0xdd, 0xdd, 0x9b, 0x37, 0x45, 0x49, 0x56, + 0x4e, 0x44, 0xaa, 0x8b, 0x40, 0xea, 0x75, 0x98, 0x97, 0x61, 0x5b, 0x88, 0x1c, 0x34, 0x6d, 0x4c, + 0xa8, 0x17, 0xbd, 0x45, 0xd6, 0x05, 0xb1, 0x5d, 0x43, 0xe4, 0x60, 0x4b, 0x6c, 0xaa, 0xff, 0x87, + 0x34, 0xc3, 0xc4, 0xc6, 0x41, 0x74, 0x13, 0xad, 0x58, 0xd2, 0x5b, 0x50, 0x3a, 0x95, 0xeb, 0x90, + 0xff, 0x3a, 0x2c, 0xec, 0x77, 0x09, 0x17, 0x7b, 0x4d, 0x0f, 0xf9, 0xbe, 0x4b, 0x9c, 0xe1, 0x8b, + 0x3c, 0x06, 0xd8, 0xc0, 0x2f, 0x86, 0x6c, 0x7e, 0xe0, 0xb3, 0x13, 0xb9, 0xe8, 0x3f, 0x2a, 0x70, + 0x51, 0x1c, 0x42, 0x49, 0x88, 0x03, 0xbe, 0x49, 0x5d, 0xd2, 0xa0, 0xf5, 0xd0, 0x53, 0xef, 0x41, + 0x9e, 0xd3, 0x26, 0xe6, 0x9d, 0x26, 0xb2, 0xed, 0x60, 0x0c, 0x93, 0x99, 0xf3, 0x60, 0xc2, 0x69, + 0x9d, 0x77, 0xc4, 0x72, 0xac, 0xd6, 0xc4, 0x78, 0xad, 0xea, 0x2e, 0xe4, 0x24, 0x4c, 0xe2, 0x4b, + 0x24, 0x61, 0xc8, 0x57, 0x4b, 0x46, 0xdc, 0x2a, 0xe2, 0xcf, 0x64, 0xc4, 0x7f, 0x26, 0x43, 0xa4, + 0x58, 0x2b, 0x8a, 0x44, 0x9e, 0xf7, 0xb5, 0x85, 0x23, 0xe4, 0x1d, 0xae, 0xeb, 0x43, 0x4f, 0xdd, + 0xca, 0x8a, 0xb5, 0xb0, 0xd1, 0xaf, 0xc2, 0xe5, 0x17, 0x14, 0x36, 0xec, 0x84, 0x1f, 0x26, 0x0a, + 0xaf, 0x87, 0x5e, 0x83, 0x0a, 0xa3, 0xb1, 0x04, 0x95, 0x89, 0x04, 0xf7, 0x00, 0x64, 0x7b, 0x44, + 0x78, 0x24, 0xfe, 0x19, 0x1e, 0x32, 0x92, 0xc4, 0xe3, 0x26, 0xa4, 0x91, 0x1c, 0x5d, 0xf1, 0x14, + 0xbe, 0x1a, 0x87, 0x9c, 0x32, 0x4e, 0x63, 0x63, 0x75, 0x09, 0x32, 0x9c, 0x46, 0xa9, 0x44, 0x77, + 0x35, 0xcd, 0xa9, 0x88, 0x37, 0x59, 0xf5, 0xb0, 0xaa, 0x41, 0xd5, 0xd5, 0x9f, 0x93, 0x90, 0xdc, + 0x61, 0x8e, 0x4a, 0x00, 0xc6, 0x7e, 0x74, 0xa5, 0xf1, 0x8e, 0x99, 0x18, 0x37, 0xe5, 0x57, 0xa6, + 0xaa, 0x86, 0x48, 0xea, 0xf7, 0xbf, 0xff, 0xed, 0x8b, 0xc4, 0x15, 0xbd, 0x3c, 0xa8, 0x77, 0xf0, + 0x25, 0x8d, 0x4d, 0x9b, 0xbc, 0xa7, 0xee, 0x42, 0x61, 0x62, 0x38, 0x5c, 0x3e, 0x11, 0x76, 0x5c, + 0x59, 0xbe, 0x76, 0x86, 0x72, 0xd8, 0xff, 0xf7, 0x60, 0xee, 0xc4, 0x2d, 0xbe, 0x7a, 0xc2, 0x6d, + 0x52, 0x5d, 0x7e, 0xf5, 0x4c, 0xf5, 0x30, 0xee, 0x87, 0xb0, 0x70, 0xea, 0x32, 0x68, 0x27, 0x5d, + 0x4f, 0x18, 0x94, 0x5f, 0x7b, 0x89, 0xc1, 0x0b, 0xa2, 0x8f, 0x3a, 0x6e, 0x4a, 0xf4, 0xa1, 0xc1, + 0xb4, 0xe8, 0xa7, 0xd8, 0xad, 0xbd, 0xf5, 0xe8, 0xb8, 0xa2, 0x3c, 0x3e, 0xae, 0x28, 0xbf, 0x1e, + 0x57, 0x94, 0xcf, 0x9e, 0x56, 0x66, 0x1e, 0x3f, 0xad, 0xcc, 0xfc, 0xf4, 0xb4, 0x32, 0xf3, 0xc1, + 0xf5, 0x97, 0x76, 0x68, 0x4f, 0xd0, 0xd6, 0x4a, 0xcb, 0x5f, 0xfc, 0x9b, 0x7f, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x3f, 0xeb, 0xa3, 0xa9, 0xd0, 0x0c, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -796,6 +907,9 @@ type MsgClient interface { // given recipient address ("to_eth_addr") in the corresponding ERC20 // representation. ConvertCoinToEvm(ctx context.Context, in *MsgConvertCoinToEvm, opts ...grpc.CallOption) (*MsgConvertCoinToEvmResponse, error) + // ConvertEvmToCoin: Sends an ERC20 token with a valid "FunToken" mapping to the + // given recipient address as a bank coin. + ConvertEvmToCoin(ctx context.Context, in *MsgConvertEvmToCoin, opts ...grpc.CallOption) (*MsgConvertEvmToCoinResponse, error) } type msgClient struct { @@ -842,6 +956,15 @@ func (c *msgClient) ConvertCoinToEvm(ctx context.Context, in *MsgConvertCoinToEv return out, nil } +func (c *msgClient) ConvertEvmToCoin(ctx context.Context, in *MsgConvertEvmToCoin, opts ...grpc.CallOption) (*MsgConvertEvmToCoinResponse, error) { + out := new(MsgConvertEvmToCoinResponse) + err := c.cc.Invoke(ctx, "/eth.evm.v1.Msg/ConvertEvmToCoin", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // EthereumTx defines a method submitting Ethereum transactions. @@ -858,6 +981,9 @@ type MsgServer interface { // given recipient address ("to_eth_addr") in the corresponding ERC20 // representation. ConvertCoinToEvm(context.Context, *MsgConvertCoinToEvm) (*MsgConvertCoinToEvmResponse, error) + // ConvertEvmToCoin: Sends an ERC20 token with a valid "FunToken" mapping to the + // given recipient address as a bank coin. + ConvertEvmToCoin(context.Context, *MsgConvertEvmToCoin) (*MsgConvertEvmToCoinResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -876,6 +1002,9 @@ func (*UnimplementedMsgServer) CreateFunToken(ctx context.Context, req *MsgCreat func (*UnimplementedMsgServer) ConvertCoinToEvm(ctx context.Context, req *MsgConvertCoinToEvm) (*MsgConvertCoinToEvmResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ConvertCoinToEvm not implemented") } +func (*UnimplementedMsgServer) ConvertEvmToCoin(ctx context.Context, req *MsgConvertEvmToCoin) (*MsgConvertEvmToCoinResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConvertEvmToCoin not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -953,6 +1082,24 @@ func _Msg_ConvertCoinToEvm_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Msg_ConvertEvmToCoin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgConvertEvmToCoin) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ConvertEvmToCoin(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/eth.evm.v1.Msg/ConvertEvmToCoin", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ConvertEvmToCoin(ctx, req.(*MsgConvertEvmToCoin)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "eth.evm.v1.Msg", HandlerType: (*MsgServer)(nil), @@ -973,6 +1120,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "ConvertCoinToEvm", Handler: _Msg_ConvertCoinToEvm_Handler, }, + { + MethodName: "ConvertEvmToCoin", + Handler: _Msg_ConvertEvmToCoin_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "eth/evm/v1/tx.proto", @@ -1677,6 +1828,86 @@ func (m *MsgConvertCoinToEvmResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } +func (m *MsgConvertEvmToCoin) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertEvmToCoin) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertEvmToCoin) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ToAddr) > 0 { + i -= len(m.ToAddr) + copy(dAtA[i:], m.ToAddr) + i = encodeVarintTx(dAtA, i, uint64(len(m.ToAddr))) + i-- + dAtA[i] = 0x22 + } + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + { + size := m.Erc20Addr.Size() + i -= size + if _, err := m.Erc20Addr.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTx(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgConvertEvmToCoinResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgConvertEvmToCoinResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgConvertEvmToCoinResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -1986,6 +2217,36 @@ func (m *MsgConvertCoinToEvmResponse) Size() (n int) { return n } +func (m *MsgConvertEvmToCoin) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Erc20Addr.Size() + n += 1 + l + sovTx(uint64(l)) + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + l = len(m.ToAddr) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + +func (m *MsgConvertEvmToCoinResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4161,6 +4422,238 @@ func (m *MsgConvertCoinToEvmResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgConvertEvmToCoin) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertEvmToCoin: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertEvmToCoin: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20Addr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Erc20Addr.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ToAddr", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ToAddr = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgConvertEvmToCoinResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgConvertEvmToCoinResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgConvertEvmToCoinResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/evm/vmtracer.go b/x/evm/vmtracer.go index 1cc1968c82..b8881ee953 100644 --- a/x/evm/vmtracer.go +++ b/x/evm/vmtracer.go @@ -29,7 +29,7 @@ func NewTracer( ) *tracing.Hooks { // TODO: feat(evm-vmtracer): enable additional log configuration logCfg := &logger.Config{ - Debug: true, + Debug: false, } switch tracer { @@ -56,7 +56,7 @@ func NewTracer( func NewDefaultTracer() *tracers.Tracer { logCfg := &logger.Config{ - Debug: true, + Debug: false, } defaultLogger := logger.NewStructLogger(logCfg) return &tracers.Tracer{ diff --git a/x/tokenfactory/cli/tx.go b/x/tokenfactory/cli/tx.go index d2c463f3a9..6f2def20d6 100644 --- a/x/tokenfactory/cli/tx.go +++ b/x/tokenfactory/cli/tx.go @@ -409,10 +409,10 @@ func parseMetadataFromFile(path string) (bank.Metadata, error) { var md bank.Metadata bz, err := os.ReadFile(path) if err != nil { - return md, fmt.Errorf("read file %q: %w", path, err) + return md, fmt.Errorf("read file %+q: %w", path, err) } if err := json.Unmarshal(bz, &md); err != nil { - return md, fmt.Errorf("unmarshal %q as bank.Metadata: %w", path, err) + return md, fmt.Errorf("unmarshal %+q as bank.Metadata: %w", path, err) } return md, nil }