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

Add special case FungibleToken handler #46

Merged
merged 19 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c60de5b
add FlowEVMBridgeHandlerInterfaces
sisyphusSmiling Apr 19, 2024
7298004
update Handler interfaces & implement in bridge config
sisyphusSmiling Apr 22, 2024
636c7f4
implement CadenceNativeTokenHandler
sisyphusSmiling Apr 22, 2024
f0e9d0d
fix Handler & config pre-conditions and integrate Handlers into bridge
sisyphusSmiling Apr 23, 2024
97aa2cd
add Handler test coverage
sisyphusSmiling Apr 23, 2024
f868c3d
fix handler tests
sisyphusSmiling Apr 23, 2024
d2c8366
update coverge normalization script
sisyphusSmiling Apr 23, 2024
2338351
refactor FlowEVMBridgeUtils.deposit to .depositFee to ease internal f…
sisyphusSmiling Apr 23, 2024
9fc8926
add Handler logic to .bridgeTokensFromEVM
sisyphusSmiling Apr 23, 2024
602b6ba
add doc comments to handler contracts
sisyphusSmiling Apr 23, 2024
adc4e54
update FlowEVMBridgeConfig handler interfaces & implementations + com…
sisyphusSmiling Apr 23, 2024
5cb0a4d
update FlowEVMBridgeConfig comments
sisyphusSmiling Apr 23, 2024
4fdd973
add totalSupply util method & script
sisyphusSmiling Apr 23, 2024
99ec218
add supporting txns, scripts & test coverage for TokenHandler integra…
sisyphusSmiling Apr 23, 2024
3ed9b69
generalize create_cadence_native_token_handler
sisyphusSmiling Apr 23, 2024
15dc839
Merge branch 'main' into add-special-case-handler
sisyphusSmiling Apr 24, 2024
3f5d19e
refactor TokenHandler Minter setting pattern
sisyphusSmiling Apr 24, 2024
669a80f
reorganize admin transactions
sisyphusSmiling Apr 24, 2024
306640a
harden balance state assertions for token bridging
sisyphusSmiling Apr 24, 2024
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
92 changes: 62 additions & 30 deletions cadence/contracts/bridge/FlowEVMBridge.cdc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import "IFlowEVMTokenBridge"
import "CrossVMNFT"
import "CrossVMToken"
import "FlowEVMBridgeConfig"
import "FlowEVMBridgeHandlerInterfaces"
import "FlowEVMBridgeUtils"
import "FlowEVMBridgeNFTEscrow"
import "FlowEVMBridgeTokenEscrow"
Expand Down Expand Up @@ -107,8 +108,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
message: "This type is not supported as defined by the project's development team"
)
// Withdraw from feeProvider and deposit to self
let feeVault <-feeProvider.withdraw(amount: FlowEVMBridgeConfig.onboardFee) as! @FlowToken.Vault
FlowEVMBridgeUtils.deposit(<-feeVault)
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: FlowEVMBridgeConfig.onboardFee)
// Deploy an EVM defining contract via the FlowBridgeFactory.sol contract
let onboardingValues = self.deployEVMContract(forAssetType: type)
// Initialize bridge escrow for the asset
Expand Down Expand Up @@ -170,9 +170,8 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
self.evmAddressRequiresOnboarding(address) == true,
message: "Onboarding is not needed for this contract"
)
// Withdraw from feeProvider and deposit to self
let feeVault <-feeProvider.withdraw(amount: FlowEVMBridgeConfig.onboardFee) as! @FlowToken.Vault
FlowEVMBridgeUtils.deposit(<-feeVault)
// Withdraw fee from feeProvider and deposit
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: FlowEVMBridgeConfig.onboardFee)
// Deploy a defining Cadence contract to the bridge account
self.deployDefiningContract(evmContractAddress: address)
}
Expand Down Expand Up @@ -215,13 +214,8 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
let storageUsed = FlowEVMBridgeNFTEscrow.lockNFT(<-token)
// Calculate the bridge fee on current rates
let feeAmount = FlowEVMBridgeUtils.calculateBridgeFee(bytes: storageUsed)
assert(
feeProvider.isAvailableToWithdraw(amount: feeAmount),
message: "Fee provider does not have balance to cover the bridge fee of ".concat(feeAmount.toString())
)
// Withdraw from feeProvider and deposit to self
let feeVault <-feeProvider.withdraw(amount: feeAmount) as! @FlowToken.Vault
FlowEVMBridgeUtils.deposit(<-feeVault)
// Withdraw fee from feeProvider and deposit
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: feeAmount)

// Does the bridge control the EVM contract associated with this type?
let associatedAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: tokenType)
Expand Down Expand Up @@ -316,8 +310,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
}
// Withdraw from feeProvider and deposit to self
let feeAmount = FlowEVMBridgeUtils.calculateBridgeFee(bytes: 0)
let feeVault <-feeProvider.withdraw(amount: feeAmount) as! @FlowToken.Vault
FlowEVMBridgeUtils.deposit(<-feeVault)
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: feeAmount)

// Get the EVMAddress of the ERC721 contract associated with the type
let associatedAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: type)
Expand Down Expand Up @@ -396,25 +389,34 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {

let vaultBalance = vault.balance
var feeAmount = 0.0
if FlowEVMBridgeConfig.typeHasTokenHandler(vaultType) {
// Some tokens pre-dating bridge require special case handling - borrow handler and passthrough to fulfill
let handler = FlowEVMBridgeConfig.borrowTokenHandler(vaultType)
?? panic("Could not retrieve handler for the given type")
assert(handler.isEnabled(), message: "Cannot bridge tokens of this type at this time")

handler.fulfillTokensToEVM(tokens: <-vault, to: to)

// Here we assume burning Vault in Cadence which doesn't require storage consumption
feeAmount = FlowEVMBridgeUtils.calculateBridgeFee(bytes: 0)
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: feeAmount)
return
}

