Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flow EVM] balance type improvement #5271

Merged
merged 11 commits into from
Jan 26, 2024
19 changes: 10 additions & 9 deletions fvm/evm/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func (a *Account) Address() types.Address {
//
// TODO: we might need to meter computation for read only operations as well
// currently the storage limits is enforced
func (a *Account) Balance() types.Balance {
func (a *Account) Balance() *types.Balance {
ctx := a.fch.getBlockContext()

blk, err := a.fch.emulator.NewReadOnlyBlockView(ctx)
Expand All @@ -193,9 +193,7 @@ func (a *Account) Balance() types.Balance {
bl, err := blk.BalanceOf(a.address)
handleError(err)

balance, err := types.NewBalanceFromAttoFlow(bl)
handleError(err)
return balance
return types.NewBalanceFromAttoFlow(bl)
}

// Deposit deposits the token from the given vault into the flow evm main vault
Expand All @@ -213,7 +211,7 @@ func (a *Account) Deposit(v *types.FLOWTokenVault) {

// Withdraw deducts the balance from the account and
// withdraw and return flow token from the Flex main vault.
func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault {
func (a *Account) Withdraw(b *types.Balance) *types.FLOWTokenVault {
a.checkAuthorized()

cfg := a.fch.getBlockContext()
Expand All @@ -226,7 +224,10 @@ func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault {
if b.ToAttoFlow().Cmp(bp.TotalSupply) == 1 {
handleError(types.ErrInsufficientTotalSupply)
}

// Don't allow withdraw for balances that has rounding error
if b.HasUFix64RoundingError() {
handleError(types.ErrWithdrawBalanceRounding)
}
call := types.NewWithdrawCall(
a.address,
b.ToAttoFlow(),
Expand All @@ -237,7 +238,7 @@ func (a *Account) Withdraw(b types.Balance) *types.FLOWTokenVault {
}

// Transfer transfers tokens between accounts
func (a *Account) Transfer(to types.Address, balance types.Balance) {
func (a *Account) Transfer(to types.Address, balance *types.Balance) {
a.checkAuthorized()

ctx := a.fch.getBlockContext()
Expand All @@ -254,7 +255,7 @@ func (a *Account) Transfer(to types.Address, balance types.Balance) {
// Deploy deploys a contract to the EVM environment
// the new deployed contract would be at the returned address and
// the contract data is not controlled by the caller accounts
func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types.Balance) types.Address {
func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance *types.Balance) types.Address {
a.checkAuthorized()
a.fch.checkGasLimit(gaslimit)

Expand All @@ -272,7 +273,7 @@ func (a *Account) Deploy(code types.Code, gaslimit types.GasLimit, balance types
// it would limit the gas used according to the limit provided
// given it doesn't goes beyond what Flow transaction allows.
// the balance would be deducted from the OFA account and would be transferred to the target address
func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimit, balance types.Balance) types.Data {
func (a *Account) Call(to types.Address, data types.Data, gaslimit types.GasLimit, balance *types.Balance) types.Data {
a.checkAuthorized()
a.fch.checkGasLimit(gaslimit)
call := types.NewContractCall(
Expand Down
6 changes: 3 additions & 3 deletions fvm/evm/handler/handler_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) {
// note that trie growth is the function of number of accounts
for i := 0; i < accountCount; i++ {
account := handler.AccountByAddress(handler.AllocateAddress(), true)
account.Deposit(types.NewFlowTokenVault(types.Balance(100)))
account.Deposit(types.NewFlowTokenVault(types.NewBalance(100)))
accounts[i] = account
}
backend.DropEvents()
Expand All @@ -49,7 +49,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) {
genes,
),
300_000_000,
types.Balance(0),
types.NewBalance(0),
)
require.Equal(b, 2, len(backend.Events()))
backend.DropEvents() // this would make things lighter
Expand All @@ -66,7 +66,7 @@ func benchmarkStorageGrowth(b *testing.B, accountCount, setupKittyCount int) {
testutils.RandomBigInt(1000),
),
300_000_000,
types.Balance(0),
types.NewBalance(0),
)

b.ReportMetric(float64(backend.TotalBytesRead()), "bytes_read")
Expand Down
69 changes: 38 additions & 31 deletions fvm/evm/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,21 +199,21 @@ func TestHandler_TransactionRun(t *testing.T) {

// deposit 1 Flow to the foa account
addr := handler.AllocateAddress()
orgBalance, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
require.NoError(t, err)
orgBalance := types.NewBalance(types.OneFlow)
vault := types.NewFlowTokenVault(orgBalance)
foa := handler.AccountByAddress(addr, true)
foa.Deposit(vault)

// transfer 0.1 flow to the non-foa address
deduction, err := types.NewBalanceFromAttoFlow(big.NewInt(1e17))
require.NoError(t, err)
deduction := types.NewBalanceFromAttoFlow(big.NewInt(1e17))
foa.Call(eoa.Address(), nil, 400000, deduction)
require.Equal(t, orgBalance.Sub(deduction), foa.Balance())
expected := orgBalance.Copy()
err := expected.Sub(deduction)
require.NoError(t, err)
require.Equal(t, expected, foa.Balance())

// transfer 0.01 flow back to the foa through
addition, err := types.NewBalanceFromAttoFlow(big.NewInt(1e16))
require.NoError(t, err)
addition := types.NewBalanceFromAttoFlow(big.NewInt(1e16))

tx := eoa.PrepareSignAndEncodeTx(
t,
Expand All @@ -227,15 +227,19 @@ func TestHandler_TransactionRun(t *testing.T) {
// setup coinbase
foa2 := handler.AllocateAddress()
account2 := handler.AccountByAddress(foa2, true)
require.Equal(t, types.Balance(0), account2.Balance())
require.Equal(t, types.NewBalance(0), account2.Balance())

// no panic means success here
handler.Run(tx, account2.Address())
require.Equal(t, orgBalance.Sub(deduction).Add(addition), foa.Balance())
expected = orgBalance.Copy()
err = expected.Sub(deduction)
require.NoError(t, err)
err = expected.Add(addition)
require.NoError(t, err)
require.Equal(t, expected, foa.Balance())

// fees has been collected to the coinbase
require.NotEqual(t, types.Balance(0), account2.Balance())

require.NotEqual(t, types.NewBalance(0), account2.Balance())
})
})
})
Expand All @@ -258,8 +262,7 @@ func TestHandler_OpsWithoutEmulator(t *testing.T) {
// do some changes
address := testutils.RandomAddress(t)
account := handler.AccountByAddress(address, true)
bal, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
require.NoError(t, err)
bal := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
account.Deposit(types.NewFlowTokenVault(bal))

// check if block height has been incremented
Expand Down Expand Up @@ -304,23 +307,18 @@ func TestHandler_BridgedAccount(t *testing.T) {
foa := handler.AccountByAddress(handler.AllocateAddress(), true)
require.NotNil(t, foa)

zeroBalance, err := types.NewBalanceFromAttoFlow(big.NewInt(0))
require.NoError(t, err)
zeroBalance := types.NewBalanceFromAttoFlow(big.NewInt(0))
require.Equal(t, zeroBalance, foa.Balance())

balance, err := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
require.NoError(t, err)
balance := types.NewBalanceFromAttoFlow(types.OneFlowInAttoFlow)
vault := types.NewFlowTokenVault(balance)

foa.Deposit(vault)
require.NoError(t, err)
require.Equal(t, balance, foa.Balance())

v := foa.Withdraw(balance)
require.NoError(t, err)
require.Equal(t, balance, v.Balance())

require.NoError(t, err)
require.Equal(t, zeroBalance, foa.Balance())

events := backend.Events()
Expand All @@ -337,7 +335,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
// transaction event
event = events[2]
assert.Equal(t, event.Type, types.EventTypeTransactionExecuted)
_, err = jsoncdc.Decode(nil, event.Payload)
_, err := jsoncdc.Decode(nil, event.Payload)
require.NoError(t, err)
// TODO: decode encoded tx and check for the amount and value
// assert.Equal(t, foa.Address(), ret.Address)
Expand All @@ -351,6 +349,15 @@ func TestHandler_BridgedAccount(t *testing.T) {
computationUsed, err := backend.ComputationUsed()
require.NoError(t, err)
require.Equal(t, types.DefaultDirectCallBaseGasUsage*2, computationUsed)

// Withdraw with invalid balance
assertPanic(t, types.IsWithdrawBalanceRoundingError, func() {
// deposit some money
foa.Deposit(vault)
// then withdraw invalid balance
foa.Withdraw(types.NewBalanceFromAttoFlow(big.NewInt(1)))
})

})
})
})
Expand All @@ -372,7 +379,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)

account := handler.AccountByAddress(testutils.RandomAddress(t), false)
account.Withdraw(types.Balance(1))
account.Withdraw(types.NewBalance(1))
})

// test insufficient total supply
Expand All @@ -386,7 +393,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Withdraw(types.Balance(1))
account.Withdraw(types.NewBalance(1))
})

