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] update stdlib balance to use UInt instead of UFix64 #5303

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions fvm/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/onflow/flow-go/engine/execution/testutil"
"github.com/onflow/flow-go/fvm"
"github.com/onflow/flow-go/fvm/evm/stdlib"
"github.com/onflow/flow-go/fvm/evm/testutils"
. "github.com/onflow/flow-go/fvm/evm/testutils"
"github.com/onflow/flow-go/fvm/storage/snapshot"
"github.com/onflow/flow-go/fvm/systemcontracts"
Expand All @@ -25,7 +24,7 @@ func TestEVMRun(t *testing.T) {
t.Parallel()

t.Run("testing EVM.run (happy case)", func(t *testing.T) {
RunWithTestBackend(t, func(backend *testutils.TestBackend) {
RunWithTestBackend(t, func(backend *TestBackend) {
RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
tc := GetStorageTestContract(t)
RunWithDeployedContract(t, tc, backend, rootAddr, func(testContract *TestContract) {
Expand Down Expand Up @@ -118,7 +117,7 @@ func TestEVMAddressDeposit(t *testing.T) {

t.Parallel()

RunWithTestBackend(t, func(backend *testutils.TestBackend) {
RunWithTestBackend(t, func(backend *TestBackend) {
RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
tc := GetStorageTestContract(t)
RunWithDeployedContract(t, tc, backend, rootAddr, func(testContract *TestContract) {
Expand Down Expand Up @@ -173,7 +172,7 @@ func TestBridgedAccountWithdraw(t *testing.T) {

t.Parallel()

RunWithTestBackend(t, func(backend *testutils.TestBackend) {
RunWithTestBackend(t, func(backend *TestBackend) {
RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
tc := GetStorageTestContract(t)
RunWithDeployedContract(t, tc, backend, rootAddr, func(testContract *TestContract) {
Expand All @@ -199,7 +198,9 @@ func TestBridgedAccountWithdraw(t *testing.T) {
let bridgedAccount <- EVM.createBridgedAccount()
bridgedAccount.deposit(from: <-vault)

let vault2 <- bridgedAccount.withdraw(balance: EVM.Balance(flow: 1.23))
let bal = EVM.Balance(0)
bal.setFLOW(flow: 1.23)
let vault2 <- bridgedAccount.withdraw(balance: bal)
ramtinms marked this conversation as resolved.
Show resolved Hide resolved
let balance = vault2.balance
destroy bridgedAccount
destroy vault2
Expand Down Expand Up @@ -232,7 +233,7 @@ func TestBridgedAccountWithdraw(t *testing.T) {

func TestBridgedAccountDeploy(t *testing.T) {
t.Parallel()
RunWithTestBackend(t, func(backend *testutils.TestBackend) {
RunWithTestBackend(t, func(backend *TestBackend) {
RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
tc := GetStorageTestContract(t)
RunWithDeployedContract(t, tc, backend, rootAddr, func(testContract *TestContract) {
Expand Down Expand Up @@ -261,7 +262,7 @@ func TestBridgedAccountDeploy(t *testing.T) {
let address = bridgedAccount.deploy(
code: [],
gasLimit: 53000,
value: EVM.Balance(flow: 1.23)
value: EVM.Balance(attoflow: 1230000000000000000)
)
destroy bridgedAccount
return address.bytes
Expand Down
53 changes: 38 additions & 15 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,47 @@ contract EVM {
let balance = InternalEVM.balance(
address: self.bytes
)

return Balance(flow: balance)
return Balance(attoflow: balance)
}
}

access(all)
struct Balance {

/// The balance in FLOW
/// The balance in atto-FLOW
/// Atto-FLOW is the smallest denomination of FLOW (1e18 FLOW)
/// that is used to store account balances inside EVM
/// similar to the way WEI is used to store ETH divisible to 18 decimal places.
access(all)
var attoflow: UInt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just checking if you intentionally name it atttoflow not attoFLOW because it's a new name for metric?


/// Constructs a new balance
access(all)
init(attoflow: UInt) {
self.attoflow = attoflow
}

/// Sets the balance by a UFix64 (8 decimal points), the format
/// that is used in Cadence to store FLOW tokens.
access(all)
let flow: UFix64
fun setFLOW(flow: UFix64){
self.attoflow = InternalEVM.castToAttoFLOW(balance: flow)
}

/// Constructs a new balance, given the balance in FLOW
init(flow: UFix64) {
self.flow = flow
/// Casts the balance to a UFix64 (rounding down)
/// Warning! casting a balance to a UFix64 which supports a lower level of precision
/// (8 decimal points in compare to 18) might result in rounding down error.
/// Use the toAttoFlow function if you care need more accuracy.
access(all)
fun inFLOW(): UFix64 {
return InternalEVM.castToFLOW(balance: self.attoflow)
}

// TODO:
// /// Returns the balance in terms of atto-FLOW.
// /// Atto-FLOW is the smallest denomination of FLOW inside EVM
// access(all)
// fun toAttoFlow(): UInt64
/// Returns the balance in Atto-FLOW
access(all)
fun inAttoFLOW(): UInt {
return self.attoflow
}
}

access(all)
Expand Down Expand Up @@ -79,11 +98,15 @@ contract EVM {
}

/// Withdraws the balance from the bridged account's balance
/// Note that amounts smaller than 10nF (10e-8) can't be withdrawn
/// given that Flow Token Vaults use UFix64s to store balances.
/// If the given balance conversion to UFix64 results in
/// rounding error, this function would fail.
access(all)
fun withdraw(balance: Balance): @FlowToken.Vault {
let vault <- InternalEVM.withdraw(
from: self.addressBytes,
amount: balance.flow
amount: balance.attoflow
) as! @FlowToken.Vault
return <-vault
}
Expand All @@ -100,7 +123,7 @@ contract EVM {
from: self.addressBytes,
code: code,
gasLimit: gasLimit,
value: value.flow
value: value.attoflow
)
return EVMAddress(bytes: addressBytes)
}
Expand All @@ -119,7 +142,7 @@ contract EVM {
to: to.bytes,
data: data,
gasLimit: gasLimit,
value: value.flow
value: value.attoflow
)
}
}
Expand Down
116 changes: 96 additions & 20 deletions fvm/evm/stdlib/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -1019,7 +1019,7 @@ var internalEVMTypeCallFunctionType = &sema.FunctionType{
},
{
Label: "value",
TypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
TypeAnnotation: sema.NewTypeAnnotation(sema.UIntType),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.ByteArrayType),
Expand Down Expand Up @@ -1120,12 +1120,12 @@ func newInternalEVMTypeCallFunction(

// Get balance

balanceValue, ok := invocation.Arguments[4].(interpreter.UFix64Value)
balanceValue, ok := invocation.Arguments[4].(interpreter.UIntValue)
if !ok {
panic(errors.NewUnreachableError())
}

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

const isAuthorized = true
Expand Down Expand Up @@ -1242,7 +1242,7 @@ var internalEVMTypeBalanceFunctionType = &sema.FunctionType{
TypeAnnotation: sema.NewTypeAnnotation(evmAddressBytesType),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.UIntType),
}

// newInternalEVMTypeBalanceFunction returns the Flow balance of the account
Expand Down Expand Up @@ -1270,12 +1270,7 @@ func newInternalEVMTypeBalanceFunction(
const isAuthorized = false
account := handler.AccountByAddress(address, isAuthorized)

// TODO: return roundoff flag or handle it
ufix, _, err := types.ConvertBalanceToUFix64(account.Balance())
if err != nil {
panic(err)
}
return interpreter.UFix64Value(ufix)
return interpreter.UIntValue{BigInt: account.Balance()}
},
)
}
Expand All @@ -1290,7 +1285,7 @@ var internalEVMTypeWithdrawFunctionType = &sema.FunctionType{
},
{
Label: "amount",
TypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
TypeAnnotation: sema.NewTypeAnnotation(sema.UIntType),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.AnyResourceType),
Expand Down Expand Up @@ -1321,24 +1316,26 @@ func newInternalEVMTypeWithdrawFunction(

// Get amount

amountValue, ok := invocation.Arguments[1].(interpreter.UFix64Value)
amountValue, ok := invocation.Arguments[1].(interpreter.UIntValue)
if !ok {
panic(errors.NewUnreachableError())
}

amount := types.NewBalanceFromUFix64(cadence.UFix64(amountValue))
amount := types.NewBalance(amountValue.BigInt)

// Withdraw

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

// TODO: return rounded off flag or handle it ?
ufix, _, err := types.ConvertBalanceToUFix64(vault.Balance())
ufix, roundedOff, err := types.ConvertBalanceToUFix64(vault.Balance())
if err != nil {
panic(err)
}
if roundedOff {
panic(types.ErrWithdrawBalanceRounding)
}

// TODO: improve: maybe call actual constructor
return interpreter.NewCompositeValue(
Expand Down Expand Up @@ -1379,7 +1376,7 @@ var internalEVMTypeDeployFunctionType = &sema.FunctionType{
},
{
Label: "value",
TypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
TypeAnnotation: sema.NewTypeAnnotation(sema.UIntType),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(evmAddressBytesType),
Expand Down Expand Up @@ -1431,12 +1428,12 @@ func newInternalEVMTypeDeployFunction(

// Get value

amountValue, ok := invocation.Arguments[3].(interpreter.UFix64Value)
amountValue, ok := invocation.Arguments[3].(interpreter.UIntValue)
if !ok {
panic(errors.NewUnreachableError())
}

amount := types.NewBalanceFromUFix64(cadence.UFix64(amountValue))
amount := types.NewBalance(amountValue.BigInt)

// Deploy

Expand All @@ -1449,6 +1446,71 @@ func newInternalEVMTypeDeployFunction(
)
}

const internalEVMTypeCastToAttoFLOWFunctionName = "castToAttoFLOW"

var internalEVMTypeCastToAttoFLOWFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Label: "balance",
TypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.UIntType),
}

func newInternalEVMTypeCastToAttoFLOWFunction(
gauge common.MemoryGauge,
handler types.ContractHandler,
) *interpreter.HostFunctionValue {
return interpreter.NewHostFunctionValue(
gauge,
internalEVMTypeCallFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
balanceValue, ok := invocation.Arguments[0].(interpreter.UFix64Value)
if !ok {
panic(errors.NewUnreachableError())
}
balance := types.NewBalanceFromUFix64(cadence.UFix64(balanceValue))
return interpreter.UIntValue{BigInt: balance}
},
)
}

const internalEVMTypeCastToFLOWFunctionName = "castToFLOW"

var internalEVMTypeCastToFLOWFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Label: "balance",
TypeAnnotation: sema.NewTypeAnnotation(sema.UIntType),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
}

func newInternalEVMTypeCastToFLOWFunction(
gauge common.MemoryGauge,
handler types.ContractHandler,
) *interpreter.HostFunctionValue {
return interpreter.NewHostFunctionValue(
gauge,
internalEVMTypeCallFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
balanceValue, ok := invocation.Arguments[0].(interpreter.UIntValue)
if !ok {
panic(errors.NewUnreachableError())
}
balance := types.NewBalance(balanceValue.BigInt)
// ignoring the rounding error and let user handle it
v, _, err := types.ConvertBalanceToUFix64(balance)
if err != nil {
panic(err)
}
return interpreter.UFix64Value(v)
},
)
}

func NewInternalEVMContractValue(
gauge common.MemoryGauge,
handler types.ContractHandler,
Expand All @@ -1469,6 +1531,8 @@ func NewInternalEVMContractValue(
internalEVMTypeBalanceFunctionName: newInternalEVMTypeBalanceFunction(gauge, handler),
internalEVMTypeEncodeABIFunctionName: newInternalEVMTypeEncodeABIFunction(gauge, location),
internalEVMTypeDecodeABIFunctionName: newInternalEVMTypeDecodeABIFunction(gauge, location),
internalEVMTypeCastToAttoFLOWFunctionName: newInternalEVMTypeCastToAttoFLOWFunction(gauge, handler),
internalEVMTypeCastToFLOWFunctionName: newInternalEVMTypeCastToFLOWFunction(gauge, handler),
},
nil,
nil,
Expand Down Expand Up @@ -1521,6 +1585,18 @@ var InternalEVMContractType = func() *sema.CompositeType {
internalEVMTypeDeployFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeCastToAttoFLOWFunctionName,
internalEVMTypeCastToAttoFLOWFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeCastToFLOWFunctionName,
internalEVMTypeCastToFLOWFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeBalanceFunctionName,
Expand Down Expand Up @@ -1600,8 +1676,8 @@ func NewBalanceCadenceType(address common.Address) *cadence.StructType {
"EVM.Balance",
[]cadence.Field{
{
Identifier: "flow",
Type: cadence.UFix64Type{},
Identifier: "attoflow",
Type: cadence.UIntType{},
},
},
nil,
Expand Down
Loading
Loading