// Lock the tokens if the bridge does not define them
if FlowEVMBridgeUtils.isCadenceNative(type: vault.getType()) {
// In most all other cases, if Cadence-native then tokens must be escrowed
if FlowEVMBridgeUtils.isCadenceNative(type: vaultType) {
// Lock the FT balance & calculate the extra used by the FT if any
let storageUsed = FlowEVMBridgeTokenEscrow.lockTokens(<-vault)
// Calculate the bridge fee on current rates
feeAmount = FlowEVMBridgeUtils.calculateBridgeFee(bytes: storageUsed)
} else {
// Since not Cadence-native, bridge defines the token - burn the vault and calculate the fee
Burner.burn(<-vault)
feeAmount = FlowEVMBridgeUtils.calculateBridgeFee(bytes: 0)
}

// Withdraw from feeProvider and deposit to self
assert(
feeProvider.isAvailableToWithdraw(amount: feeAmount),
message: "Fee provider does not have balance to cover the bridge fee of ".concat(feeAmount.toString())
)
let feeVault <-feeProvider.withdraw(amount: feeAmount) as! @FlowToken.Vault
FlowEVMBridgeUtils.deposit(<-feeVault)
// Withdraw fee amount from feeProvider and deposit
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: feeAmount)

// Does the bridge control the EVM contract associated with this type?
let associatedAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: vaultType)
Expand All @@ -423,6 +425,8 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
let decimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: associatedAddress)
let bridgeAmount = FlowEVMBridgeUtils.ufix64ToUInt256(value: vaultBalance, decimals: decimals)

let toPreBalance = FlowEVMBridgeUtils.balanceOf(owner: to, evmContractAddress: associatedAddress)

let isFactoryDeployed = FlowEVMBridgeUtils.isEVMContractBridgeOwned(evmContractAddress: associatedAddress)
// Controlled by the bridge - mint or transfer based on the bridge's EVM contract authority
if isFactoryDeployed {
Expand All @@ -446,6 +450,13 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
)
assert(callResult.status == EVM.Status.successful, message: "Tranfer to bridge recipient failed")
}

// Ensure bridge to recipient was succcessful
let toPostBalance = FlowEVMBridgeUtils.balanceOf(owner: to, evmContractAddress: associatedAddress)
assert(
toPostBalance == toPreBalance + bridgeAmount,
message: "Transfer to bridge recipient failed"
)
}

/// Public entrypoint to bridge FTs from EVM to Cadence
Expand Down Expand Up @@ -478,8 +489,21 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
}
// Withdraw from feeProvider and deposit to self
let feeAmount = FlowEVMBridgeUtils.calculateBridgeFee(bytes: 0)
let feeVault <-feeProvider.withdraw(amount: feeAmount) as! @FlowToken.Vault
FlowEVMBridgeUtils.deposit(<-feeVault)
FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: feeAmount)

if FlowEVMBridgeConfig.typeHasTokenHandler(type) {
// Some tokens pre-dating bridge require special case handling - borrow handler and passthrough to fulfill
let handler = FlowEVMBridgeConfig.borrowTokenHandler(type)
?? panic("Could not retrieve handler for the given type")
assert(handler.isEnabled(), message: "Cannot bridge tokens of this type at this time")

return <-handler.fulfillTokensFromEVM(
owner: owner,
type: type,
amount: amount,
protectedTransferCall: protectedTransferCall
)
}

// Get the EVMAddress of the ERC20 contract associated with the type
let associatedAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: type)
Expand All @@ -493,21 +517,28 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
)
assert(hasSufficientBalance, message: "Caller does not have sufficient balance to bridge requested tokens")

// Get the bridge COA's balance of the token before executing the protected transfer call
// Get the owner and escrow balance of the token before executing the protected transfer call
let bridgeCOAAddress = self.getBridgeCOAEVMAddress()
let ownerBalanceBefore = FlowEVMBridgeUtils.balanceOf(owner: owner, evmContractAddress: associatedAddress)
let bridgeBalanceBefore = FlowEVMBridgeUtils.balanceOf(
owner: self.getBridgeCOAEVMAddress(),
owner: bridgeCOAAddress,
evmContractAddress: associatedAddress
)

// Execute the transfer from the calling owner to the bridge's COA, escrowing the tokens in EVM
let callResult = protectedTransferCall()
assert(callResult.status == EVM.Status.successful, message: "Transfer to bridge COA failed")

// Get the bridge COA's balance of the token before executing the protected transfer call
// Confirm the transfer of the expected was successful in both sending owner and recipient escrow
let ownerBalanceAfter = FlowEVMBridgeUtils.balanceOf(owner: owner, evmContractAddress: associatedAddress)
let bridgeBalanceAfter = FlowEVMBridgeUtils.balanceOf(
owner: self.getBridgeCOAEVMAddress(),
owner: bridgeCOAAddress,
evmContractAddress: associatedAddress
)
assert(
ownerBalanceAfter == ownerBalanceBefore - amount,
message: "Transfer to bridge COA failed - cannot bridge FT without bridge escrow"
)
assert(
bridgeBalanceAfter == bridgeBalanceBefore + amount,
message: "Transfer to bridge COA failed - cannot bridge FT without bridge escrow"
Expand Down Expand Up @@ -582,7 +613,8 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge {
if !FlowEVMBridgeUtils.isValidFlowAsset(type: type) {
return nil
}
return FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) == nil
return FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) == nil &&
!FlowEVMBridgeConfig.typeHasTokenHandler(type)
}

/// Returns whether an EVM-native asset needs to be onboarded to the bridge
Expand Down
Loading
Loading