// test non fatal error of emulator
Expand All @@ -400,7 +407,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Withdraw(types.Balance(0))
account.Withdraw(types.NewBalance(0))
})

// test fatal error of emulator
Expand All @@ -414,7 +421,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Withdraw(types.Balance(0))
account.Withdraw(types.NewBalance(0))
})
})
})
Expand Down Expand Up @@ -444,7 +451,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Deposit(types.NewFlowTokenVault(1))
account.Deposit(types.NewFlowTokenVault(types.NewBalance(1)))
})

// test fatal error of emulator
Expand All @@ -458,7 +465,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
handler := handler.NewContractHandler(flowTokenAddress, bs, aa, backend, em)
account := handler.AccountByAddress(testutils.RandomAddress(t), true)

account.Deposit(types.NewFlowTokenVault(1))
account.Deposit(types.NewFlowTokenVault(types.NewBalance(1)))
})
})
})
Expand All @@ -481,7 +488,7 @@ func TestHandler_BridgedAccount(t *testing.T) {
foa.Deposit(vault)

testContract := testutils.GetStorageTestContract(t)
addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.Balance(0))
addr := foa.Deploy(testContract.ByteCode, math.MaxUint64, types.NewBalance(0))
require.NotNil(t, addr)

num := big.NewInt(22)
Expand All @@ -490,13 +497,13 @@ func TestHandler_BridgedAccount(t *testing.T) {
addr,
testContract.MakeCallData(t, "store", num),
math.MaxUint64,
types.Balance(0))
types.NewBalance(0))

