onflow / flow-nft

The non-fungible token standard on the Flow blockchain
https://onflow.org
The Unlicense
465 stars 169 forks source link

Add an EVMBridgedData View in MetadataViews #221

Open btspoony opened 4 months ago

btspoony commented 4 months ago

Instructions

Issue To Be Solved

Need an EVMBridgedData view to contain the information of this token on the EVM side.

Suggest A Solution

Example:

access(all) struct EVMBridgedData {

        /// The EVM contract address of the corresponding ERC20 contract address
        ///
        access(all) let contractAddress: EVM.EVMAddress

        /// The Cadence type associated with the ERC20 contract address
        ///
        access(all) let associatedType:Type

        init(address: EVM.EVMAddress, type: Type) {
            self.contractAddress = address
            self.associatedType = type
        }
}

It is best to implement this View in Templates: https://github.com/onflow/flow-evm-bridge/tree/main/cadence/contracts/templates

Context

Discord conversation: https://discord.com/channels/613813861610684416/1240279845473226853

sisyphusSmiling commented 4 months ago

Thanks for this suggestion! Is the intention for this view to be used solely by bridged NFT/tokens?

I ask because any existing Cadence project would not be able to implement this view until their bridged EVM address is known, in which case they would need to update their contracts. But if the intention is to determine if an asset is onboarded to the bridge and therefore "bridgeable", then we can't rely on projects to do so in a timely manner or really at all, defeating the reliability of this view as a proxy for that info.

Another consideration - there is the potential that projects at some point create their own self-rolled bridges. In that case, which EVM address does the project list - the bridge-deployed EVM address or the project-deployed EVM address served by the self-rolled bridge?

What I would hate to have happen is for us to introduce a standard view that implies it can be used as a source of truth for state that's actually validated elsewhere. Or alternatively that the view's data can be taken optimistically and validated against what we currently call ground truth only to end up in future where the source of that truth changes with no way to redirect that validation path.

For instance, let's say I've resolved this view on some NFT - how do I know that it's true? I could ask the bridge, is this type and EVM contract associated? But what if the developers decide to create their own bridged NFT and provide an their bridge's EVM address? Then I ask the public infrastructure bridge the same question and get back false...the generalized validation pattern has proven to be unreliable.

That's not to say I'm against this view, just trying to think through any implications. If we introduce this view, I think it's also important that we include verification pattern that does not rely on the central bridge.

Instead of asking the bridge, I could just ask the listed EVM contract for its associated type (or type identifier). This means I wouldn't have to rely on any bridge for validation because the two sides just point to each other.

For instance, take the view above with a solidity interface:

interface ICrossVM {
    function getCadenceIdentifier() external view returns (string);
    function getCadenceContractAddress() external view returns (string);
}

This could also work against any imposter contracts in a way that prevents reliance on any bridge in particular. The contract above mirrors the existing ICrossVM.cdc contract interface used in the bridge templates, something I meant to implement in the bridged EVM contracts but didn't end up doing.

cc: @lmcmz

btspoony commented 4 months ago

Thanks for this suggestion! Is the intention for this view to be used solely by bridged NFT/tokens?

I ask because any existing Cadence project would not be able to implement this view until their bridged EVM address is known, in which case they would need to update their contracts. But if the intention is to determine if an asset is onboarded to the bridge and therefore "bridgeable", then we can't rely on projects to do so in a timely manner or really at all, defeating the reliability of this view as a proxy for that info.

Another consideration - there is the potential that projects at some point create their own self-rolled bridges. In that case, which EVM address does the project list - the bridge-deployed EVM address or the project-deployed EVM address served by the self-rolled bridge?

What I would hate to have happen is for us to introduce a standard view that implies it can be used as a source of truth for state that's actually validated elsewhere. Or alternatively that the view's data can be taken optimistically and validated against what we currently call ground truth only to end up in future where the source of that truth changes with no way to redirect that validation path.

For instance, let's say I've resolved this view on some NFT - how do I know that it's true? I could ask the bridge, is this type and EVM contract associated? But what if the developers decide to create their own bridged NFT and provide an their bridge's EVM address? Then I ask the public infrastructure bridge the same question and get back false...the generalized validation pattern has proven to be unreliable.

That's not to say I'm against this view, just trying to think through any implications. If we introduce this view, I think it's also important that we include verification pattern that does not rely on the central bridge.

Instead of asking the bridge, I could just ask the listed EVM contract for its associated type (or type identifier). This means I wouldn't have to rely on any bridge for validation because the two sides just point to each other.

