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

[EVM] Add bridging interface to EVM contract #5677

Merged
merged 13 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
6 changes: 5 additions & 1 deletion cmd/util/ledger/migrations/change_contract_code_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,11 @@ func SystemContractChanges(chainID flow.ChainID) []SystemContractChange {
// EVM related contracts
NewSystemContractChange(
systemContracts.EVMContract,
evm.ContractCode(systemContracts.FlowToken.Address),
evm.ContractCode(
systemContracts.NonFungibleToken.Address,
systemContracts.FungibleToken.Address,
systemContracts.FlowToken.Address,
),
),
}
}
Expand Down
6 changes: 3 additions & 3 deletions fvm/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ func (b *bootstrapExecutor) Execute() error {
b.setStakingAllowlist(service, b.identities.NodeIDs())

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

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

func (b *bootstrapExecutor) setupEVM(serviceAddress, fungibleTokenAddress, flowTokenAddress flow.Address) {
func (b *bootstrapExecutor) setupEVM(serviceAddress, nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress flow.Address) {
if b.setupEVMEnabled {
// account for storage
// we dont need to deploy anything to this account, but it needs to exist
Expand All @@ -817,7 +817,7 @@ func (b *bootstrapExecutor) setupEVM(serviceAddress, fungibleTokenAddress, flowT
// deploy the EVM contract to the service account
tx := blueprints.DeployContractTransaction(
serviceAddress,
stdlib.ContractCode(flowTokenAddress),
stdlib.ContractCode(nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress),
stdlib.ContractName,
)
// WithEVMEnabled should only be used after we create an account for storage
Expand Down
110 changes: 110 additions & 0 deletions fvm/evm/stdlib/contract.cdc
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Crypto
import "NonFungibleToken"
import "FungibleToken"
import "FlowToken"

access(all)
Expand Down Expand Up @@ -288,6 +290,59 @@ contract EVM {
value: value.attoflow
) as! Result
}

/// Bridges the given NFT to the EVM environment, requiring a Provider from which to withdraw a fee to fulfill
/// the bridge request
access(all)
fun depositNFT(
nft: @NonFungibleToken.NFT,
feeProvider: &{FungibleToken.Provider}
ramtinms marked this conversation as resolved.
Show resolved Hide resolved
) {
EVM.borrowBridgeAccessor().depositNFT(nft: <-nft, to: self.address(), feeProvider: feeProvider)
}

/// Bridges the given NFT from the EVM environment, requiring a Provider from which to withdraw a fee to fulfill
/// the bridge request. Note: the caller should own the requested NFT in EVM
access(all)
fun withdrawNFT(
type: Type,
id: UInt256,
feeProvider: &{FungibleToken.Provider}
): @NonFungibleToken.NFT {
return <- EVM.borrowBridgeAccessor().withdrawNFT(
caller: &self as &CadenceOwnedAccount,
type: type,
id: id,
feeProvider: feeProvider
)
}
ramtinms marked this conversation as resolved.
Show resolved Hide resolved

/// Bridges the given Vault to the EVM environment, requiring a Provider from which to withdraw a fee to fulfill
/// the bridge request
access(all)
fun depositTokens(
vault: @FungibleToken.Vault,
feeProvider: &{FungibleToken.Provider}
) {
EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider)
ramtinms marked this conversation as resolved.
Show resolved Hide resolved
}

/// Bridges the given fungible tokens from the EVM environment, requiring a Provider from which to withdraw a
/// fee to fulfill the bridge request. Note: the caller should own the requested tokens & sufficient balance of
/// requested tokens in EVM
access(all)
fun withdrawTokens(
type: Type,
amount: UInt256,
feeProvider: &{FungibleToken.Provider}
): @FungibleToken.Vault {
return <- EVM.borrowBridgeAccessor().withdrawTokens(
caller: &self as &CadenceOwnedAccount,
type: type,
amount: amount,
feeProvider: feeProvider
)
}
}

/// Creates a new cadence owned account
Expand Down Expand Up @@ -488,4 +543,59 @@ contract EVM {
fun getLatestBlock(): EVMBlock {
return InternalEVM.getLatestBlock() as! EVMBlock
}

/// Interface for a resource which acts as an entrypoint to the VM bridge
access(all)
resource interface BridgeAccessor {

/// Endpoint enabling the bridging of an NFT to EVM
access(all)
fun depositNFT(
nft: @NonFungibleToken.NFT,
to: EVMAddress,
feeProvider: &{FungibleToken.Provider}
)

/// Endpoint enabling the bridging of an NFT from EVM
access(all)
fun withdrawNFT(
caller: &CadenceOwnedAccount,
type: Type,
id: UInt256,
feeProvider: &{FungibleToken.Provider}
): @NonFungibleToken.NFT

/// Endpoint enabling the bridging of a fungible token vault to EVM
access(all)
fun depositTokens(
vault: @FungibleToken.Vault,
to: EVMAddress,
feeProvider: &{FungibleToken.Provider}
)

/// Endpoint enabling the bridging of fungible tokens from EVM
access(all)
fun withdrawTokens(
caller: &CadenceOwnedAccount,
type: Type,
amount: UInt256,
feeProvider: &{FungibleToken.Provider}
): @FungibleToken.Vault
}

/// Interface which captures a Capability to the bridge Accessor, saving it within the BridgeRouter resource
access(all)
resource interface BridgeRouter {

/// Returns a reference to the BridgeAccessor designated for internal bridge requests
access(all) view fun borrowBridgeAccessor(): &{BridgeAccessor}
}

/// Returns a reference to the BridgeAccessor designated for internal bridge requests
access(self)
view fun borrowBridgeAccessor(): &{BridgeAccessor} {
return self.account.borrow<&{BridgeRouter}>(from: /storage/evmBridgeRouter)
?.borrowBridgeAccessor()
?? panic("Could not borrow reference to the EVM bridge")
ramtinms marked this conversation as resolved.
Show resolved Hide resolved
}
}
19 changes: 15 additions & 4 deletions fvm/evm/stdlib/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,24 @@ import (
//go:embed contract.cdc
var contractCode string

var flowTokenImportPattern = regexp.MustCompile(`(?m)^import "FlowToken"\n`)
var nftImportPattern = regexp.MustCompile(`(?m)^import "NonFungibleToken"`)
var fungibleTokenImportPattern = regexp.MustCompile(`(?m)^import "FungibleToken"`)
var flowTokenImportPattern = regexp.MustCompile(`(?m)^import "FlowToken"`)

func ContractCode(flowTokenAddress flow.Address) []byte {
return []byte(flowTokenImportPattern.ReplaceAllString(
func ContractCode(nonFungibleTokenAddress, fungibleTokenAddress, flowTokenAddress flow.Address) []byte {
contractCode = nftImportPattern.ReplaceAllString(
contractCode,
fmt.Sprintf("import NonFungibleToken from %s", nonFungibleTokenAddress.HexWithPrefix()),
)
contractCode = fungibleTokenImportPattern.ReplaceAllString(
contractCode,
fmt.Sprintf("import FungibleToken from %s", fungibleTokenAddress.HexWithPrefix()),
)
contractCode = flowTokenImportPattern.ReplaceAllString(
contractCode,
fmt.Sprintf("import FlowToken from %s", flowTokenAddress.HexWithPrefix()),
))
)
return []byte(contractCode)
}

const ContractName = "EVM"
Expand Down
2 changes: 1 addition & 1 deletion fvm/evm/stdlib/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func deployContracts(
},
{
name: stdlib.ContractName,
code: stdlib.ContractCode(contractsAddress),
code: stdlib.ContractCode(contractsAddress, contractsAddress, contractsAddress),
},
}

Expand Down
2 changes: 2 additions & 0 deletions fvm/fvm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3065,6 +3065,7 @@ func TestEVM(t *testing.T) {
withBootstrapProcedureOptions(fvm.WithSetupEVMEnabled(true)).
ramtinms marked this conversation as resolved.
Show resolved Hide resolved
withContextOptions(
fvm.WithEVMEnabled(true),
fvm.WithChain(flow.Emulator.Chain()),
fvm.WithCadenceLogging(true),
).
run(func(
Expand Down Expand Up @@ -3226,6 +3227,7 @@ func TestEVM(t *testing.T) {
// so we have to use emulator here so that the EVM storage contract is deployed
// to the 5th address
fvm.WithChain(flow.Emulator.Chain()),
fvm.WithEVMEnabled(true),
).
run(func(
t *testing.T,
Expand Down
Loading