ret := foa.Call(
addr,
testContract.MakeCallData(t, "retrieve"),
math.MaxUint64,
types.Balance(0))
types.NewBalance(0))

require.Equal(t, num, new(big.Int).SetBytes(ret))
})
Expand All @@ -522,7 +529,7 @@ func TestHandler_BridgedAccount(t *testing.T) {

arch := handler.MakePrecompileAddress(1)

ret := foa.Call(arch, precompiles.FlowBlockHeightFuncSig[:], math.MaxUint64, types.Balance(0))
ret := foa.Call(arch, precompiles.FlowBlockHeightFuncSig[:], math.MaxUint64, types.NewBalance(0))
require.Equal(t, big.NewInt(int64(blockHeight)), new(big.Int).SetBytes(ret))
})
})
Expand Down
21 changes: 14 additions & 7 deletions fvm/evm/stdlib/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -1125,8 +1125,7 @@ func newInternalEVMTypeCallFunction(
panic(errors.NewUnreachableError())
}

balance := types.Balance(balanceValue)

balance := types.NewBalance(cadence.UFix64(balanceValue))
// Call

const isAuthorized = true
Expand Down Expand Up @@ -1204,7 +1203,7 @@ func newInternalEVMTypeDepositFunction(
panic(errors.NewUnreachableError())
}

amount := types.Balance(amountValue)
amount := types.NewBalance(cadence.UFix64(amountValue))

// Get to address

Expand Down Expand Up @@ -1271,7 +1270,11 @@ func newInternalEVMTypeBalanceFunction(
const isAuthorized = false
account := handler.AccountByAddress(address, isAuthorized)

return interpreter.UFix64Value(account.Balance())
ufix, err := account.Balance().ToUFix64()
if err != nil {
panic(err)
}
return interpreter.UFix64Value(ufix)
},
)
}
Expand Down Expand Up @@ -1322,14 +1325,18 @@ func newInternalEVMTypeWithdrawFunction(
panic(errors.NewUnreachableError())
}

amount := types.Balance(amountValue)
amount := types.NewBalance(cadence.UFix64(amountValue))

// Withdraw

const isAuthorized = true
account := handler.AccountByAddress(fromAddress, isAuthorized)
vault := account.Withdraw(amount)

ufix, err := vault.Balance().ToUFix64()
if err != nil {
panic(err)
}
// TODO: improve: maybe call actual constructor
return interpreter.NewCompositeValue(
inter,
Expand All @@ -1341,7 +1348,7 @@ func newInternalEVMTypeWithdrawFunction(
{
Name: "balance",
Value: interpreter.NewUFix64Value(gauge, func() uint64 {
return uint64(vault.Balance())
return uint64(ufix)
}),
},
},
Expand Down Expand Up @@ -1426,7 +1433,7 @@ func newInternalEVMTypeDeployFunction(
panic(errors.NewUnreachableError())
}

amount := types.Balance(amountValue)
amount := types.NewBalance(cadence.UFix64(amountValue))

// Deploy

Expand Down
Loading
Loading