Skip to content

Commit 2748fe2

Browse files
fix(evm): disallow permissionless creation of FunToken mappings when tokens do not already have metadata. (#2379)
* wip!: impl checkpoint with passing tests before change to use of commit constants * wip!: refactor: working checkpoint where CallContractWithInput only ever accepts commit=false * wip!: checkpoint with consistent use of commit=false * finish impl * wip!: save for now to implement this feature after [feat(evm): Implement a state (ctx) serializable EVM StateDB to make asynchronous access more safe](#2378) * revert precompile edits to do them in a future upgrade * wip!: all passing tests. want to add more * finish impl * docs: fix ultra small typo * fix v2_5_0_test to start from 0 decimals * fix(evm): robustness improvments on metadata constructor * --amend * fix(funtoken_from_erc20): add robustness checks against ERC20.symbols with deterministic display denom during bank.Metadata generation * fix(funtoken_from_erc20): pass through ERC20 name and symbol in raw form to allow bank.Metadata.Validate to protect against empty values
1 parent 2d0d391 commit 2748fe2

37 files changed

+1460
-782
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ handling for NIBI via WNIBI.
5656
- [#2371](https://github.com/NibiruChain/nibiru/pull/2371) - feat(evm): fix UnmarshalJSON to accept ASCII hex strings
5757
- [#2372](https://github.com/NibiruChain/nibiru/pull/2372) - feat(tokenfactory-cli): add CLI commands for set denom functions
5858
- [#2375](https://github.com/NibiruChain/nibiru/pull/2375) - feat(evm): Inject WNIBI.sol for non-mainnet networks in the v2.7.0 upgrade handler
59+
- [#2379](https://github.com/NibiruChain/nibiru/pull/2379) - fix(evm): disallow permissionless creation of FunToken mappings when tokens do not already have metadata.
5960

6061
### Dependencies
6162
- Bump `base-x` from 3.0.10 to 3.0.11 ([#2355](https://github.com/NibiruChain/nibiru/pull/2355))

api/eth/evm/v1/tx.pulsar.go

Lines changed: 77 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/upgrades/v2_5_0/v2_5_0.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,18 @@ func UpgradeStNibiContractOnMainnet(
156156
}()
157157
evmObj := keepers.EvmKeeper.NewEVM(ctx, evmMsg, keepers.EvmKeeper.GetEVMConfig(ctx), nil, stateDB)
158158

159-
evmResp, err := keepers.EvmKeeper.CallContractWithInput(
160-
ctx, evmObj, evmMsg.From, nil, true /*commit*/, contractInput,
161-
evmkeeper.Erc20GasLimitDeploy, nil,
159+
evmResp, err := keepers.EvmKeeper.CallContract(
160+
ctx, evmObj, evmMsg.From, nil, contractInput,
161+
evmkeeper.Erc20GasLimitDeploy,
162+
evm.COMMIT_ETH_TX, /*commit*/
163+
nil,
162164
)
163165
if err != nil {
164166
return fmt.Errorf("failed to deploy ERC20 contract: %w", err)
165167
} else if len(evmResp.VmError) > 0 {
166168
return fmt.Errorf("VM Error in deploy ERC20: %s", evmResp.VmError)
169+
} else if err := stateDB.Commit(); err != nil {
170+
return fmt.Errorf("%s: %w", evm.ErrStateDBCommit, err)
167171
}
168172

169173
evmLogs = append(evmLogs, evmResp.Logs...)

app/upgrades/v2_5_0/v2_5_0_test.go

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
sdkmath "cosmossdk.io/math"
1010
sdk "github.com/cosmos/cosmos-sdk/types"
11+
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
1112
gogoproto "github.com/cosmos/gogoproto/proto"
1213
gethcommon "github.com/ethereum/go-ethereum/common"
1314
"github.com/stretchr/testify/suite"
@@ -39,9 +40,7 @@ func (s *Suite) TestUpgrade() {
3940
}.DefaultBankMetadata()
4041

4142
// FunToken mapping for stNIBI
42-
funtoken = evmtest.CreateFunTokenForBankCoin(
43-
deps, originalbankMetadata.Base, &s.Suite,
44-
)
43+
funtoken evm.FunToken
4544

4645
// ten holders for testing
4746
holders = []gethcommon.Address{
@@ -58,6 +57,65 @@ func (s *Suite) TestUpgrade() {
5857
}
5958
)
6059

60+
{
61+
bankDenom := originalbankMetadata.Base
62+
63+
if deps.App.BankKeeper.HasDenomMetaData(deps.Ctx, bankDenom) {
64+
s.Failf("setting bank.DenomMetadata would overwrite existing denom \"%s\"", bankDenom)
65+
}
66+
67+
s.T().Log("Setup: Create a coin in the bank state")
68+
bankMetadata := bank.Metadata{
69+
DenomUnits: []*bank.DenomUnit{
70+
{
71+
Denom: bankDenom,
72+
Exponent: 0,
73+
},
74+
},
75+
Base: bankDenom,
76+
Display: bankDenom,
77+
Name: bankDenom,
78+
Symbol: bankDenom,
79+
}
80+
81+
deps.App.BankKeeper.SetDenomMetaData(deps.Ctx, bankMetadata)
82+
83+
// Give the sender funds for the fee
84+
s.Require().NoError(testapp.FundAccount(
85+
deps.App.BankKeeper,
86+
deps.Ctx,
87+
deps.Sender.NibiruAddr,
88+
deps.EvmKeeper.FeeForCreateFunToken(deps.Ctx),
89+
))
90+
91+
s.T().Log("happy: CreateFunToken for the bank coin")
92+
createFuntokenResp, err := deps.EvmKeeper.CreateFunToken(
93+
sdk.WrapSDKContext(deps.Ctx),
94+
&evm.MsgCreateFunToken{
95+
FromBankDenom: bankDenom,
96+
Sender: deps.Sender.NibiruAddr.String(),
97+
AllowZeroDecimals: true,
98+
},
99+
)
100+
s.Require().NoError(err, "bankDenom %s", bankDenom)
101+
102+
erc20 := createFuntokenResp.FuntokenMapping.Erc20Addr
103+
funtoken = evm.FunToken{
104+
Erc20Addr: erc20,
105+
BankDenom: bankDenom,
106+
IsMadeFromCoin: true,
107+
}
108+
s.Equal(funtoken, createFuntokenResp.FuntokenMapping)
109+
110+
s.T().Log("Expect ERC20 to be deployed")
111+
_, err = deps.EvmKeeper.Code(deps.Ctx,
112+
&evm.QueryCodeRequest{
113+
Address: erc20.String(),
114+
},
115+
)
116+
s.NoError(err)
117+
}
118+
61119
s.T().Log("Adds some tokens in circulation. We'll use these later to create ERC20 holders")
62120
s.NoError(
63121
testapp.FundAccount(
@@ -171,14 +229,14 @@ func (s *Suite) TestUpgrade() {
171229
input, err := compiledContract.ABI.Pack("owner")
172230
s.Require().NoError(err)
173231
evmObj, _ := deps.NewEVMLessVerboseLogger()
174-
evmResp, err := deps.EvmKeeper.CallContractWithInput(
232+
evmResp, err := deps.EvmKeeper.CallContract(
175233
deps.Ctx,
176234
evmObj,
177235
deps.Sender.EthAddr,
178236
&funtoken.Erc20Addr.Address,
179-
false, // commit
180237
input,
181238
evmkeeper.Erc20GasLimitQuery,
239+
evm.COMMIT_READONLY, /*commit*/
182240
nil,
183241
)
184242
s.Require().NoError(err)

eth/rpc/pubsub/pubsub.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
package pubsub
33

44
import (
5+
"fmt"
56
"sync"
67
"sync/atomic"
78

8-
pkgerrors "github.com/pkg/errors"
9-
109
coretypes "github.com/cometbft/cometbft/rpc/core/types"
1110
)
1211

@@ -72,7 +71,7 @@ func (m *memEventBus) AddTopic(name string, src <-chan coretypes.ResultEvent) er
7271
m.topicsMux.RUnlock()
7372

7473
if ok {
75-
return pkgerrors.New("topic already registered")
74+
return fmt.Errorf("topic already registered")
7675
}
7776

7877
m.topicsMux.Lock()
@@ -101,7 +100,7 @@ func (m *memEventBus) Subscribe(name string) (<-chan coretypes.ResultEvent, Unsu
101100
m.topicsMux.RUnlock()
102101

103102
if !ok {
104-
return nil, nil, pkgerrors.Errorf("topic not found: %s", name)
103+
return nil, nil, fmt.Errorf("topic not found: %s", name)
105104
}
106105

107106
ch := make(chan coretypes.ResultEvent)

eth/rpc/rpc.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@ import (
2727
// ignored in JSON-RPC API.
2828
const ErrExceedBlockGasLimit = "out of gas in location: block gas meter; gasWanted:"
2929

30-
// ErrStateDBCommit defines the error message when commit after executing EVM
31-
// transaction, for example transfer native token to a distribution module
32-
// account using an evm transaction. Note, the transfer amount cannot be set to
33-
// 0, otherwise this problem will not be triggered.
34-
const ErrStateDBCommit = "failed to commit stateDB"
35-
3630
// RawTxToEthTx returns a evm MsgEthereum transaction from raw tx bytes.
3731
func RawTxToEthTx(clientCtx client.Context, txBz cmttypes.Tx) ([]*evm.MsgEthereumTx, error) {
3832
tx, err := clientCtx.TxConfig.TxDecoder()(txBz)
@@ -237,7 +231,7 @@ func TxIsValidEnough(res *abci.ResponseDeliverTx) (condition bool, reason string
237231
return true, "tx succeeded"
238232
} else if strings.Contains(res.Log, ErrExceedBlockGasLimit) {
239233
return true, "tx exceeded block gas limit"
240-
} else if strings.Contains(res.Log, ErrStateDBCommit) {
234+
} else if strings.Contains(res.Log, evm.ErrStateDBCommit) {
241235
return true, "tx state db commit error"
242236
}
243237
return false, "unexpected failure"

gosdk/grpc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func GetGRPCConnection(
3737
conn, err := grpc.DialContext(ctx, grpcUrl, options...)
3838
if err != nil {
3939
return nil, fmt.Errorf(
40-
"%w: Cannot connect to gRPC endpoint %s", err, grpcUrl)
40+
"cannot connect to gRPC endpoint %s: %w", grpcUrl, err)
4141
}
4242

4343
return conn, nil

0 commit comments

Comments
 (0)