From 1d18062739fd5c986f17c868c3e13b2b26ca2a72 Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:30:37 -0400 Subject: [PATCH 1/3] code ref plugin --- docs/evm/cadence/vm-bridge.md | 642 +------------------------ docusaurus.config.js | 1 + package.json | 2 + src/plugins/code-reference.js | 49 ++ src/plugins/insert-info-tags-loader.js | 32 -- yarn.lock | 39 ++ 6 files changed, 108 insertions(+), 657 deletions(-) create mode 100644 src/plugins/code-reference.js delete mode 100644 src/plugins/insert-info-tags-loader.js diff --git a/docs/evm/cadence/vm-bridge.md b/docs/evm/cadence/vm-bridge.md index bf4b029200..d45d3358ac 100644 --- a/docs/evm/cadence/vm-bridge.md +++ b/docs/evm/cadence/vm-bridge.md @@ -87,129 +87,16 @@ Below are transactions relevant to onboarding assets:
onboard_by_type.cdc -```cadence title="onboard_by_type.cdc" -// source: https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/onboarding/onboard_by_type.cdc - -import "FungibleToken" -import "FlowToken" - -import "ScopedFTProviders" - -import "EVM" - -import "FlowEVMBridge" -import "FlowEVMBridgeConfig" - -/// This transaction onboards the asset type to the bridge, configuring the bridge to move assets between environments -/// NOTE: This must be done before bridging a Cadence-native asset to EVM -/// -/// @param type: The Cadence type of the bridgeable asset to onboard to the bridge -/// -transaction(type: Type) { - - let scopedProvider: @ScopedFTProviders.ScopedFTProvider - - prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { - - /* --- Configure a ScopedFTProvider --- */ - // - // Issue and store bridge-dedicated Provider Capability in storage if necessary - if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { - let providerCap = signer.capabilities.storage.issue( - /storage/flowTokenVault - ) - signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) - } - // Copy the stored Provider capability and create a ScopedFTProvider - let providerCapCopy = signer.storage.copy>( - from: FlowEVMBridgeConfig.providerCapabilityStoragePath - ) ?? panic("Invalid Provider Capability found in storage.") - let providerFilter = ScopedFTProviders.AllowanceFilter(FlowEVMBridgeConfig.onboardFee) - self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( - provider: providerCapCopy, - filters: [ providerFilter ], - expiration: getCurrentBlock().timestamp + 1.0 - ) - } - - execute { - // Onboard the asset Type - FlowEVMBridge.onboardByType( - type, - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - destroy self.scopedProvider - } - - post { - FlowEVMBridge.typeRequiresOnboarding(type) == false: - "Asset ".concat(type.identifier).concat(" was not onboarded to the bridge.") - } -} +```cadence onboard_by_type.cdc +!from https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/onboarding/onboard_by_type.cdc ```
onboard_by_evm_address.cdc -``` -// source: https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc - -import "FungibleToken" -import "FlowToken" - -import "ScopedFTProviders" - -import "EVM" - -import "FlowEVMBridge" -import "FlowEVMBridgeConfig" - -/// This transaction onboards the NFT type to the bridge, configuring the bridge to move NFTs between environments -/// NOTE: This must be done before bridging a Cadence-native NFT to EVM -/// -/// @param contractAddressHex: The EVM address of the contract defining the bridgeable asset to be onboarded -/// -transaction(contractAddressHex: String) { - - let contractAddress: EVM.EVMAddress - let scopedProvider: @ScopedFTProviders.ScopedFTProvider - - prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { - /* --- Construct EVMAddress from hex string (no leading `"0x"`) --- */ - // - self.contractAddress = EVM.addressFromString(contractAddressHex) - - /* --- Configure a ScopedFTProvider --- */ - // - // Issue and store bridge-dedicated Provider Capability in storage if necessary - if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { - let providerCap = signer.capabilities.storage.issue( - /storage/flowTokenVault - ) - signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) - } - // Copy the stored Provider capability and create a ScopedFTProvider - let providerCapCopy = signer.storage.copy>( - from: FlowEVMBridgeConfig.providerCapabilityStoragePath - ) ?? panic("Invalid Provider Capability found in storage.") - let providerFilter = ScopedFTProviders.AllowanceFilter(FlowEVMBridgeConfig.onboardFee) - self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( - provider: providerCapCopy, - filters: [ providerFilter ], - expiration: getCurrentBlock().timestamp + 1.0 - ) - } - - execute { - // Onboard the EVM contract - FlowEVMBridge.onboardByEVMAddress( - self.contractAddress, - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - destroy self.scopedProvider - } -} +```cadence onboard_by_evm_address.cdc +!from https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc ```
@@ -234,26 +121,10 @@ You may also retrieve the type associated with a given EVM contract address usin get_associated_type.cdc -```cadence title="get_associated_type.cdc" -// source: https://github.com/onflow/flow-evm-bridge/blob/main/cadence/scripts/bridge/get_associated_type.cdc - -import "EVM" - -import "FlowEVMBridgeConfig" - -/// Returns the Cadence Type associated with the given EVM address (as its hex String) -/// -/// @param evmAddressHex: The hex-encoded address of the EVM contract as a String -/// -/// @return The Cadence Type associated with the EVM address or nil if the address is not onboarded. `nil` may also be -/// returned if the address is not a valid EVM address. -/// -access(all) -fun main(addressHex: String): Type? { - let address = EVM.addressFromString(addressHex) - return FlowEVMBridgeConfig.getTypeAssociated(with: address) -} +```cadence get_associated_type.cdc +!from https://github.com/onflow/flow-evm-bridge/blob/main/cadence/scripts/bridge/get_associated_type.cdc ``` + Alternatively, given some onboarded Cadence type, you can retrieve the associated EVM address using the following @@ -263,28 +134,8 @@ script: get_associated_address.cdc -```cadence title="get_associated_address.cdc" -// source: https://github.com/onflow/flow-evm-bridge/blob/main/cadence/scripts/bridge/get_associated_evm_address.cdc - -import "EVM" - -import "FlowEVMBridgeConfig" - -/// Returns the EVM address associated with the given Cadence type (as its identifier String) -/// -/// @param typeIdentifier: The Cadence type identifier String -/// -/// @return The EVM address as a hex string if the type has an associated EVMAddress, otherwise nil -/// -access(all) -fun main(identifier: String): String? { - if let type = CompositeType(identifier) { - if let address = FlowEVMBridgeConfig.getEVMAddressAssociated(with: type) { - return address.toString() - } - } - return nil -} +```cadence get_associated_address.cdc +!from https://github.com/onflow/flow-evm-bridge/blob/main/cadence/scripts/bridge/get_associated_evm_address.cdc ``` @@ -300,127 +151,8 @@ Below are transactions relevant to bridging NFTs: bridge_nft_to_evm.cdc -```cadence title="bridge_nft_to_evm.cdc" -// source: https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc - -import "FungibleToken" -import "NonFungibleToken" -import "ViewResolver" -import "MetadataViews" -import "FlowToken" - -import "ScopedFTProviders" - -import "EVM" - -import "FlowEVMBridge" -import "FlowEVMBridgeConfig" -import "FlowEVMBridgeUtils" - -/// Bridges an NFT from the signer's collection in Cadence to the signer's COA in FlowEVM -/// -/// NOTE: This transaction also onboards the NFT to the bridge if necessary which may incur additional fees -/// than bridging an asset that has already been onboarded. -/// -/// @param nftIdentifier: The Cadence type identifier of the NFT to bridge - e.g. nft.getType().identifier -/// @param id: The Cadence NFT.id of the NFT to bridge to EVM -/// -transaction(nftIdentifier: String, id: UInt64) { - - let nft: @{NonFungibleToken.NFT} - let coa: auth(EVM.Bridge) &EVM.CadenceOwnedAccount - let requiresOnboarding: Bool - let scopedProvider: @ScopedFTProviders.ScopedFTProvider - - prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { - /* --- Reference the signer's CadenceOwnedAccount --- */ - // - // Borrow a reference to the signer's COA - self.coa = signer.storage.borrow(from: /storage/evm) - ?? panic("Could not borrow COA from provided gateway address") - - /* --- Construct the NFT type --- */ - // - // Construct the NFT type from the provided identifier - let nftType = CompositeType(nftIdentifier) - ?? panic("Could not construct NFT type from identifier: ".concat(nftIdentifier)) - // Parse the NFT identifier into its components - let nftContractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: nftType) - ?? panic("Could not get contract address from identifier: ".concat(nftIdentifier)) - let nftContractName = FlowEVMBridgeUtils.getContractName(fromType: nftType) - ?? panic("Could not get contract name from identifier: ".concat(nftIdentifier)) - - /* --- Retrieve the NFT --- */ - // - // Borrow a reference to the NFT collection, configuring if necessary - let viewResolver = getAccount(nftContractAddress).contracts.borrow<&{ViewResolver}>(name: nftContractName) - ?? panic("Could not borrow ViewResolver from NFT contract") - let collectionData = viewResolver.resolveContractView( - resourceType: nftType, - viewType: Type() - ) as! MetadataViews.NFTCollectionData? ?? panic("Could not resolve NFTCollectionData view") - let collection = signer.storage.borrow( - from: collectionData.storagePath - ) ?? panic("Could not access signer's NFT Collection") - - // Withdraw the requested NFT & calculate the approximate bridge fee based on NFT storage usage - let currentStorageUsage = signer.storage.used - self.nft <- collection.withdraw(withdrawID: id) - let withdrawnStorageUsage = signer.storage.used - var approxFee = FlowEVMBridgeUtils.calculateBridgeFee( - bytes: currentStorageUsage - withdrawnStorageUsage - ) * 1.10 - // Determine if the NFT requires onboarding - this impacts the fee required - self.requiresOnboarding = FlowEVMBridge.typeRequiresOnboarding(self.nft.getType()) - ?? panic("Bridge does not support this asset type") - if self.requiresOnboarding { - approxFee = approxFee + FlowEVMBridgeConfig.onboardFee - } - - /* --- Configure a ScopedFTProvider --- */ - // - // Issue and store bridge-dedicated Provider Capability in storage if necessary - if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { - let providerCap = signer.capabilities.storage.issue( - /storage/flowTokenVault - ) - signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) - } - // Copy the stored Provider capability and create a ScopedFTProvider - let providerCapCopy = signer.storage.copy>( - from: FlowEVMBridgeConfig.providerCapabilityStoragePath - ) ?? panic("Invalid Provider Capability found in storage.") - let providerFilter = ScopedFTProviders.AllowanceFilter(approxFee) - self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( - provider: providerCapCopy, - filters: [ providerFilter ], - expiration: getCurrentBlock().timestamp + 1.0 - ) - } - - pre { - self.nft.getType().identifier == nftIdentifier: - "Attempting to send invalid nft type - requested: ".concat(nftIdentifier) - .concat(", sending: ").concat(self.nft.getType().identifier) - } - - execute { - if self.requiresOnboarding { - // Onboard the NFT to the bridge - FlowEVMBridge.onboardByType( - self.nft.getType(), - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - } - // Execute the bridge - self.coa.depositNFT( - nft: <-self.nft, - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - // Destroy the ScopedFTProvider - destroy self.scopedProvider - } -} +```cadence bridge_nft_to_evm.cdc +!from https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc ``` @@ -428,115 +160,8 @@ transaction(nftIdentifier: String, id: UInt64) { bridge_nft_from_evm.cdc -```cadence title="bridge_nft_from_evm.cdc" -// source: https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/nft/bridge_nft_from_evm.cdc - -import "FungibleToken" -import "NonFungibleToken" -import "ViewResolver" -import "MetadataViews" -import "FlowToken" - -import "ScopedFTProviders" - -import "EVM" - -import "FlowEVMBridge" -import "FlowEVMBridgeConfig" -import "FlowEVMBridgeUtils" - -/// This transaction bridges an NFT from EVM to Cadence assuming it has already been onboarded to the FlowEVMBridge -/// NOTE: The ERC721 must have first been onboarded to the bridge. This can be checked via the method -/// FlowEVMBridge.evmAddressRequiresOnboarding(address: self.evmContractAddress) -/// -/// @param nftIdentifier: The Cadence type identifier of the NFT to bridge - e.g. nft.getType().identifier -/// @param id: The ERC721 id of the NFT to bridge to Cadence from EVM -/// -transaction(nftIdentifier: String, id: UInt256) { - - let nftType: Type - let collection: &{NonFungibleToken.Collection} - let scopedProvider: @ScopedFTProviders.ScopedFTProvider - let coa: auth(EVM.Bridge) &EVM.CadenceOwnedAccount - - prepare(signer: auth(BorrowValue, CopyValue, IssueStorageCapabilityController, PublishCapability, SaveValue, UnpublishCapability) &Account) { - /* --- Reference the signer's CadenceOwnedAccount --- */ - // - // Borrow a reference to the signer's COA - self.coa = signer.storage.borrow(from: /storage/evm) - ?? panic("Could not borrow COA from provided gateway address") - - /* --- Construct the NFT type --- */ - // - // Construct the NFT type from the provided identifier - self.nftType = CompositeType(nftIdentifier) - ?? panic("Could not construct NFT type from identifier: ".concat(nftIdentifier)) - // Parse the NFT identifier into its components - let nftContractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: self.nftType) - ?? panic("Could not get contract address from identifier: ".concat(nftIdentifier)) - let nftContractName = FlowEVMBridgeUtils.getContractName(fromType: self.nftType) - ?? panic("Could not get contract name from identifier: ".concat(nftIdentifier)) - - /* --- Reference the signer's NFT Collection --- */ - // - // Borrow a reference to the NFT collection, configuring if necessary - let viewResolver = getAccount(nftContractAddress).contracts.borrow<&{ViewResolver}>(name: nftContractName) - ?? panic("Could not borrow ViewResolver from NFT contract") - let collectionData = viewResolver.resolveContractView( - resourceType: self.nftType, - viewType: Type() - ) as! MetadataViews.NFTCollectionData? ?? panic("Could not resolve NFTCollectionData view") - if signer.storage.borrow<&{NonFungibleToken.Collection}>(from: collectionData.storagePath) == nil { - signer.storage.save(<-collectionData.createEmptyCollection(), to: collectionData.storagePath) - signer.capabilities.unpublish(collectionData.publicPath) - let collectionCap = signer.capabilities.storage.issue<&{NonFungibleToken.Collection}>(collectionData.storagePath) - signer.capabilities.publish(collectionCap, at: collectionData.publicPath) - } - self.collection = signer.storage.borrow<&{NonFungibleToken.Collection}>(from: collectionData.storagePath) - ?? panic("Could not borrow collection from storage path") - - /* --- Configure a ScopedFTProvider --- */ - // - // Calculate the bridge fee - bridging from EVM consumes no storage, so flat fee - let approxFee = FlowEVMBridgeUtils.calculateBridgeFee(bytes: 0) - // Issue and store bridge-dedicated Provider Capability in storage if necessary - if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { - let providerCap = signer.capabilities.storage.issue( - /storage/flowTokenVault - ) - signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) - } - // Copy the stored Provider capability and create a ScopedFTProvider - let providerCapCopy = signer.storage.copy>( - from: FlowEVMBridgeConfig.providerCapabilityStoragePath - ) ?? panic("Invalid Provider Capability found in storage.") - let providerFilter = ScopedFTProviders.AllowanceFilter(approxFee) - self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( - provider: providerCapCopy, - filters: [ providerFilter ], - expiration: getCurrentBlock().timestamp + 1.0 - ) - } - - execute { - // Execute the bridge - let nft: @{NonFungibleToken.NFT} <- self.coa.withdrawNFT( - type: self.nftType, - id: id, - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - // Ensure the bridged nft is the correct type - assert( - nft.getType() == self.nftType, - message: "Bridged nft type mismatch - requeswted: ".concat(self.nftType.identifier) - .concat(", received: ").concat(nft.getType().identifier) - ) - // Deposit the bridged NFT into the signer's collection - self.collection.deposit(token: <-nft) - // Destroy the ScopedFTProvider - destroy self.scopedProvider - } -} +```cadence bridge_nft_from_evm.cdc +!from https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/nft/bridge_nft_from_evm.cdc ``` @@ -560,128 +185,8 @@ Below are transactions relevant to bridging fungible tokens: bridge_tokens_to_evm.cdc -```cadence title="bridge_tokens_to_evm.cdc" -// source: https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/tokens/bridge_tokens_to_evm.cdc - -import "FungibleToken" -import "ViewResolver" -import "FungibleTokenMetadataViews" -import "FlowToken" - -import "ScopedFTProviders" - -import "EVM" - -import "FlowEVMBridge" -import "FlowEVMBridgeConfig" -import "FlowEVMBridgeUtils" - -/// Bridges a Vault from the signer's storage to the signer's COA in EVM.Account. -/// -/// NOTE: This transaction also onboards the Vault to the bridge if necessary which may incur additional fees -/// than bridging an asset that has already been onboarded. -/// -/// @param vaultIdentifier: The Cadence type identifier of the FungibleToken Vault to bridge -/// - e.g. vault.getType().identifier -/// @param amount: The amount of tokens to bridge from EVM -/// -transaction(vaultIdentifier: String, amount: UFix64) { - - let sentVault: @{FungibleToken.Vault} - let coa: auth(EVM.Bridge) &EVM.CadenceOwnedAccount - let requiresOnboarding: Bool - let scopedProvider: @ScopedFTProviders.ScopedFTProvider - - prepare(signer: auth(CopyValue, BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) { - /* --- Reference the signer's CadenceOwnedAccount --- */ - // - // Borrow a reference to the signer's COA - self.coa = signer.storage.borrow(from: /storage/evm) - ?? panic("Could not borrow COA from provided gateway address") - - /* --- Construct the Vault type --- */ - // - // Construct the Vault type from the provided identifier - let vaultType = CompositeType(vaultIdentifier) - ?? panic("Could not construct Vault type from identifier: ".concat(vaultIdentifier)) - // Parse the Vault identifier into its components - let tokenContractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: vaultType) - ?? panic("Could not get contract address from identifier: ".concat(vaultIdentifier)) - let tokenContractName = FlowEVMBridgeUtils.getContractName(fromType: vaultType) - ?? panic("Could not get contract name from identifier: ".concat(vaultIdentifier)) - - /* --- Retrieve the funds --- */ - // - // Borrow a reference to the FungibleToken Vault - let viewResolver = getAccount(tokenContractAddress).contracts.borrow<&{ViewResolver}>(name: tokenContractName) - ?? panic("Could not borrow ViewResolver from FungibleToken contract") - let vaultData = viewResolver.resolveContractView( - resourceType: vaultType, - viewType: Type() - ) as! FungibleTokenMetadataViews.FTVaultData? ?? panic("Could not resolve FTVaultData view") - let vault = signer.storage.borrow( - from: vaultData.storagePath - ) ?? panic("Could not access signer's FungibleToken Vault") - - // Withdraw the requested balance & calculate the approximate bridge fee based on storage usage - let currentStorageUsage = signer.storage.used - self.sentVault <- vault.withdraw(amount: amount) - let withdrawnStorageUsage = signer.storage.used - // Approximate the bridge fee based on the difference in storage usage with some buffer - var approxFee = FlowEVMBridgeUtils.calculateBridgeFee( - bytes: currentStorageUsage - withdrawnStorageUsage - ) * 1.10 - // Determine if the Vault requires onboarding - this impacts the fee required - self.requiresOnboarding = FlowEVMBridge.typeRequiresOnboarding(self.sentVault.getType()) - ?? panic("Bridge does not support this asset type") - if self.requiresOnboarding { - approxFee = approxFee + FlowEVMBridgeConfig.onboardFee - } - - /* --- Configure a ScopedFTProvider --- */ - // - // Issue and store bridge-dedicated Provider Capability in storage if necessary - if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { - let providerCap = signer.capabilities.storage.issue( - /storage/flowTokenVault - ) - signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) - } - // Copy the stored Provider capability and create a ScopedFTProvider - let providerCapCopy = signer.storage.copy>( - from: FlowEVMBridgeConfig.providerCapabilityStoragePath - ) ?? panic("Invalid Provider Capability found in storage.") - let providerFilter = ScopedFTProviders.AllowanceFilter(approxFee) - self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( - provider: providerCapCopy, - filters: [ providerFilter ], - expiration: getCurrentBlock().timestamp + 1.0 - ) - } - - pre { - self.sentVault.getType().identifier == vaultIdentifier: - "Attempting to send invalid vault type - requested: ".concat(vaultIdentifier) - .concat(", sending: ").concat(self.sentVault.getType().identifier) - } - - execute { - if self.requiresOnboarding { - // Onboard the Vault to the bridge - FlowEVMBridge.onboardByType( - self.sentVault.getType(), - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - } - // Execute the bridge - self.coa.depositTokens( - vault: <-self.sentVault, - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - // Destroy the ScopedFTProvider - destroy self.scopedProvider - } -} +```cadence bridge_tokens_to_evm.cdc +!from https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/tokens/bridge_tokens_to_evm.cdc ``` @@ -689,121 +194,8 @@ transaction(vaultIdentifier: String, amount: UFix64) { bridge_tokens_from_evm.cdc -```cadence title="bridge_tokens_from_evm.cdc" -// source: https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/tokens/bridge_tokens_from_evm.cdc - -import "FungibleToken" -import "FungibleTokenMetadataViews" -import "ViewResolver" -import "MetadataViews" -import "FlowToken" - -import "ScopedFTProviders" - -import "EVM" - -import "FlowEVMBridge" -import "FlowEVMBridgeConfig" -import "FlowEVMBridgeUtils" - -/// This transaction bridges fungible tokens from EVM to Cadence assuming it has already been onboarded to the -/// FlowEVMBridge. -/// -/// NOTE: The ERC20 must have first been onboarded to the bridge. This can be checked via the method -/// FlowEVMBridge.evmAddressRequiresOnboarding(address: self.evmContractAddress) -/// -/// @param vaultIdentifier: The Cadence type identifier of the FungibleToken Vault to bridge -/// - e.g. vault.getType().identifier -/// @param amount: The amount of tokens to bridge from EVM -/// -transaction(vaultIdentifier: String, amount: UInt256) { - - let vaultType: Type - let receiver: &{FungibleToken.Vault} - let scopedProvider: @ScopedFTProviders.ScopedFTProvider - let coa: auth(EVM.Bridge) &EVM.CadenceOwnedAccount - - prepare(signer: auth(BorrowValue, CopyValue, IssueStorageCapabilityController, PublishCapability, SaveValue, UnpublishCapability) &Account) { - /* --- Reference the signer's CadenceOwnedAccount --- */ - // - // Borrow a reference to the signer's COA - self.coa = signer.storage.borrow(from: /storage/evm) - ?? panic("Could not borrow COA from provided gateway address") - - /* --- Construct the Vault type --- */ - // - // Construct the Vault type from the provided identifier - self.vaultType = CompositeType(vaultIdentifier) - ?? panic("Could not construct Vault type from identifier: ".concat(vaultIdentifier)) - // Parse the Vault identifier into its components - let tokenContractAddress = FlowEVMBridgeUtils.getContractAddress(fromType: self.vaultType) - ?? panic("Could not get contract address from identifier: ".concat(vaultIdentifier)) - let tokenContractName = FlowEVMBridgeUtils.getContractName(fromType: self.vaultType) - ?? panic("Could not get contract name from identifier: ".concat(vaultIdentifier)) - - /* --- Reference the signer's Vault --- */ - // - // Borrow a reference to the FungibleToken Vault, configuring if necessary - let viewResolver = getAccount(tokenContractAddress).contracts.borrow<&{ViewResolver}>(name: tokenContractName) - ?? panic("Could not borrow ViewResolver from FungibleToken contract") - let vaultData = viewResolver.resolveContractView( - resourceType: self.vaultType, - viewType: Type() - ) as! FungibleTokenMetadataViews.FTVaultData? ?? panic("Could not resolve FTVaultData view") - // If the vault does not exist, create it and publish according to the contract's defined configuration - if signer.storage.borrow<&{FungibleToken.Vault}>(from: vaultData.storagePath) == nil { - signer.storage.save(<-vaultData.createEmptyVault(), to: vaultData.storagePath) - - signer.capabilities.unpublish(vaultData.receiverPath) - signer.capabilities.unpublish(vaultData.metadataPath) - - let receiverCap = signer.capabilities.storage.issue<&{FungibleToken.Vault}>(vaultData.storagePath) - let metadataCap = signer.capabilities.storage.issue<&{FungibleToken.Vault}>(vaultData.storagePath) - - signer.capabilities.publish(receiverCap, at: vaultData.receiverPath) - signer.capabilities.publish(metadataCap, at: vaultData.metadataPath) - } - self.receiver = signer.storage.borrow<&{FungibleToken.Vault}>(from: vaultData.storagePath) - ?? panic("Could not borrow Vault from storage path") - - /* --- Configure a ScopedFTProvider --- */ - // - // Calculate the bridge fee - bridging from EVM consumes no storage, so flat fee - let approxFee = FlowEVMBridgeUtils.calculateBridgeFee(bytes: 0) - // Issue and store bridge-dedicated Provider Capability in storage if necessary - if signer.storage.type(at: FlowEVMBridgeConfig.providerCapabilityStoragePath) == nil { - let providerCap = signer.capabilities.storage.issue( - /storage/flowTokenVault - ) - signer.storage.save(providerCap, to: FlowEVMBridgeConfig.providerCapabilityStoragePath) - } - // Copy the stored Provider capability and create a ScopedFTProvider - let providerCapCopy = signer.storage.copy>( - from: FlowEVMBridgeConfig.providerCapabilityStoragePath - ) ?? panic("Invalid Provider Capability found in storage.") - let providerFilter = ScopedFTProviders.AllowanceFilter(approxFee) - self.scopedProvider <- ScopedFTProviders.createScopedFTProvider( - provider: providerCapCopy, - filters: [ providerFilter ], - expiration: getCurrentBlock().timestamp + 1.0 - ) - } - - execute { - // Execute the bridge request - let vault: @{FungibleToken.Vault} <- self.coa.withdrawTokens( - type: self.vaultType, - amount: amount, - feeProvider: &self.scopedProvider as auth(FungibleToken.Withdraw) &{FungibleToken.Provider} - ) - // Ensure the bridged vault is the correct type - assert(vault.getType() == self.vaultType, message: "Bridged vault type mismatch") - // Deposit the bridged token into the signer's vault - self.receiver.deposit(from: <-vault) - // Destroy the ScopedFTProvider - destroy self.scopedProvider - } -} +```cadence bridge_tokens_from_evm.cdc +!from https://www.github.com/onflow/flow-evm-bridge/blob/main/cadence/transactions/bridge/tokens/bridge_tokens_from_evm.cdc ``` diff --git a/docusaurus.config.js b/docusaurus.config.js index 7c32808ab0..ad9e7f4daa 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -177,6 +177,7 @@ const config = { ({ docs: { beforeDefaultRemarkPlugins: [ + require('./src/plugins/code-reference'), [ remarkCodeHike, { theme: 'nord', lineNumbers: true, showCopyButton: true }, diff --git a/package.json b/package.json index c8889b51bf..b69ed545d1 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "dotenv": "16.0.3", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.1", + "node-fetch": "^3.3.2", "postcss": "^8.4.31", "prettier": "^2.8.7", "prism-react-renderer": "^1.3.5", @@ -51,6 +52,7 @@ "shiki": "1.14.1", "tailwind-scrollbar-hide": "1.1.7", "tailwindcss": "^3.3.5", + "unist-util-visit": "^5.0.0", "webpack-merge": "5.8.0" }, "devDependencies": { diff --git a/src/plugins/code-reference.js b/src/plugins/code-reference.js new file mode 100644 index 0000000000..779f1f13d7 --- /dev/null +++ b/src/plugins/code-reference.js @@ -0,0 +1,49 @@ +import { visit } from 'unist-util-visit'; +import fetch from 'node-fetch'; + +const VALUE_STARTS_WITH = '!from '; + +const githubReplace = /^(https:\/\/)(www.)?github.com\/(.+)\/blob\/(.+)/; + +const getUrl = (nodeValue) => { + const url = nodeValue.replace(VALUE_STARTS_WITH, '').trim(); + + return url.replace(githubReplace, '$1raw.githubusercontent.com/$3/$4'); +}; + +const plugin = () => { + const transformer = async (ast) => { + const promises = []; + visit(ast, 'code', (node) => { + if (node.value?.startsWith(VALUE_STARTS_WITH)) { + const url = getUrl(node.value); + if (!url) { + return; + } + const fetchPromise = fetch(url) + .then((res) => { + if (!res.ok) { + throw new Error( + `Failed to fetch code from ${url}: ${res.statusText}`, + ); + } + return res.text(); + }) + .then((code) => { + node.value = code; + }) + .catch((err) => { + console.error(err); + node.value = `Error fetching code: ${err.message}`; + }); + + promises.push(fetchPromise); + } + }); + + await Promise.all(promises); + }; + return transformer; +}; + +export default plugin; diff --git a/src/plugins/insert-info-tags-loader.js b/src/plugins/insert-info-tags-loader.js deleted file mode 100644 index a930d3a095..0000000000 --- a/src/plugins/insert-info-tags-loader.js +++ /dev/null @@ -1,32 +0,0 @@ -module.exports = function (source) { - const infoBannerMessage = - 'For Cadence 0.42 go to [Legacy Docs](https://legacy.developers.flow.com/)'; - // Function to check if a page content contains "cadence" code block - const containsCadenceCodeBlock = (content) => { - return content.includes('```cadence'); - }; - - // Function to insert :::info::: tag into content - const insertInfoTag = (content) => { - const infoBanner = ':::info\n' + infoBannerMessage + '\n:::\n'; - // Check fof front matter - if (!content.startsWith('---\n')) { - // Insert :::info::: tag at the beginning of the content - return infoBanner + content; - } - // Split the content into front matter and body - const [frontMatter, ...body] = content.split('---\n').slice(1); - - // Insert :::info::: tag after front matter - return `---\n${frontMatter}---\n${infoBanner}${body.join('---\n')}`; - }; - - // Check if the content contains a "cadence" code block - if (containsCadenceCodeBlock(source)) { - // Insert :::info::: tag - return insertInfoTag(source); - } - - // Return the original content if no changes are needed - return source; -}; diff --git a/yarn.lock b/yarn.lock index 7180ea239e..7a74733951 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5414,6 +5414,11 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + date-fns@2.29.3: version "2.29.3" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" @@ -6608,6 +6613,14 @@ feed@^4.2.2: dependencies: xml-js "^1.6.11" +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -6790,6 +6803,13 @@ format@^0.2.0: resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -9415,6 +9435,11 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + node-downloader-helper@^2.1.6: version "2.1.9" resolved "https://registry.yarnpkg.com/node-downloader-helper/-/node-downloader-helper-2.1.9.tgz#a59ee7276b2bf708bbac2cc5872ad28fc7cd1b0e" @@ -9444,6 +9469,15 @@ node-fetch@^2.0.0, node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-fetch@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -13055,6 +13089,11 @@ web-namespaces@^2.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From a409880b528564665508c798863c25fd2eb2e6af Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:45:47 -0400 Subject: [PATCH 2/3] contributing --- CONTRIBUTING.md => CONTRIBUTING | 48 ++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 10 deletions(-) rename CONTRIBUTING.md => CONTRIBUTING (95%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING similarity index 95% rename from CONTRIBUTING.md rename to CONTRIBUTING index 57dff0a2f9..81a8d03e9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING @@ -135,18 +135,23 @@ import { useLocation } from '@docusaurus/router'; ``` - +:::warning + The `index` page should only include information that is available elsewhere within the category's other pages. - + +::: ### Using DocCardList Using `DocCardList` improves the layout of the index page: - +:::note Example + ![edit page](./static/images/contributing/doc-card-list-light.png#gh-light-mode-only) + ![edit page](./static/images/contributing/doc-card-list-dark.png#gh-dark-mode-only) - + +::: On the most pages you can use just `` component imported from `'@theme/DocCardList'` @@ -173,12 +178,13 @@ import { useLocation } from '@docusaurus/router'; !isSamePath(item.href, useLocation().pathname))}/> ``` - +:::warning + If you use `` on the top level category index page (e.g. `./docs/tools/index.mdx`), Docusaurus will throw an error: > useCurrentSidebarCategory() should only be used on category index pages. - +::: ## SEO @@ -225,9 +231,11 @@ Writing links in your documents is easy if you follow this rule-of-thumb: If it [link](https://www.google.com) ``` - +:::tip + Use relative links directly to .md/.mdx files - + +::: ### Callouts @@ -243,12 +251,32 @@ Available types: Example: ```markdown - +:::tip + Use relative links directly to .md/.mdx files - +::: ``` +### Code references + +To include code from a file using a direct URL use `!from` operand inside a code block. + +Example: + +```` +``` +!from https://raw.githubusercontent.com/onflow/docs/refs/heads/main/docs/evm/about.md +``` +```` + +mdx +Copy code +```javascript +{code} +This method keeps your documentation synchronized with your codebase by pulling the latest code directly into your docs. + + ## Content Validation Content is validated each time a PR is submitted to the `docs` repository. From 1c5dca84fddd361699c90d8764ce98858d1f7fac Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:17:41 -0400 Subject: [PATCH 3/3] markdown snippet --- CONTRIBUTING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING b/CONTRIBUTING index 81a8d03e9f..585bdf8bc9 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -264,7 +264,7 @@ To include code from a file using a direct URL use `!from` operand inside a code Example: -```` +````markdown ``` !from https://raw.githubusercontent.com/onflow/docs/refs/heads/main/docs/evm/about.md ```