For instance, take the view above with a solidity interface:

interface ICrossVM {
    function getCadenceIdentifier() external view returns (string);
    function getCadenceContractAddress() external view returns (string);
}

This could also work against any imposter contracts in a way that prevents reliance on any bridge in particular. The contract above mirrors the existing ICrossVM.cdc contract interface used in the bridge templates, something I meant to implement in the bridged EVM contracts but didn't end up doing.

cc: @lmcmz

I think it is for any token. Cadence native tokens can be updated in the contract after obtaining the EVM address. This is an optional view and not necessarily required to be implemented at the beginning.

Regarding the second question, I think it involves how to verify the token after giving these info. Maybe we can add the address of the bridge and the type information of the bridge in this view?

like this:

access(all) struct EVMBridgedData {
       access(all) let contractAddress: EVM.EVMAddress
       access(all) let associatedType:Type
       access(all) let bridgeCadence: Address
       access(all) let bridgeEVM: EVM.EVMAddress

        init(address: EVM.EVMAddress, type: Type, bridge: Address, bridgeEVM: EVM.EVMAddress) {
            self.contractAddress = address
            self.associatedType = type
            self.bridgeCadence = bridge
            self.bridgeEVM = bridgeEVM
        }
}

Then implement some standard interfaces for Bridges for additional info.

sisyphusSmiling commented 4 months ago

This is an optional view and not necessarily required to be implemented at the beginning.

If it's optional, how can it be a reliable filter for bridgeable assets? In the original thread, it was mentioned this view would be used to determine if something is onboarded for bridging so it's not clear to me how relying on an optional view is more useful than just asking the source of truth relevant to the intent of the script/transaction.

Maybe we can add the address of the bridge and the type information of the bridge in this view

I feel this doesn't solve the problem of verifying associations - an imposter could just point to a bridge contract they own which "validates" their lie of associated types. For example, let's say they're some ERC20 token X on EVM. An imposter deploys a Cadence contract claiming to be the bridged token X - call it bridgedX - as well as an imposter bridge contract according to whatever the bridge contract standard is. Then a wallet provider or DEX, seeing this view resolved from a token, looks at the evmAddress, sees the actual X token and then asks the bridge address to validate the bridgedX type & X EVM contract associations. The imposter bridge of course just validates the lie and now the wallet provider and therefore user has been fooled.

The lowest effort ways around this IMO are:

  1. Ask both sides their cross-VM associations. If they both point to each other, then the association is trustworthy. However, because we've said this requires contract updates and input from projects, we've determined this is unreliable.
  2. Platforms and wallets will need to choose which bridge providers they trust and integrate with and therefore use as a reliable source of truth for associations.

I agree that a cross-VM bridge standard will be useful, but I think that it's best to take that on as patterns of implementation and use case needs emerge organically.

Again, I don't think that the contents of the view are objectionable, I just want to get to the crux of the intent - the core of what we're trying to achieve with it - and get clear on what it can and cannot be relied on. It's clear there's no way we can trust a bridged token to be honest as there's obvious incentive to lie about such a thing and I don't see a way around asking either the corresponding contract and/or a trusted bridge the true association. If we're saying that validity is not a concern here, then good enough, we just need to be clear on that in docs and communications.

btspoony commented 4 months ago

Emm, maybe we mentioned validity too early, because that was not the original intention. The reason why I proposed that this view can be optional is also for this reason. Optional means that if this view exists, then this token can be marked as a token containing bridge information, that's all, If it doesn't exist, then it does not contain detailed bridge information. As for verification, verification is something outside of this view.

Just like NFTCollectionData, if the contract creator behaves maliciously, they can also set arbitrary values. You can only perform validity checks again after resolving the NFTCollectionData. After you obtain the View information, you can choose to believe it or choose not to believe it and proceed with further verification. This is the responsibility of the ViewResolver's user.

Just to clarify, the purpose of this view is to be a standard information provider So its core purpose is: we can obtain this part of information through the standard way of ViewResolver instead of querying through additional API with extra contract importing.

btspoony commented 4 months ago

However, speaking from my usage, I'm also fine without this view.

For the TokenList's requirements of bridge's info, I can obtain the accurate information through CrossVM interface and then convert it into additional structs for return. and then provide the accurate VM bridge Info as a JSON file to the Flow Wallet.

But I still think it would be better to have this view, so that I can do a quick filter (The MetadataViews.EVMBridgedMetadata, another view about Bridge, may be required for NFT, but FT does not need it.).