Skip to content

Commit

Permalink
Merge pull request #4889 from onflow/bastian/evm-deposit
Browse files Browse the repository at this point in the history
[FVM] beyond EVM part 6.1 - Implement EVMAddress.deposit
  • Loading branch information
turbolent authored Nov 14, 2023
2 parents fa7a065 + beec792 commit 75fa942
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 134 deletions.
8 changes: 4 additions & 4 deletions fvm/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ func (b *bootstrapExecutor) Execute() error {
b.setStakingAllowlist(service, b.identities.NodeIDs())

// sets up the EVM environment
b.setupEVM(service)
b.setupEVM(service, flowToken)

return nil
}
Expand Down Expand Up @@ -788,12 +788,12 @@ func (b *bootstrapExecutor) setStakingAllowlist(
panicOnMetaInvokeErrf("failed to set staking allow-list: %s", txError, err)
}

func (b *bootstrapExecutor) setupEVM(service flow.Address) {
func (b *bootstrapExecutor) setupEVM(serviceAddress, flowTokenAddress flow.Address) {
if b.setupEVMEnabled {
b.createAccount(nil) // account for storage
tx := blueprints.DeployContractTransaction(
service,
stdlib.ContractCode,
serviceAddress,
stdlib.ContractCode(flowTokenAddress),
stdlib.ContractName,
)
// WithEVMEnabled should only be used after we create an account for storage
Expand Down
10 changes: 1 addition & 9 deletions fvm/evm/evm.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package evm

import (
"fmt"

"github.com/onflow/cadence/runtime"

"github.com/onflow/flow-go/fvm/environment"
Expand All @@ -15,13 +13,7 @@ import (
)

func RootAccountAddress(chainID flow.ChainID) (flow.Address, error) {
// TODO handle other chains
switch chainID {
case flow.Emulator:
return chainID.Chain().AddressAtIndex(environment.EVMAccountIndex)
default:
return flow.Address{}, fmt.Errorf("unsupported chain %s", chainID)
}
return chainID.Chain().AddressAtIndex(environment.EVMAccountIndex)
}

func SetupEnvironment(
Expand Down
48 changes: 48 additions & 0 deletions fvm/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,51 @@ func RunWithNewTestVM(t *testing.T, chain flow.Chain, f func(fvm.Context, fvm.VM

f(fvm.NewContextFromParent(ctx, fvm.WithEVMEnabled(true)), vm, snapshotTree)
}

// TODO: test with actual amount
func TestEVMAddressDeposit(t *testing.T) {

t.Parallel()

RunWithTestBackend(t, func(backend types.Backend) {
RunWithTestFlowEVMRootAddress(t, backend, func(rootAddr flow.Address) {
RunWithDeployedContract(t, backend, rootAddr, func(testContract *TestContract) {
RunWithEOATestAccount(t, backend, rootAddr, func(testAccount *EOATestAccount) {
chain := flow.Emulator.Chain()
RunWithNewTestVM(t, chain, func(ctx fvm.Context, vm fvm.VM, snapshot snapshot.SnapshotTree) {

code := []byte(fmt.Sprintf(
`
import EVM from %s
import FlowToken from %s
access(all)
fun main() {
let address = EVM.EVMAddress(
bytes: [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
)
let vault <- FlowToken.createEmptyVault() as! @FlowToken.Vault
address.deposit(from: <-vault)
}
`,
chain.ServiceAddress().HexWithPrefix(),
fvm.FlowTokenAddress(chain).HexWithPrefix(),
))

script := fvm.Script(code)

executionSnapshot, output, err := vm.Run(
ctx,
script,
snapshot)
require.NoError(t, err)
require.NoError(t, output.Err)

// TODO:
_ = executionSnapshot
})
})
})
})
})
}
38 changes: 27 additions & 11 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import "FlowToken"

access(all)
contract EVM {

Expand All @@ -9,33 +11,45 @@ contract EVM {
access(all)
let bytes: [UInt8; 20]

/// Constructs a new EVM address from the given byte representation.
/// Constructs a new EVM address from the given byte representation
init(bytes: [UInt8; 20]) {
self.bytes = bytes
}

/// Deposits the given vault into the EVM account with the given address
access(all)
fun deposit(from: @FlowToken.Vault) {
let amount = from.balance
destroy from
InternalEVM.deposit(
to: self.bytes,
amount: amount
)
}
}

access(all)
struct Balance {

/// The balance in FLOW.
/// The balance in FLOW
access(all)
let flow: UFix64

/// Constructs a new balance, given the balance in FLOW.
/// Constructs a new balance, given the balance in FLOW
init(flow: UFix64) {
self.flow = flow
}

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

access(all)
resource BridgedAccount {

access(self)
let addressBytes: [UInt8; 20]

Expand All @@ -50,17 +64,19 @@ contract EVM {
return EVMAddress(bytes: self.addressBytes)
}

/// Deposits the given vault into the bridged account's balance
access(all)
fun deposit(from: @FlowToken.Vault) {
self.address().deposit(from: <-from)
}

// TODO:
// /// Deposits the given vault into the bridged account's balance
// access(all)
// fun deposit(from: @FlowToken.Vault)
//
// /// Withdraws the balance from the bridged account's balance
// access(all)
// fun withdraw(balance: Balance): @FlowToken.Vault
//
// /// Deploys a contract to the EVM environment.
// /// Returns the address of the newly deployed contract.
// /// Returns the address of the newly deployed contract
// access(all)
// fun deploy(
// code: [UInt8],
Expand All @@ -69,7 +85,7 @@ contract EVM {
// ): EVMAddress
/// Calls a function with the given data.
/// The execution is limited by the given amount of gas.
/// The execution is limited by the given amount of gas
access(all)
fun call(
to: EVMAddress,
Expand Down Expand Up @@ -99,7 +115,7 @@ contract EVM {
/// and deposits the gas fees into the provided coinbase address.
///
/// Returns true if the transaction was successful,
/// and returns false otherwise.
/// and returns false otherwise
access(all)
fun run(tx: [UInt8], coinbase: EVMAddress) {
InternalEVM.run(tx: tx, coinbase: coinbase.bytes)
Expand Down
79 changes: 78 additions & 1 deletion fvm/evm/stdlib/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package stdlib

import (
_ "embed"
"fmt"
"regexp"

"github.com/onflow/cadence"
"github.com/onflow/cadence/runtime"
Expand All @@ -16,7 +18,16 @@ import (
)

//go:embed contract.cdc
var ContractCode []byte
var contractCode string

var flowTokenImportPattern = regexp.MustCompile(`^import "FlowToken"\n`)

func ContractCode(flowTokenAddress flow.Address) []byte {
return []byte(flowTokenImportPattern.ReplaceAllString(
contractCode,
fmt.Sprintf("import FlowToken from %s", flowTokenAddress.HexWithPrefix()),
))
}

const ContractName = "EVM"

Expand Down Expand Up @@ -273,6 +284,65 @@ func newInternalEVMTypeCreateBridgedAccountFunction(
)
}

const internalEVMTypeDepositFunctionName = "deposit"

var internalEVMTypeDepositFunctionType = &sema.FunctionType{
Parameters: []sema.Parameter{
{
Label: "to",
TypeAnnotation: sema.NewTypeAnnotation(evmAddressBytesType),
},
{
Label: "amount",
TypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type),
},
},
ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.VoidType),
}

func newInternalEVMTypeDepositFunction(
gauge common.MemoryGauge,
handler types.ContractHandler,
) *interpreter.HostFunctionValue {
return interpreter.NewHostFunctionValue(
gauge,
internalEVMTypeCallFunctionType,
func(invocation interpreter.Invocation) interpreter.Value {
inter := invocation.Interpreter
locationRange := invocation.LocationRange

// Get to address

toAddressValue, ok := invocation.Arguments[0].(*interpreter.ArrayValue)
if !ok {
panic(errors.NewUnreachableError())
}

toAddress, err := AddressBytesArrayValueToEVMAddress(inter, locationRange, toAddressValue)
if err != nil {
panic(err)
}

// Get amount

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

amount := types.Balance(amountValue)

// Call

const isAuthorized = false
account := handler.AccountByAddress(toAddress, isAuthorized)
account.Deposit(types.NewFlowTokenVault(amount))

return interpreter.Void
},
)
}

func NewInternalEVMContractValue(
gauge common.MemoryGauge,
handler types.ContractHandler,
Expand All @@ -286,6 +356,7 @@ func NewInternalEVMContractValue(
internalEVMTypeRunFunctionName: newInternalEVMTypeRunFunction(gauge, handler),
internalEVMTypeCreateBridgedAccountFunctionName: newInternalEVMTypeCreateBridgedAccountFunction(gauge, handler),
internalEVMTypeCallFunctionName: newInternalEVMTypeCallFunction(gauge, handler),
internalEVMTypeDepositFunctionName: newInternalEVMTypeDepositFunction(gauge, handler),
},
nil,
nil,
Expand Down Expand Up @@ -320,6 +391,12 @@ var InternalEVMContractType = func() *sema.CompositeType {
internalEVMTypeCallFunctionType,
"",
),
sema.NewUnmeteredPublicFunctionMember(
ty,
internalEVMTypeDepositFunctionName,
internalEVMTypeDepositFunctionType,
"",
),
})
return ty
}()
Expand Down
Loading

0 comments on commit 75fa942

Please sign in to comment.