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 1.0 bridging interface to COA #5664

Closed
Closed
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
2 changes: 2 additions & 0 deletions cmd/util/ledger/migrations/change_contract_code_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ func SystemContractChanges(chainID flow.ChainID, options SystemContractsMigratio
NewSystemContractChange(
systemContracts.EVMContract,
evm.ContractCode(
flow.HexToAddress(env.NonFungibleTokenAddress),
flow.HexToAddress(env.FungibleTokenAddress),
flow.HexToAddress(env.FlowTokenAddress),
),
),
Expand Down
6 changes: 3 additions & 3 deletions fvm/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ func (b *bootstrapExecutor) Execute() error {
b.deployStakingCollection(service, &env)

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

err = expectAccounts(systemcontracts.EVMStorageAccountIndex)
if err != nil {
Expand Down Expand Up @@ -895,7 +895,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 @@ -906,7 +906,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
111 changes: 111 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 All @@ -10,6 +12,7 @@ contract EVM {
access(all) entitlement Call
access(all) entitlement Deploy
access(all) entitlement Owner
access(all) entitlement Bridge

access(all)
event CadenceOwnedAccountCreated(addressBytes: [UInt8; 20])
Expand Down Expand Up @@ -301,6 +304,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: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
) {
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(Owner | Bridge)
fun withdrawNFT(
type: Type,
id: UInt256,
feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
): @{NonFungibleToken.NFT} {
return <- EVM.borrowBridgeAccessor().withdrawNFT(
caller: &self as auth(Call) &CadenceOwnedAccount,
type: type,
id: id,
feeProvider: feeProvider
)
}

/// 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: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
) {
EVM.borrowBridgeAccessor().depositTokens(vault: <-vault, to: self.address(), feeProvider: feeProvider)
}

/// 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(Owner | Bridge)
fun withdrawTokens(
type: Type,
amount: UInt256,
feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
): @{FungibleToken.Vault} {
return <- EVM.borrowBridgeAccessor().withdrawTokens(
caller: &self as auth(Call) &CadenceOwnedAccount,
type: type,
amount: amount,
feeProvider: feeProvider
)
}
}

/// Creates a new cadence owned account
Expand Down Expand Up @@ -500,4 +556,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(Bridge)
fun depositNFT(
nft: @{NonFungibleToken.NFT},
to: EVMAddress,
feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}
)

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

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

/// Endpoint enabling the bridging of fungible tokens from EVM
access(Bridge)
fun withdrawTokens(
caller: auth(Call) &CadenceOwnedAccount,
type: Type,
amount: UInt256,
feeProvider: auth(FungibleToken.Withdraw) &{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(Bridge) view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor}
}

/// Returns a reference to the BridgeAccessor designated for internal bridge requests
access(self)
view fun borrowBridgeAccessor(): auth(Bridge) &{BridgeAccessor} {
return self.account.storage.borrow<auth(Bridge) &{BridgeRouter}>(from: /storage/evmBridgeRouter)
?.borrowBridgeAccessor()
?? panic("Could not borrow reference to the EVM bridge")
}
}
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 @@ -238,7 +238,7 @@ func deployContracts(
},
{
name: stdlib.ContractName,
code: stdlib.ContractCode(contractsAddress),
code: stdlib.ContractCode(contractsAddress, contractsAddress, contractsAddress),
},
}

Expand Down
Loading