diff --git a/pkg/core/native/native_nep17.go b/pkg/core/native/native_nep17.go index 6a2c42c02..da449f744 100644 --- a/pkg/core/native/native_nep17.go +++ b/pkg/core/native/native_nep17.go @@ -180,6 +180,9 @@ func (c *nep17TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint16 if amount.Sign() < 0 { return nil, errors.New("insufficient funds") } + if requiredBalance != nil && requiredBalance.Sign() > 0 { + return nil, errors.New("insufficient funds") + } if amount.Sign() == 0 { // it's OK to transfer 0 if the balance is 0, no need to put si to the storage return nil, nil diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go index e0a46f5d2..bd8c2b43e 100644 --- a/pkg/core/native/native_test/neo_test.go +++ b/pkg/core/native/native_test/neo_test.go @@ -595,6 +595,22 @@ func TestNEO_TransferZeroWithNonZeroBalance(t *testing.T) { }) } +// https://github.com/nspcc-dev/neo-go/issues/3190 +func TestNEO_TransferNonZeroWithZeroBalance(t *testing.T) { + neoValidatorsInvoker := newNeoValidatorsClient(t) + e := neoValidatorsInvoker.Executor + + acc := neoValidatorsInvoker.WithSigners(e.NewAccount(t)) + accH := acc.Signers[0].ScriptHash() + h := acc.Invoke(t, false, "transfer", accH, accH, int64(5), nil) + aer := e.CheckHalt(t, h, stackitem.Make(false)) + require.Equal(t, 0, len(aer.Events)) + // check balance wasn't changed and height was not updated + updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(accH) + require.Equal(t, int64(0), updatedBalance.Int64()) + require.Equal(t, uint32(0), updatedHeight) +} + func TestNEO_CalculateBonus(t *testing.T) { neoCommitteeInvoker := newNeoCommitteeClient(t, 10_0000_0000) e := neoCommitteeInvoker.Executor