diff --git a/README.md b/README.md index 2c42ae09..123151bd 100644 --- a/README.md +++ b/README.md @@ -1,125 +1,190 @@ ![Tests](https://github.com/onflow/flow-evm-bridge/actions/workflows/cadence_test.yml/badge.svg) [![codecov](https://codecov.io/gh/onflow/flow-evm-bridge/graph/badge.svg?token=C1vCK0t88F)](https://codecov.io/gh/onflow/flow-evm-bridge) -# [WIP] Flow EVM Bridge +# Flow EVM Bridge -> :warning: This repo is a work in progress :building_construction: +> :warning: Upcoming breaking changes may result in updated deployment addresses and redirection of bridge requests. -This repo contains contracts enabling bridging of fungible & non-fungible assets between Cadence and EVM. +This repo contains contracts enabling bridging of fungible & non-fungible tokens between Cadence and EVM. -## Deploying Bridge Contracts Locally +## Deployments -As the bridge contracts are still in development, builders should be aware that interfaces may change. If you would like to participate in early development, below are the steps to deploy the bridge contracts to your local emulator environment. +PreviewNet is currently the only EVM-enabled network on Flow. The bridge in this repo are deployed to the following +addresses: -### Prerequisites +|Network|Address| +|---|---| +|PreviewNet|`0x634acef27f871527`| +|Testnet|TBD| +|Mainnet|TBD| -Install an EVM-compatible pre-release version of the Flow CLI (currently [v1.12.0-cadence-v1.0.0-M8-2](https://github.com/onflow/flow-cli/releases/tag/v1.12.0-cadence-v1.0.0-M8-2)): +## Interacting with the bridge -```sh -sudo sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/feature/stable-cadence/install.sh)" -``` +> :information_source: All bridging activity in either direction is orchestrated via Cadence on `CadenceOwnedAccount` +> (COA) resources. This means that all bridging activity must be initiated via a Cadence transaction, not an EVM +> transaction regardless of the directionality of the bridge request. For more information on the interplay between +> Cadence and EVM, see [EVM Integration FLIP #223](https://github.com/onflow/flips/pull/225/files) -If you wish to interact with the contracts from the EVM side, this repo is configured for use with Foundry. See [Foundry's installation docs](https://book.getfoundry.sh/getting-started/installation) for more information. +### Overview -### Setup +The Flow EVM bridge allows both fungible and non-fungible tokens to move atomically between Cadence and EVM. In the +context of EVM, fungible tokens are defined as ERC20 tokens, and non-fungible tokens as ERC721 tokens. In Cadence, +fungible tokens are defined by contracts implementing FungibleToken and non-fungible tokens the NonFungibleToken +standard contract interfaces. -1. Clone this repo and navigate to the root directory. +Like all operations on Flow, there are native fees associated with both computation and storage. To prevent spam and +sustain the bridge account's storage consumption, fees are charged for both onboarding assets and bridging assets. In +the case where storage consumption is expected, fees are charges based on the storage consumed at the current network +rates. In all cases, there is a flat-rate fee in addition to any storage fees. -2. Start the emulator with EVM enabled +### Onboarding -```sh -flow-c1 emulator --evm-enabled -``` +Since a contract must define the asset in the target VM, an asset must be "onboarded" to the bridge before requests can +be fulfilled. Moving from Cadence to EVM, onboarding can occur on the fly, deploying a template contract in the same +transaction as the asset is bridged to EVM if the transaction so specifies. Moving from EVM to Cadence, however, +requires that onboarding occur in a separate transaction due to the fact that a Cadence contract is initialized at the +end of a transaction and isn't available in the runtime until after the transaction has executed. -3. (Optional) If you would like to interact with Flow EVM via the EVM RPC gateway (e.g. via Hardhat, Foundry, etc.), run the gateway locally +Below are transactions relevant to onboarding assets: +- [`onboard_by_type.cdc`](./cadence/transactions/bridge/onboarding/onboard_by_type.cdc) +- [`onboard_by_evm_address.cdc`](./cadence/transactions/bridge/onboarding/onboard_by_evm_address.cdc) -> :information_source: Running the gateway enables you to interact with your emulator instance via traditional EVM tooling. More information about the Flow EVM Gateway can be found [here](https://github.com/onflow/flow-evm-gateway) -```sh -flow-c1 evm gateway --coa-address f8d6e0586b0a20c7 --coinbase 0000000000000000000000025521cbccbbaa9977 --coa-key fe809cc837ddcd7e761a482721c050aae43657448db859f4eb8fc421e9609938 --network emulator -``` +### Bridging -4. Execute the first setup script - -```sh -sh local/setup_emulator.1.sh -``` +Once an asset has been onboarded, either by its Cadence type or EVM contract address, it can be bridged in either +direction referred to by its Cadence type. For Cadence-native assets, this is simply its native type. For EVM-native +assets, this is in most cases a templated Cadence contract deployed to the bridge account, the name of which is derived +from the EVM contract address. For instance, an ERC721 contract at address `0x1234` would be onboarded to the bridge as +`EVMVMBridgedNFT_0x1234`, making its type identifier `A..EVMVMBridgedNFT_0x1234.NFT`. -5. Note the last `deployedContractAddress` field in a `evm.TransactionExecuted` event emitted by a deployment transaction executed in the setup script. Got into [`setup_emulator.2.sh`](./local/setup_emulator.2.sh) and replace the first command's argument with the value. Then run the second setup script. +However, the derivation of these identifiers can be abstracted within transactions. For example, calling applications +can provide the defining contract address and name of the bridged asset (see +[`bridge_nft_to_evm.cdc`](./cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc)). Alternatively, the defining EVM +contract could be provided, etc - this flexibility is thanks to Cadence's scripted transactions. -```sh -sh local/setup_emulator.2.sh -``` +#### NFTs -6. Note the `deployedContractAddress` emitted by the last command in the second script. This is an ERC721 contract deployed by the `erc721` Flow account (as named in the [`flow.json`](./flow.json)). We'll use this address in the last command with one other address. The last command we execute will give us an EVM-native ERC721 minted to the `user` account as named in the [`flow.json`](./flow.json). But first we need to get the COA address owned by the `user` account. Run the following script to get the user's COA's EVM address: +Any Cadence NFTs bridging to EVM are escrowed in the bridge account and either minted in a bridge-deployed ERC721 +contract or transferred from escrow to the calling COA in EVM. On the return trip, NFTs are escrowed in EVM - owned by +the bridge's COA - and either unlocked from escrow if locked or minted from a bridge-owned NFT contract. -```sh -flow-c1 scripts execute ./cadence/scripts/evm/get_evm_address_string.cdc f3fcd2c1a78f5eee -``` +Below are transactions relevant to bridging NFTs: +- [`bridge_nft_to_evm.cdc`](./cadence/transactions/bridge/nft/bridge_nft_to_evm.cdc) +- [`bridge_nft_from_evm.cdc`](./cadence/transactions/bridge/nft/bridge_nft_from_evm.cdc) -7. To mint the EVM-native ERC721 to the `user` account, run the following script with the `deployedContractAddress` from the second setup script and the COA address from the previous step: +#### Fungible Tokens -```sh -flow-c1 transactions send ./cadence/transactions/example-assets/safe_mint_erc721.cdc \ - 42 "URI" 200000 \ - --signer erc721 -``` +Any Cadence fungible tokens bridging to EVM are escrowed in the bridge account only if they are Cadence-native. If the +bridge defines the tokens, they are burned. On the return trip the pattern is similar, with the bridge burning +bridge-defined tokens or escrowing them if they are EVM-native. In all cases, if the bridge has authority to mint on one +side, it must escrow on the other as the native VM contract is owned by an external party. -You now have all bridge contracts deployed, and a user account with an `ExampleNFT` Cadence NFT minted to it and an `ExampleERC721` EVM-native ERC721 minted to its COA. If you've run the gateway, you can call to the ERC721 address to get the owner of the minted NFT ID (42 in the last command). +With fungible tokens in particular, there may be some cases where the Cadence contract is not deployed to the bridge +account, but the bridge still follows a mint/burn pattern in Cadence. These cases are handled via +[`TokenHandler`](./cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc) implementations. Also know +that moving $FLOW to EVM is built into the `EVMAddress` object so any requests bridging $FLOW to EVM will simply +leverage this interface; however, moving $FLOW from EVM to Cadence must be done through the COA resource. -```sh -cast call --rpc-url 127.0.0.1:3000 0x "ownerOf(uint256)" 42 -``` +Below are transactions relevant to bridging fungible tokens: +- [`bridge_tokens_to_evm.cdc`](./cadence/transactions/bridge/ft/bridge_tokens_to_evm.cdc) +- [`bridge_tokens_from_evm.cdc`](./cadence/transactions/bridge/ft/bridge_tokens_from_evm.cdc) -The result should be the COA address returned when you queried the `user` account's COA address. To get the NFT ID of the Cadence NFT minted to the `user`, run the following script which queries the `user`'s Flow account to check the Collection for the NFT IDs contained within it. -```sh -flow-c1 scripts execute cadence/scripts/nft/get_ids.cdc f3fcd2c1a78f5eee cadenceExampleNFTCollection -``` +## Prep Your Assets for Bridging -### Bridging +### Context -The user has some NFTs to bridge and the bridge is now running, so let's get started. +To maximize utility to the ecosystem, this bridge is permissionless and open to any fungible or non-fungible token as +defined by the respective Cadence standards and limited to ERC20 and ERC721 Solidity standards. Ultimately, a project +does not have to do anything for users to be able to bridge their assets between VMs. However, there are some +considerations developers may take to enhance the representation of their assets in non-native VMs. These largely relate +to asset metadata and ensuring that bridging does not compromise critical user assumptions about asset ownership. -1. Onboard `ExampleNFT` to the bridge. This will deploy a corresponding ERC721 in EVM from which the bridge's owning COA can mint & escrow tokens. To do this, we run the [`onboard_by_type.cdc` transaction](./cadence/transactions/bridge/onboard_by_type.cdc) and provide the Cadence type identifier of the NFT we want to onboard. +### EVMBridgedMetadata -```sh -flow-c1 transactions send cadence/transactions/bridge/onboard_by_type.cdc \ - 'A.179b6b1cb6755e31.ExampleNFT.NFT' \ - --signer user -``` +Proposed in [@onflow/flow-nft/pull/203](https://github.com/onflow/flow-nft/pull/203), the `EVMBridgedMetadata` view +presents a mechanism to both represent metadata from bridged EVM assets as well as enable Cadence-native projects to +specify the representation of their assets in EVM. Implementing this view is not required for assets to be bridged, but +the bridge does default to it when available as a way to provide projects greater control over their EVM asset +definitions within the scope of ERC20 and ERC721 standards. -2. Now that the `ExampleNFT` is onboarded, we can bridge the NFT from Cadence to EVM. To do this, we run the [`bridge_nft_to_evm.cdc` transaction](./cadence/transactions/bridge/bridge_nft.cdc) and provide the NFT contract address, contract name, and ID we want to bridge. Refer to the `get_ids.cdc` script we ran above in the [setup](#setup) section to get the NFT ID. +The interface for this view is as follows: -```sh -flow-c1 transactions send cadence/transactions/bridge/bridge_nft_to_evm.cdc \ - 0x179b6b1cb6755e31 ExampleNFT \ - --signer user -``` +```cadence +access(all) struct URI: MetadataViews.File { + /// The base URI prefix, if any. Not needed for all URIs, but helpful + /// for some use cases For example, updating a whole NFT collection's + /// image host easily + access(all) let baseURI: String? + /// The URI string value + /// NOTE: this is set on init as a concatenation of the baseURI and the + /// value if baseURI != nil + access(self) let value: String -3. Several events signify the bridging was successful, namely the `A.f8d6e0586b0a20c7.FlowEVMBridge.BridgedNFTToEVM` event which contains the type, ID, ERC721 ID, recipient and EVM defining ERC721 contract address. Let's validate the owner of that NFT with another call to the gateway, referencing the emitted event values for the command below: - -```sh -cast call --rpc-url 127.0.0.1:3000 "ownerOf(uint256)" -``` + access(all) view fun uri(): String + +} -4. The result should be the `user` account's COA address. Let's now bridge back to Cadence with the [`bridge_nft_from_evm.cdc` transaction](./cadence/transactions/bridge/bridge_nft_from_evm.cdc) with arguments very similar to bridging to EVM. +access(all) struct EVMBridgedMetadata { + access(all) let name: String + access(all) let symbol: String -```sh -flow-c1 transactions send cadence/transactions/bridge/bridge_nft_from_evm.cdc \ - 0x179b6b1cb6755e31 ExampleNFT \ - --signer user + access(all) let uri: {MetadataViews.File} +} ``` -We should now see several more events including `A.f8d6e0586b0a20c7.FlowEVMBridge.BridgedNFTFromEVM` which informs us of the type, id, EVM ID, calling COA EVM Address, and ERC721 contract Address associated with the bridge request. Note that the Flow account Address isn't returned, since the resource-oriented nature of NFTs in Cadence means the bridge doesn't know the identity of the caller in Cadence. However, the successive `A.f8d6e0586b0a20c7.NonFungibleToken.Deposited` method identifies the bridged NFT ID and the recipient account as `0xf3fcd2c1a78f5eee`. - -And that's it! Bridging an ERC721 from EVM to Cadence is largely similar with the exception that onboarding is conducted via the contract's EVM address since that's how the NFT is identified. See the [`onboard_by_evm_address.cdc` transaction](./cadence/transactions/bridge/onboard_by_evm_address.cdc) for what that looks like. - -## References - -This repo is working on the basis of the design laid out in [FLIP #237](https://github.com/onflow/flips/pull/233). - -### Additional Resource +This uri value could be a pointer to some offchain metadata if you expect your metadata to be static. Or you could +couple the `uri()` method with the utility contract below to serialize the onchain metadata on the fly. + +### SerializeMetadata + +The key consideration with respect to metadata is the distinct metadata storage patterns between ecosystem. It's +critical for NFT utility that the metadata be bridged in addition to the representation of the NFTs ownership. However, +it's commonplace for Cadence NFTs to store metadata onchain while EVM NFTs often store an onchain pointer to metadata +stored offchain. In order for Cadence NFTs to be properly represented in EVM platforms, the metadata must be bridged in +a format expected by those platforms and be done in a manner that also preserves the atomicity of bridge requests. The +path forward on this was decided to be a commitment of serialized Cadence NFT metadata into formats popular in the EVM +ecosystem. + +For assets that do not implement `EVMBridgedMetadata`, the bridge will attempt to serialize the metadata of the asset as +a JSON data URL string. This is done via the [`SerializeMetadata` +contract](./cadence/contracts/utils/SerializeMetadata.cdc) which serializes metadata values into a JSON blob compatible +with the OpenSea metadata standard. The serialized metadata is then committed as the ERC721 `tokenURI` upon bridging +Cadence-native NFTs to EVM. Since Cadence NFTs can easily update onchain metadata either by field or by the ownership of +sub-NFTs, this serialization pattern enables token URI updates on subsequent bridge requests. + +### Opting Out + +It's also recognized that the logic of some use cases may actually be compromised by the act of bridging, particularly +in such a unique runtime environment. These would be cases that do not maintain ownership assumptions implicit to +ecosystem standards. For instance, an ERC721 implementation may reclaim a user's assets after a month of inactivity time +period. In such a case, bridging that ERC721 to Cadence would decouple the representation of ownership of the bridged +NFT from the actual ownership in the definining ERC721 contract after the token had been reclaimed - there would be no +NFT in escrow for the bridge to transfer on fulfillment of the NFT back to EVM. In such cases, projects may choose to +opt-out of bridging, but **importantly must do so before the asset has been onboarded to the bridge**. + +For Solidity contracts, opting out is as simple as extending the [`BridgePermissions.sol` abstract +contract](./solidity/src/BridgePermissions.sol) which defaults `allowsBridging()` to false. The bridge explicitly checks +for the implementation of `IBridgePermissions` and the value of `allowsBridging()` to validate that the contract has not +opted out of bridging. + +Similarly, Cadence contracts can implement the [`IBridgePermissions.cdc` contract +interface](./cadence/contracts/bridge/interfaces/IBridgePermissions.cdc). This contract has a single method +`allowsBridging()` with a default implementation returning `false`. Again, the bridge explicitly checks for the +implementation of `IBridgePermissions` and the value of `allowsBridging()` to validate that the contract has not opted +out of bridging. Should you later choose to enable bridging, you can simply override the default implementation and +return true. + +In both cases, `allowsBridging()` gates onboarding to the bridge. Once onboarded - **a permissionless operation anyone can +execute** - the value of `allowsBridging()` is irrelevant and assets can move between VMs permissionlessly. + +## Under the Hood (facilitating cross-vm interactions) + +For an in-depth look at the high-level architecture of the bridge, see [FLIP +#237](https://github.com/onflow/flips/pull/233) + +### Additional Resources For the current state of Flow EVM across various task paths, see the following resources: diff --git a/cadence/contracts/bridge/FlowEVMBridge.cdc b/cadence/contracts/bridge/FlowEVMBridge.cdc index d0963dd9..fc60c6be 100644 --- a/cadence/contracts/bridge/FlowEVMBridge.cdc +++ b/cadence/contracts/bridge/FlowEVMBridge.cdc @@ -9,7 +9,7 @@ import "FlowToken" import "EVM" import "EVMUtils" -import "BridgePermissions" +import "IBridgePermissions" import "ICrossVM" import "IEVMBridgeNFTMinter" import "IEVMBridgeTokenMinter" @@ -54,32 +54,6 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { evmContractAddress: String ) - /**************** - Constructs - *****************/ - - /// Struct used to preserve and pass around multiple values preventing the need to make multiple EVM calls - /// during EVM asset onboarding - /// - access(all) struct EVMOnboardingValues { - access(all) let evmContractAddress: EVM.EVMAddress - access(all) let name: String - access(all) let symbol: String - access(all) let decimals: UInt8? - - init( - evmContractAddress: EVM.EVMAddress, - name: String, - symbol: String, - decimals: UInt8? - ) { - self.evmContractAddress = evmContractAddress - self.name = name - self.symbol = symbol - self.decimals = decimals - } - } - /************************** Public Onboarding **************************/ @@ -95,6 +69,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { access(all) fun onboardByType(_ type: Type, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}) { pre { + !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" type != Type<@FlowToken.Vault>(): "$FLOW cannot be bridged via the VM bridge - use the CadenceOwnedAccount interface" self.typeRequiresOnboarding(type) == true: "Onboarding is not needed for this type" @@ -106,7 +81,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { // // Withdraw from feeProvider and deposit to self FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: FlowEVMBridgeConfig.onboardFee) - + /* EVM setup */ // // Deploy an EVM defining contract via the FlowBridgeFactory.sol contract @@ -162,6 +137,9 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { _ address: EVM.EVMAddress, feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) { + pre { + !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" + } /* Validate the EVM contract */ // // Ensure the project has not opted out of bridge support @@ -178,7 +156,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { // // Withdraw fee from feeProvider and deposit FlowEVMBridgeUtils.depositFee(feeProvider, feeAmount: FlowEVMBridgeConfig.onboardFee) - + /* Setup Cadence-defining contract */ // // Deploy a defining Cadence contract to the bridge account @@ -202,6 +180,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) { pre { + !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" !token.isInstance(Type<@{FungibleToken.Vault}>()): "Mixed asset types are not yet supported" self.typeRequiresOnboarding(token.getType()) == false: "NFT must first be onboarded" } @@ -216,7 +195,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { // Grab the URI from the NFT if available var uri: String = "" // Default to project-specified URI - if let metadata = token.resolveView(Type()) as! CrossVMNFT.EVMBridgedMetadata? { + if let metadata = token.resolveView(Type()) as! MetadataViews.EVMBridgedMetadata? { uri = metadata.uri.uri() } else { // Otherwise, serialize the NFT @@ -238,7 +217,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { let associatedAddress = FlowEVMBridgeConfig.getEVMAddressAssociated(with: tokenType) ?? panic("No EVMAddress found for token type") let isFactoryDeployed = FlowEVMBridgeUtils.isEVMContractBridgeOwned(evmContractAddress: associatedAddress) - + /* Third-party controlled ERC721 handling */ // // Not bridge-controlled, transfer existing ownership @@ -285,6 +264,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { protectedTransferCall: fun (): EVM.Result ): @{NonFungibleToken.NFT} { pre { + !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" !type.isSubtype(of: Type<@{FungibleToken.Vault}>()): "Mixed asset types are not yet supported" self.typeRequiresOnboarding(type) == false: "NFT must first be onboarded" } @@ -357,6 +337,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider} ) { pre { + !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" !vault.isInstance(Type<@{NonFungibleToken.NFT}>()): "Mixed asset types are not yet supported" self.typeRequiresOnboarding(vault.getType()) == false: "FT must first be onboarded" } @@ -432,7 +413,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { /// Entrypoint to bridge ERC20 tokens from EVM to Cadence as FungibleToken Vaults /// - /// @param owner: The EVM address of the FT owner. Current ownership and successful transfer (via + /// @param owner: The EVM address of the FT owner. Current ownership and successful transfer (via /// `protectedTransferCall`) is validated before the bridge request is executed. /// @param calldata: Caller-provided approve() call, enabling contract COA to operate on FT in EVM contract /// @param amount: The amount of tokens to be bridged @@ -452,6 +433,7 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { protectedTransferCall: fun (): EVM.Result ): @{FungibleToken.Vault} { pre { + !FlowEVMBridgeConfig.isPaused(): "Bridge operations are currently paused" !type.isSubtype(of: Type<@{NonFungibleToken.Collection}>()): "Mixed asset types are not yet supported" !type.isInstance(Type<@FlowToken.Vault>()): "Must use the CadenceOwnedAccount interface to bridge $FLOW from EVM" self.typeRequiresOnboarding(type) == false: "NFT must first be onboarded" @@ -603,145 +585,34 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { /// @returns The EVMAddress of the deployed contract /// access(self) - fun deployEVMContract(forAssetType: Type): EVMOnboardingValues { - if forAssetType.isSubtype(of: Type<@{NonFungibleToken.NFT}>()) { - return self.deployERC721(forAssetType) - } else if forAssetType.isSubtype(of: Type<@{FungibleToken.Vault}>()) { - return self.deployERC20(forAssetType) - } - panic("Unsupported asset type: ".concat(forAssetType.identifier)) - } - - /// Deploys templated ERC721 contract supporting EVM-native asset bridging to Cadence - /// - /// @param forNFTType: The Cadence Type of the NFT - /// - /// @returns The EVMAddress of the deployed contract - /// - access(self) - fun deployERC721(_ forNFTType: Type): EVMOnboardingValues { - // Retrieve the Cadence type's defining contract name, address, & its identifier - var name = FlowEVMBridgeUtils.getContractName(fromType: forNFTType) - ?? panic("Could not contract name from type: ".concat(forNFTType.identifier)) - let identifier = forNFTType.identifier - let cadenceAddress = FlowEVMBridgeUtils.getContractAddress(fromType: forNFTType) - ?? panic("Could not derive contract address for token type: ".concat(identifier)) - // Assign a default symbol - var symbol = "BRDG" - // Borrow the ViewResolver to attempt to resolve the EVMBridgedMetadata view - let viewResolver = getAccount(cadenceAddress).contracts.borrow<&{ViewResolver}>(name: name)! - var contractURI = "" - // Try to resolve the EVMBridgedMetadata - let bridgedMetadata = viewResolver.resolveContractView( - resourceType: forNFTType, - viewType: Type() - ) as! CrossVMNFT.EVMBridgedMetadata? - // Default to project-defined URI if available - if bridgedMetadata != nil { - name = bridgedMetadata!.name - symbol = bridgedMetadata!.symbol - contractURI = bridgedMetadata!.uri.uri() - } else { - // Otherwise, serialize collection-level NFTCollectionDisplay - if let collectionDisplay = viewResolver.resolveContractView( - resourceType: forNFTType, - viewType: Type() - ) as! MetadataViews.NFTCollectionDisplay? { - name = collectionDisplay.name - let serializedDisplay = SerializeMetadata.serializeFromDisplays(nftDisplay: nil, collectionDisplay: collectionDisplay)! - contractURI = "data:application/json;utf8,{".concat(serializedDisplay).concat("}") - } - } - - // Call to the factory contract to deploy an ERC721 - let callResult: EVM.Result = FlowEVMBridgeUtils.call( - signature: "deployERC721(string,string,string,string,string)", - targetEVMAddress: FlowEVMBridgeUtils.bridgeFactoryEVMAddress, - args: [name, symbol, cadenceAddress.toString(), identifier, contractURI], - gasLimit: 15000000, - value: 0.0 - ) - assert(callResult.status == EVM.Status.successful, message: "Contract deployment failed") - let decodedResult: [AnyStruct] = EVM.decodeABI(types: [Type()], data: callResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") - - // Associate the deployed contract with the given type & return the deployed address - let erc721Address = decodedResult[0] as! EVM.EVMAddress - FlowEVMBridgeConfig.associateType(forNFTType, with: erc721Address) - return EVMOnboardingValues( - evmContractAddress: erc721Address, - name: name, - symbol: symbol, - decimals: nil - ) - } - - /// Deploys templated ERC20 contract supporting EVM-native asset bridging to Cadence - /// - /// @param forTokenType: The Cadence Type of the FungibleToken.Vault - /// - /// @returns The EVMAddress of the deployed contract - /// - access(self) - fun deployERC20(_ forTokenType: Type): EVMOnboardingValues { - // Retrieve the Cadence type's defining contract name, address, & its identifier - var name = FlowEVMBridgeUtils.getContractName(fromType: forTokenType) - ?? panic("Could not contract name from type: ".concat(forTokenType.identifier)) - let identifier = forTokenType.identifier - let cadenceAddress = FlowEVMBridgeUtils.getContractAddress(fromType: forTokenType) - ?? panic("Could not derive contract address for token type: ".concat(identifier)) - // Assign a default symbol - var symbol: String? = nil - // Borrow the ViewResolver to attempt to resolve the EVMBridgedMetadata view - let viewResolver = getAccount(cadenceAddress).contracts.borrow<&{ViewResolver}>(name: name)! - var contractURI = "" - // Try to resolve the EVMBridgedMetadata - let bridgedMetadata = viewResolver.resolveContractView( - resourceType: forTokenType, - viewType: Type() - ) as! CrossVMNFT.EVMBridgedMetadata? - let ftDisplay = viewResolver.resolveContractView( - resourceType: forTokenType, - viewType: Type() - ) as! FungibleTokenMetadataViews.FTDisplay? - // Default to project-defined bridged metadata if available - if bridgedMetadata != nil { - name = bridgedMetadata!.name - symbol = bridgedMetadata!.symbol - contractURI = bridgedMetadata!.uri.uri() - } else if ftDisplay != nil { - // Otherwise pull from FTDisplay - name = ftDisplay!.name - symbol = ftDisplay!.symbol - } - if contractURI.length == 0 && ftDisplay != nil { - let serializedDisplay = SerializeMetadata.serializeFTDisplay(ftDisplay!) - contractURI = "data:application/json;utf8,{".concat(serializedDisplay).concat("}") + fun deployEVMContract(forAssetType: Type): FlowEVMBridgeUtils.EVMOnboardingValues { + pre { + FlowEVMBridgeUtils.isValidFlowAsset(type: forAssetType): + "Asset type is not supported by the bridge" } + let isNFT = forAssetType.isSubtype(of: Type<@{NonFungibleToken.NFT}>()) - // Ensure symbol is assigned before proceeding - assert(symbol != nil, message: "Symbol must be assigned before deploying ERC20 contract") + let onboardingValues = FlowEVMBridgeUtils.getCadenceOnboardingValues(forAssetType: forAssetType) - // Call to the factory contract to deploy an ERC20 & validate result - let callResult: EVM.Result = FlowEVMBridgeUtils.call( - signature: "deployERC20(string,string,string,string,string)", - targetEVMAddress: FlowEVMBridgeUtils.bridgeFactoryEVMAddress, - args: [name, symbol!, cadenceAddress.toString(), identifier, contractURI], // TODO: Decide on and update symbol - gasLimit: 15000000, - value: 0.0 - ) - assert(callResult.status == EVM.Status.successful, message: "Contract deployment failed") - let decodedResult: [AnyStruct] = EVM.decodeABI(types: [Type()], data: callResult.data) - assert(decodedResult.length == 1, message: "Invalid response length") + let deployedContractAddress = FlowEVMBridgeUtils.mustDeployEVMContract( + name: onboardingValues.name, + symbol: onboardingValues.symbol, + cadenceAddress: onboardingValues.contractAddress, + flowIdentifier: onboardingValues.identifier, + contractURI: onboardingValues.contractURI, + isERC721: isNFT + ) // Associate the deployed contract with the given type & return the deployed address - let erc20Address = decodedResult[0] as! EVM.EVMAddress - FlowEVMBridgeConfig.associateType(forTokenType, with: erc20Address) - return EVMOnboardingValues( - evmContractAddress: erc20Address, - name: name, - symbol: symbol!, - decimals: FlowEVMBridgeConfig.defaultDecimals + FlowEVMBridgeConfig.associateType(forAssetType, with: deployedContractAddress) + return FlowEVMBridgeUtils.EVMOnboardingValues( + evmContractAddress: deployedContractAddress, + name: onboardingValues.name, + symbol: onboardingValues.symbol, + decimals: isNFT ? nil : FlowEVMBridgeConfig.defaultDecimals, + contractURI: nil, + cadenceContractName: FlowEVMBridgeUtils.getContractName(fromType: forAssetType)!, + isERC721: isNFT ) } @@ -752,43 +623,39 @@ contract FlowEVMBridge : IFlowEVMNFTBridge, IFlowEVMTokenBridge { /// access(self) fun deployDefiningContract(evmContractAddress: EVM.EVMAddress) { - // Deploy the Cadence contract defining the asset - // Treat as NFT if contract is ERC721, otherwise treat as FT - let name: String = FlowEVMBridgeUtils.getName(evmContractAddress: evmContractAddress) - let symbol: String = FlowEVMBridgeUtils.getSymbol(evmContractAddress: evmContractAddress) - let contractURI = FlowEVMBridgeUtils.getContractURI(evmContractAddress: evmContractAddress) - var decimals: UInt8 = FlowEVMBridgeConfig.defaultDecimals - - // Derive contract name - let isERC721: Bool = FlowEVMBridgeUtils.isERC721(evmContractAddress: evmContractAddress) - var cadenceContractName: String = "" - if isERC721 { - // Assert the contract is not mixed asset - let isERC20 = FlowEVMBridgeUtils.isERC20(evmContractAddress: evmContractAddress) - assert(!isERC20, message: "Contract is mixed asset and is not currently supported by the bridge") - // Derive the contract name from the ERC721 contract - cadenceContractName = FlowEVMBridgeUtils.deriveBridgedNFTContractName(from: evmContractAddress) - } else { - cadenceContractName = FlowEVMBridgeUtils.deriveBridgedTokenContractName(from: evmContractAddress) - decimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: evmContractAddress) - } + // Gather identifying information about the EVM contract + let evmOnboardingValues = FlowEVMBridgeUtils.getEVMOnboardingValues(evmContractAddress: evmContractAddress) // Get Cadence code from template & deploy to the bridge account let cadenceCode: [UInt8] = FlowEVMBridgeTemplates.getBridgedAssetContractCode( - cadenceContractName, - isERC721: isERC721 + evmOnboardingValues.cadenceContractName, + isERC721: evmOnboardingValues.isERC721 ) ?? panic("Problem retrieving code for Cadence-defining contract") - if isERC721 { - self.account.contracts.add(name: cadenceContractName, code: cadenceCode, name, symbol, evmContractAddress, contractURI) + if evmOnboardingValues.isERC721 { + self.account.contracts.add( + name: evmOnboardingValues.cadenceContractName, + code: cadenceCode, + evmOnboardingValues.name, + evmOnboardingValues.symbol, + evmContractAddress, + evmOnboardingValues.contractURI + ) } else { - self.account.contracts.add(name: cadenceContractName, code: cadenceCode, name, symbol, decimals, evmContractAddress, contractURI) + self.account.contracts.add( + name: evmOnboardingValues.cadenceContractName, + code: cadenceCode, + evmOnboardingValues.name, + evmOnboardingValues.symbol, + evmOnboardingValues.decimals!, + evmContractAddress, evmOnboardingValues.contractURI + ) } emit BridgeDefiningContractDeployed( - contractName: cadenceContractName, - assetName: name, - symbol: symbol, - isERC721: isERC721, + contractName: evmOnboardingValues.cadenceContractName, + assetName: evmOnboardingValues.name, + symbol: evmOnboardingValues.symbol, + isERC721: evmOnboardingValues.isERC721, evmContractAddress: EVMUtils.getEVMAddressAsHexString(address: evmContractAddress) ) } diff --git a/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc b/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc index 5248fd0a..32c2edd3 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeConfig.cdc @@ -15,6 +15,7 @@ contract FlowEVMBridgeConfig { *******************/ access(all) entitlement Fee + access(all) entitlement Pause /************* Fields @@ -29,6 +30,9 @@ contract FlowEVMBridgeConfig { /// Default ERC20.decimals() value access(all) let defaultDecimals: UInt8 + /// Flag enabling pausing of bridge operations + access(self) + var paused: Bool /// Mapping of Type to its associated EVMAddress as relevant to the bridge access(self) let typeToEVMAddress: {Type: EVM.EVMAddress} @@ -65,14 +69,25 @@ contract FlowEVMBridgeConfig { /// access(all) event BridgeFeeUpdated(old: UFix64, new: UFix64, isOnboarding: Bool) - // TODO + /// Emitted whenever a TokenHandler is configured + /// access(all) event HandlerConfigured(targetType: Type, targetEVMAddress: String?, isEnabled: Bool) + /// Emitted whenever the bridge is paused + /// + access(all) + event PauseStatusUpdated(paused: Bool) /************* Getters *************/ + /// Returns whether the bridge is paused + access(all) + view fun isPaused(): Bool { + return self.paused + } + /// Retrieves the EVMAddress associated with a given Type if it has been onboarded to the bridge /// access(all) @@ -220,6 +235,28 @@ contract FlowEVMBridgeConfig { FlowEVMBridgeConfig.baseFee = new } + /// Pauses the bridge, preventing all bridge operations + /// + access(Pause) + fun pauseBridge() { + if FlowEVMBridgeConfig.isPaused() { + return + } + FlowEVMBridgeConfig.paused = true + emit PauseStatusUpdated(paused: true) + } + + /// Unpauses the bridge, allowing bridge operations to resume + /// + access(Pause) + fun unpauseBridge() { + if !FlowEVMBridgeConfig.isPaused() { + return + } + FlowEVMBridgeConfig.paused = false + emit PauseStatusUpdated(paused: false) + } + /// Sets the target EVM contract address on the handler for a given Type, associating the Cadence type with the /// provided EVM address. If a TokenHandler does not exist for the given Type, the operation reverts. /// @@ -282,6 +319,7 @@ contract FlowEVMBridgeConfig { self.onboardFee = 0.0 self.baseFee = 0.0 self.defaultDecimals = 18 + self.paused = false // Although $FLOW does not have ERC20 address, we associate the the Vault with the EVM address from which // EVM transfers originate diff --git a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc index 52d83196..77c5ad2f 100644 --- a/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc +++ b/cadence/contracts/bridge/FlowEVMBridgeUtils.cdc @@ -1,6 +1,7 @@ import "NonFungibleToken" import "FungibleToken" import "MetadataViews" +import "FungibleTokenMetadataViews" import "ViewResolver" import "FlowToken" import "FlowStorageFees" @@ -8,8 +9,10 @@ import "FlowStorageFees" import "EVM" import "EVMUtils" +import "SerializeMetadata" import "FlowEVMBridgeConfig" -import "BridgePermissions" +import "CrossVMNFT" +import "IBridgePermissions" /// This contract serves as a source of utility methods leveraged by FlowEVMBridge contracts // @@ -26,6 +29,65 @@ contract FlowEVMBridgeUtils { access(self) let contractNamePrefixes: {Type: {String: String}} + /**************** + Constructs + *****************/ + + /// Struct used to preserve and pass around multiple values relating to Cadence asset onboarding + /// + access(all) struct CadenceOnboardingValues { + access(all) let contractAddress: Address + access(all) let name: String + access(all) let symbol: String + access(all) let identifier: String + access(all) let contractURI: String + + init( + contractAddress: Address, + name: String, + symbol: String, + identifier: String, + contractURI: String + ) { + self.contractAddress = contractAddress + self.name = name + self.symbol = symbol + self.identifier = identifier + self.contractURI = contractURI + } + } + + /// Struct used to preserve and pass around multiple values preventing the need to make multiple EVM calls + /// during EVM asset onboarding + /// + access(all) struct EVMOnboardingValues { + access(all) let evmContractAddress: EVM.EVMAddress + access(all) let name: String + access(all) let symbol: String + access(all) let decimals: UInt8? + access(all) let contractURI: String? + access(all) let cadenceContractName: String + access(all) let isERC721: Bool + + init( + evmContractAddress: EVM.EVMAddress, + name: String, + symbol: String, + decimals: UInt8?, + contractURI: String?, + cadenceContractName: String, + isERC721: Bool + ) { + self.evmContractAddress = evmContractAddress + self.name = name + self.symbol = symbol + self.decimals = decimals + self.contractURI = contractURI + self.cadenceContractName = cadenceContractName + self.isERC721 = isERC721 + } + } + /************************** Public Bridge Utils **************************/ @@ -45,9 +107,9 @@ contract FlowEVMBridgeUtils { return storageFee + FlowEVMBridgeConfig.baseFee } - /// Returns whether the given type is allowed to be bridged as defined by the BridgePermissions contract interface. - /// If the type's defining contract does not implement BridgePermissions, the method returns true as the bridge - /// operates permissionlessly by default. Otherwise, the result of {BridgePermissions}.allowsBridging() is returned + /// Returns whether the given type is allowed to be bridged as defined by the IBridgePermissions contract interface. + /// If the type's defining contract does not implement IBridgePermissions, the method returns true as the bridge + /// operates permissionlessly by default. Otherwise, the result of {IBridgePermissions}.allowsBridging() is returned /// /// @param type: The Type of the asset to check /// @@ -59,7 +121,7 @@ contract FlowEVMBridgeUtils { ?? panic("Could not construct contract address from type identifier: ".concat(type.identifier)) let contractName = self.getContractName(fromType: type) ?? panic("Could not construct contract name from type identifier: ".concat(type.identifier)) - if let bridgePermissions = getAccount(contractAddress).contracts.borrow<&{BridgePermissions}>(name: contractName) { + if let bridgePermissions = getAccount(contractAddress).contracts.borrow<&{IBridgePermissions}>(name: contractName) { return bridgePermissions.allowsBridging() } return true @@ -85,7 +147,7 @@ contract FlowEVMBridgeUtils { if callResult.status != EVM.Status.successful { return true } - // Contract is BridgePermissions - return the result + // Contract is IBridgePermissions - return the result let decodedResult = EVM.decodeABI(types: [Type()], data: callResult.data) as! [AnyStruct] return (decodedResult.length == 1 && decodedResult[0] as! Bool) == true ? true : false } @@ -221,6 +283,130 @@ contract FlowEVMBridgeUtils { return FlowEVMBridgeUtils.borrowCOA().address() } + /// Retrieves the relevant information for onboarding a Cadence asset to the bridge. This method is used to + /// retrieve the name, symbol, contract address, and contract URI for a given Cadence asset type. These values + /// are used to then deploy a corresponding EVM contract. If EVMBridgedMetadata is supported by the asset's + /// defining contract, the values are retrieved from that view. Otherwise, the values are derived from other + /// common metadata views. + /// + /// @param forAssetType: The Type of the asset to retrieve onboarding values for + /// + /// @return The CadenceOnboardingValues struct containing the asset's name, symbol, identifier, contract address, + /// and contract URI + /// + access(all) + fun getCadenceOnboardingValues(forAssetType: Type): CadenceOnboardingValues { + pre { + self.isValidFlowAsset(type: forAssetType): "This type is not a supported Flow asset type." + } + // If not an NFT, assumed to be fungible token. + let isNFT = forAssetType.isSubtype(of: Type<@{NonFungibleToken.NFT}>()) + + // Retrieve the Cadence type's defining contract name, address, & its identifier + var name = FlowEVMBridgeUtils.getContractName(fromType: forAssetType) + ?? panic("Could not contract name from type: ".concat(forAssetType.identifier)) + let identifier = forAssetType.identifier + let cadenceAddress = FlowEVMBridgeUtils.getContractAddress(fromType: forAssetType) + ?? panic("Could not derive contract address for token type: ".concat(identifier)) + // Initialize asset symbol which will be assigned later + // based on presence of asset-defined metadata + var symbol: String? = nil + // Borrow the ViewResolver to attempt to resolve the EVMBridgedMetadata view + let viewResolver = getAccount(cadenceAddress).contracts.borrow<&{ViewResolver}>(name: name)! + var contractURI = "" + + // Try to resolve the EVMBridgedMetadata + let bridgedMetadata = viewResolver.resolveContractView( + resourceType: forAssetType, + viewType: Type() + ) as! MetadataViews.EVMBridgedMetadata? + // Default to project-defined URI if available + if bridgedMetadata != nil { + name = bridgedMetadata!.name + symbol = bridgedMetadata!.symbol + contractURI = bridgedMetadata!.uri.uri() + } else { + if isNFT { + // Otherwise, serialize collection-level NFTCollectionDisplay + if let collectionDisplay = viewResolver.resolveContractView( + resourceType: forAssetType, + viewType: Type() + ) as! MetadataViews.NFTCollectionDisplay? { + name = collectionDisplay.name + let serializedDisplay = SerializeMetadata.serializeFromDisplays(nftDisplay: nil, collectionDisplay: collectionDisplay)! + contractURI = "data:application/json;utf8,{".concat(serializedDisplay).concat("}") + } + if symbol == nil { + symbol = SerializeMetadata.deriveSymbol(fromString: name) + } + } else { + let ftDisplay = viewResolver.resolveContractView( + resourceType: forAssetType, + viewType: Type() + ) as! FungibleTokenMetadataViews.FTDisplay? + if ftDisplay != nil { + name = ftDisplay!.name + symbol = ftDisplay!.symbol + } + if contractURI.length == 0 && ftDisplay != nil { + let serializedDisplay = SerializeMetadata.serializeFTDisplay(ftDisplay!) + contractURI = "data:application/json;utf8,{".concat(serializedDisplay).concat("}") + } + } + } + + return CadenceOnboardingValues( + contractAddress: cadenceAddress, + name: name, + symbol: symbol!, + identifier: identifier, + contractURI: contractURI + ) + } + + /// Retrieves identifying information about an EVM contract related to bridge onboarding. + /// + /// @param evmContractAddress: The EVM contract address to retrieve onboarding values for + /// + /// @return The EVMOnboardingValues struct containing the asset's name, symbol, decimals, contractURI, and + /// Cadence contract name as well as whether the asset is an ERC721 + /// + access(all) + fun getEVMOnboardingValues(evmContractAddress: EVM.EVMAddress): EVMOnboardingValues { + // Retrieve the EVM contract's name, symbol, and contractURI + let name: String = self.getName(evmContractAddress: evmContractAddress) + let symbol: String = self.getSymbol(evmContractAddress: evmContractAddress) + let contractURI = self.getContractURI(evmContractAddress: evmContractAddress) + // Default to 18 decimals for ERC20s + var decimals: UInt8 = FlowEVMBridgeConfig.defaultDecimals + + // Derive Cadence contract name + let isERC721: Bool = self.isERC721(evmContractAddress: evmContractAddress) + var cadenceContractName: String = "" + if isERC721 { + // Assert the contract is not mixed asset + let isERC20 = self.isERC20(evmContractAddress: evmContractAddress) + assert(!isERC20, message: "Contract is mixed asset and is not currently supported by the bridge") + // Derive the contract name from the ERC721 contract + cadenceContractName = self.deriveBridgedNFTContractName(from: evmContractAddress) + } else { + // Otherwise, treat as ERC20. Upstream bridge calls would have confirmed the contract is either ERC20 or + // ERC721 + cadenceContractName = self.deriveBridgedTokenContractName(from: evmContractAddress) + decimals = self.getTokenDecimals(evmContractAddress: evmContractAddress) + } + + return EVMOnboardingValues( + evmContractAddress: evmContractAddress, + name: name, + symbol: symbol, + decimals: decimals, + contractURI: contractURI, + cadenceContractName: cadenceContractName, + isERC721: isERC721 + ) + } + /************************ EVM Call Wrappers ************************/ @@ -744,7 +930,7 @@ contract FlowEVMBridgeUtils { fun depositFee(_ feeProvider: auth(FungibleToken.Withdraw) &{FungibleToken.Provider}, feeAmount: UFix64) { let vault = self.account.storage.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Could not borrow FlowToken.Vault reference") - + let feeVault <-feeProvider.withdraw(amount: feeAmount) as! @FlowToken.Vault assert(feeVault.balance == feeAmount, message: "Fee provider did not return the requested fee") @@ -787,7 +973,7 @@ contract FlowEVMBridgeUtils { access(account) fun mustSafeTransferERC721(erc721Address: EVM.EVMAddress, to: EVM.EVMAddress, id: UInt256) { let bridgeCOAAddress = self.getBridgeCOAEVMAddress() - + let bridgePreStatus = self.isOwner(ofNFT: id, owner: bridgeCOAAddress, evmContractAddress: erc721Address) let toPreStatus = self.isOwner(ofNFT: id, owner: to, evmContractAddress: erc721Address) assert(bridgePreStatus, message: "Bridge COA does not own ERC721 requesting to be transferred") @@ -817,7 +1003,7 @@ contract FlowEVMBridgeUtils { access(account) fun mustSafeMintERC721(erc721Address: EVM.EVMAddress, to: EVM.EVMAddress, id: UInt256, uri: String) { let bridgeCOAAddress = self.getBridgeCOAEVMAddress() - + let mintResult: EVM.Result = FlowEVMBridgeUtils.call( signature: "safeMint(address,uint256,string)", targetEVMAddress: erc721Address, @@ -838,7 +1024,7 @@ contract FlowEVMBridgeUtils { access(account) fun mustUpdateTokenURI(erc721Address: EVM.EVMAddress, id: UInt256, uri: String) { let bridgeCOAAddress = self.getBridgeCOAEVMAddress() - + let updateResult: EVM.Result = FlowEVMBridgeUtils.call( signature: "updateTokenURI(uint256,string)", targetEVMAddress: erc721Address, @@ -970,12 +1156,37 @@ contract FlowEVMBridgeUtils { owner: FlowEVMBridgeUtils.getBridgeCOAEVMAddress(), evmContractAddress: erc20Address ) - + // Confirm the transfer of the expected was successful in both sending owner and recipient escrow assert(ownerPostBalance == ownerPreBalance - amount, message: "Transfer to owner failed") assert(bridgePostBalance == bridgePreBalance + amount, message: "Transfer to bridge escrow failed") } + /// Calls to the bridge factory to deploy an ERC721/ERC20 contract and returns the deployed contract address + /// + access(account) + fun mustDeployEVMContract( + name: String, + symbol: String, + cadenceAddress: Address, + flowIdentifier: String, + contractURI: String, + isERC721: Bool + ): EVM.EVMAddress { + let signature = isERC721 ? "deployERC721(string,string,string,string,string)" : "deployERC20(string,string,string,string,string)" + let deployResult: EVM.Result = FlowEVMBridgeUtils.call( + signature: signature, + targetEVMAddress: FlowEVMBridgeUtils.bridgeFactoryEVMAddress, + args: [name, symbol, cadenceAddress.toString(), flowIdentifier, contractURI], + gasLimit: 15000000, + value: 0.0 + ) + assert(deployResult.status == EVM.Status.successful, message: "EVM Token contract deployment failed") + let decodedResult: [AnyStruct] = EVM.decodeABI(types: [Type()], data: deployResult.data) + assert(decodedResult.length == 1, message: "Invalid response length") + return decodedResult[0] as! EVM.EVMAddress + } + init(bridgeFactoryBytecodeHex: String) { self.delimiter = "_" self.contractNamePrefixes = { diff --git a/cadence/contracts/bridge/CrossVMNFT.cdc b/cadence/contracts/bridge/interfaces/CrossVMNFT.cdc similarity index 54% rename from cadence/contracts/bridge/CrossVMNFT.cdc rename to cadence/contracts/bridge/interfaces/CrossVMNFT.cdc index c153ed19..0a33d06e 100644 --- a/cadence/contracts/bridge/CrossVMNFT.cdc +++ b/cadence/contracts/bridge/interfaces/CrossVMNFT.cdc @@ -8,45 +8,6 @@ import "EVM" /// access(all) contract CrossVMNFT { - /// A struct to represent a general case URI, used to represent the URI of the NFT where the type of URI is not - /// able to be determined (i.e. HTTP, IPFS, etc.) - /// - access(all) struct URI : MetadataViews.File { - /// The base URI prefix, if any. Not needed for all URIs, but helpful for some use cases - /// For example, updating a whole NFT collection's image host easily - access(all) let baseURI: String? - /// The URI value - /// NOTE: this is set on init as a concatenation of the baseURI and the value if baseURI != nil - access(self) let value: String - - access(all) view fun uri(): String { - return self.value - } - - init(baseURI: String?, value: String) { - self.baseURI = baseURI - self.value = baseURI != nil ? baseURI!.concat(value) : value - } - } - - /// Proof of concept metadata to represent the ERC721 values of the NFT - /// - access(all) struct EVMBridgedMetadata { - /// The name of the NFT - access(all) let name: String - /// The symbol of the NFT - access(all) let symbol: String - /// The URI of the asset - this can either be contract-level or token-level URI depending on where the metadata - /// is requested. See the ViewResolver contract interface to discover how contract & resource-level metadata - /// requests are handled. - access(all) let uri: {MetadataViews.File} - - init(name: String, symbol: String, uri: {MetadataViews.File}) { - self.name = name - self.symbol = symbol - self.uri = uri - } - } /// A simple interface for an NFT that is bridged to the EVM. This may be necessary in some cases as there is /// discrepancy between Flow NFT standard IDs (UInt64) and EVM NFT standard IDs (UInt256). Discrepancies on IDs diff --git a/cadence/contracts/bridge/CrossVMToken.cdc b/cadence/contracts/bridge/interfaces/CrossVMToken.cdc similarity index 100% rename from cadence/contracts/bridge/CrossVMToken.cdc rename to cadence/contracts/bridge/interfaces/CrossVMToken.cdc diff --git a/cadence/contracts/bridge/FlowEVMBridgeHandlerInterfaces.cdc b/cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc similarity index 100% rename from cadence/contracts/bridge/FlowEVMBridgeHandlerInterfaces.cdc rename to cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc diff --git a/cadence/contracts/bridge/BridgePermissions.cdc b/cadence/contracts/bridge/interfaces/IBridgePermissions.cdc similarity index 89% rename from cadence/contracts/bridge/BridgePermissions.cdc rename to cadence/contracts/bridge/interfaces/IBridgePermissions.cdc index e79f701b..9911f60b 100644 --- a/cadence/contracts/bridge/BridgePermissions.cdc +++ b/cadence/contracts/bridge/interfaces/IBridgePermissions.cdc @@ -6,12 +6,14 @@ /// e.g. assets are reclaimed after a certain period of time, NFTs share IDs, etc. /// access(all) -contract interface BridgePermissions { +contract interface IBridgePermissions { /// Contract-level method enabling implementing contracts to identify whether they allow bridging for their /// project's assets. Implementers may consider adding a hook which would later enable an update to this value /// should either the project be updated or the bridge be updated to handle the asset's non-standard logic which /// would otherwise prevent them from supporting VM bridging at the outset. /// access(all) - view fun allowsBridging(): Bool + view fun allowsBridging(): Bool { + return false + } } diff --git a/cadence/contracts/bridge/ICrossVM.cdc b/cadence/contracts/bridge/interfaces/ICrossVM.cdc similarity index 100% rename from cadence/contracts/bridge/ICrossVM.cdc rename to cadence/contracts/bridge/interfaces/ICrossVM.cdc diff --git a/cadence/contracts/bridge/IEVMBridgeNFTMinter.cdc b/cadence/contracts/bridge/interfaces/IEVMBridgeNFTMinter.cdc similarity index 100% rename from cadence/contracts/bridge/IEVMBridgeNFTMinter.cdc rename to cadence/contracts/bridge/interfaces/IEVMBridgeNFTMinter.cdc diff --git a/cadence/contracts/bridge/IEVMBridgeTokenMinter.cdc b/cadence/contracts/bridge/interfaces/IEVMBridgeTokenMinter.cdc similarity index 100% rename from cadence/contracts/bridge/IEVMBridgeTokenMinter.cdc rename to cadence/contracts/bridge/interfaces/IEVMBridgeTokenMinter.cdc diff --git a/cadence/contracts/bridge/IFlowEVMNFTBridge.cdc b/cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc similarity index 100% rename from cadence/contracts/bridge/IFlowEVMNFTBridge.cdc rename to cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc diff --git a/cadence/contracts/bridge/IFlowEVMTokenBridge.cdc b/cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc similarity index 100% rename from cadence/contracts/bridge/IFlowEVMTokenBridge.cdc rename to cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc diff --git a/cadence/contracts/standards/MetadataViews.cdc b/cadence/contracts/standards/MetadataViews.cdc index f8695bc0..a35bb6a5 100644 --- a/cadence/contracts/standards/MetadataViews.cdc +++ b/cadence/contracts/standards/MetadataViews.cdc @@ -703,4 +703,44 @@ access(all) contract MetadataViews { } return nil } + + /// A struct to represent a general case URI, used to represent the URI of the NFT where the type of URI is not + /// able to be determined (i.e. HTTP, IPFS, etc.) + /// + access(all) struct URI : File { + /// The base URI prefix, if any. Not needed for all URIs, but helpful for some use cases + /// For example, updating a whole NFT collection's image host easily + access(all) let baseURI: String? + /// The URI value + /// NOTE: this is set on init as a concatenation of the baseURI and the value if baseURI != nil + access(self) let value: String + + access(all) view fun uri(): String { + return self.value + } + + init(baseURI: String?, value: String) { + self.baseURI = baseURI + self.value = baseURI != nil ? baseURI!.concat(value) : value + } + } + + /// Proof of concept metadata to represent the ERC721 values of the NFT + /// + access(all) struct EVMBridgedMetadata { + /// The name of the NFT + access(all) let name: String + /// The symbol of the NFT + access(all) let symbol: String + /// The URI of the asset - this can either be contract-level or token-level URI depending on where the metadata + /// is requested. See the ViewResolver contract interface to discover how contract & resource-level metadata + /// requests are handled. + access(all) let uri: {MetadataViews.File} + + init(name: String, symbol: String, uri: {MetadataViews.File}) { + self.name = name + self.symbol = symbol + self.uri = uri + } + } } diff --git a/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc b/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc index 09f8a36d..0cfc10d6 100644 --- a/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc +++ b/cadence/contracts/templates/emulator/EVMBridgedNFTTemplate.cdc @@ -66,7 +66,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi /// Returns the metadata view types supported by this NFT access(all) view fun getViews(): [Type] { return [ - Type(), + Type(), Type(), Type(), Type() @@ -90,11 +90,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi switch view { // We don't know what kind of file the URI represents (IPFS v HTTP), so we can't resolve Display view // with the URI as thumbnail - we may a new standard view for EVM NFTs - this is interim - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.getName(), symbol: self.getSymbol(), - uri: CrossVMNFT.URI(baseURI: nil, value: self.tokenURI()) + uri: MetadataViewsa.URI(baseURI: nil, value: self.tokenURI()) ) case Type(): return MetadataViews.Serial( @@ -281,7 +281,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi return [ Type(), Type(), - Type() + Type() ] } @@ -319,11 +319,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi bannerImage: media, socials: {} ) - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.name, symbol: self.symbol, - uri: self.contractURI != nil ? CrossVMNFT.URI(baseURI: nil, value: self.contractURI!) : CrossVMNFT.URI(baseURI: nil, value: "") + uri: self.contractURI != nil ? MetadataViewsa.URI(baseURI: nil, value: self.contractURI!) : MetadataViewsa.URI(baseURI: nil, value: "") ) } return nil diff --git a/cadence/contracts/templates/emulator/EVMBridgedTokenTemplate.cdc b/cadence/contracts/templates/emulator/EVMBridgedTokenTemplate.cdc index b28503ee..3df6d030 100644 --- a/cadence/contracts/templates/emulator/EVMBridgedTokenTemplate.cdc +++ b/cadence/contracts/templates/emulator/EVMBridgedTokenTemplate.cdc @@ -180,7 +180,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, Fungib Type(), Type(), Type(), - Type() + Type() ] } @@ -229,11 +229,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, Fungib return FungibleTokenMetadataViews.TotalSupply( totalSupply: self.totalSupply ) - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.name, symbol: self.symbol, - uri: self.contractURI != nil ? CrossVMNFT.URI(baseURI: nil, value: self.contractURI!) : CrossVMNFT.URI(baseURI: nil, value: "") + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") ) } return nil diff --git a/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc b/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc index b1c2c75e..1fed2c5e 100644 --- a/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc +++ b/cadence/contracts/templates/previewnet/EVMBridgedNFTTemplate.cdc @@ -66,7 +66,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi /// Returns the metadata view types supported by this NFT access(all) view fun getViews(): [Type] { return [ - Type(), + Type(), Type(), Type(), Type() @@ -90,11 +90,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi switch view { // We don't know what kind of file the URI represents (IPFS v HTTP), so we can't resolve Display view // with the URI as thumbnail - we may a new standard view for EVM NFTs - this is interim - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.getName(), symbol: self.getSymbol(), - uri: CrossVMNFT.URI(baseURI: nil, value: self.tokenURI()) + uri: MetadataViews.URI(baseURI: nil, value: self.tokenURI()) ) case Type(): return MetadataViews.Serial( @@ -281,7 +281,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi return [ Type(), Type(), - Type() + Type() ] } @@ -319,11 +319,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi bannerImage: media, socials: {} ) - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.name, symbol: self.symbol, - uri: self.contractURI != nil ? CrossVMNFT.URI(baseURI: nil, value: self.contractURI!) : CrossVMNFT.URI(baseURI: nil, value: "") + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") ) } return nil diff --git a/cadence/contracts/templates/previewnet/EVMBridgedTokenTemplate.cdc b/cadence/contracts/templates/previewnet/EVMBridgedTokenTemplate.cdc index 47ae7bff..770436c1 100644 --- a/cadence/contracts/templates/previewnet/EVMBridgedTokenTemplate.cdc +++ b/cadence/contracts/templates/previewnet/EVMBridgedTokenTemplate.cdc @@ -180,7 +180,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, Fungib Type(), Type(), Type(), - Type() + Type() ] } @@ -229,11 +229,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, Fungib return FungibleTokenMetadataViews.TotalSupply( totalSupply: self.totalSupply ) - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.name, symbol: self.symbol, - uri: self.contractURI != nil ? CrossVMNFT.URI(baseURI: nil, value: self.contractURI!) : CrossVMNFT.URI(baseURI: nil, value: "") + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") ) } return nil diff --git a/cadence/contracts/templates/testing/EVMBridgedNFTTemplate.cdc b/cadence/contracts/templates/testing/EVMBridgedNFTTemplate.cdc index 86288041..0ca732cf 100644 --- a/cadence/contracts/templates/testing/EVMBridgedNFTTemplate.cdc +++ b/cadence/contracts/templates/testing/EVMBridgedNFTTemplate.cdc @@ -66,7 +66,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi /// Returns the metadata view types supported by this NFT access(all) view fun getViews(): [Type] { return [ - Type(), + Type(), Type(), Type(), Type() @@ -90,11 +90,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi switch view { // We don't know what kind of file the URI represents (IPFS v HTTP), so we can't resolve Display view // with the URI as thumbnail - we may a new standard view for EVM NFTs - this is interim - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.getName(), symbol: self.getSymbol(), - uri: CrossVMNFT.URI(baseURI: nil, value: self.tokenURI()) + uri: MetadataViews.URI(baseURI: nil, value: self.tokenURI()) ) case Type(): return MetadataViews.Serial( @@ -281,7 +281,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi return [ Type(), Type(), - Type() + Type() ] } @@ -319,11 +319,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeNFTMinter, NonFungi bannerImage: media, socials: {} ) - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.name, symbol: self.symbol, - uri: self.contractURI != nil ? CrossVMNFT.URI(baseURI: nil, value: self.contractURI!) : CrossVMNFT.URI(baseURI: nil, value: "") + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") ) } return nil diff --git a/cadence/contracts/templates/testing/EVMBridgedTokenTemplate.cdc b/cadence/contracts/templates/testing/EVMBridgedTokenTemplate.cdc index b69ab766..b7b7575d 100644 --- a/cadence/contracts/templates/testing/EVMBridgedTokenTemplate.cdc +++ b/cadence/contracts/templates/testing/EVMBridgedTokenTemplate.cdc @@ -180,7 +180,7 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, Fungib Type(), Type(), Type(), - Type() + Type() ] } @@ -229,11 +229,11 @@ access(all) contract {{CONTRACT_NAME}} : ICrossVM, IEVMBridgeTokenMinter, Fungib return FungibleTokenMetadataViews.TotalSupply( totalSupply: self.totalSupply ) - case Type(): - return CrossVMNFT.EVMBridgedMetadata( + case Type(): + return MetadataViews.EVMBridgedMetadata( name: self.name, symbol: self.symbol, - uri: self.contractURI != nil ? CrossVMNFT.URI(baseURI: nil, value: self.contractURI!) : CrossVMNFT.URI(baseURI: nil, value: "") + uri: self.contractURI != nil ? MetadataViews.URI(baseURI: nil, value: self.contractURI!) : MetadataViews.URI(baseURI: nil, value: "") ) } return nil diff --git a/cadence/contracts/utils/SerializeMetadata.cdc b/cadence/contracts/utils/SerializeMetadata.cdc index d9bee9f7..4cdba1de 100644 --- a/cadence/contracts/utils/SerializeMetadata.cdc +++ b/cadence/contracts/utils/SerializeMetadata.cdc @@ -161,4 +161,43 @@ access(all) contract SerializeMetadata { .concat(externalLink).concat(Serialize.tryToJSONString(ftDisplay.externalURL.url)!) .concat("}") } + + /// Derives a symbol for use as an ERC20 or ERC721 symbol from a given string, presumably a Cadence contract name. + /// Derivation is a process of slicing the first 4 characters of the string and converting them to uppercase. + /// + /// @param fromString: The string from which to derive a symbol + /// + /// @returns: A derived symbol for use as an ERC20 or ERC721 symbol based on the provided string, presumably a + /// Cadence contract name + /// + access(all) view fun deriveSymbol(fromString: String): String { + let defaultLen = 4 + let len = fromString.length < defaultLen ? fromString.length : defaultLen + return self.toUpper(fromString, upTo: len) + } + + /// Returns the uppercase alphanumeric version of a given string. + /// + access(self) view fun toUpper(_ str: String, upTo: Int?): String { + let len = upTo ?? str.length + var upper: String = "" + for char in str { + if upper.length == len { + break + } + let bytes = char.utf8 + if bytes.length != 1 { + continue + } + if bytes[0] >= 97 && bytes[0] <= 122 { + let upperChar = String.fromUTF8([bytes[0] - UInt8(32)])! + upper = upper.concat(upperChar) + } else if bytes[0] >= 65 && bytes[0] <= 90 { + upper = upper.concat(char.toString()) + } else { + continue + } + } + return upper + } } diff --git a/cadence/scripts/bridge/is_paused.cdc b/cadence/scripts/bridge/is_paused.cdc new file mode 100644 index 00000000..21f65ebb --- /dev/null +++ b/cadence/scripts/bridge/is_paused.cdc @@ -0,0 +1,6 @@ +import "FlowEVMBridgeConfig" + +access(all) +fun main(): Bool { + return FlowEVMBridgeConfig.isPaused() +} diff --git a/cadence/contracts/test/EVMDeployer.cdc b/cadence/tests/contracts/EVMDeployer.cdc similarity index 100% rename from cadence/contracts/test/EVMDeployer.cdc rename to cadence/tests/contracts/EVMDeployer.cdc diff --git a/cadence/tests/flow_evm_bridge_handler_tests.cdc b/cadence/tests/flow_evm_bridge_handler_tests.cdc index 4006b99b..abf648b7 100644 --- a/cadence/tests/flow_evm_bridge_handler_tests.cdc +++ b/cadence/tests/flow_evm_bridge_handler_tests.cdc @@ -72,9 +72,17 @@ fun setup() { ) Test.expect(err, Test.beNil()) + // Update MetadataViews contract with proposed URI & EVMBridgedMetadata view COA integration + // TODO: Remove once MetadataViews contract is updated in CLI's core contracts + var updateResult = executeTransaction( + "./transactions/update_contract.cdc", + ["MetadataViews", getMetadataViewsUpdateCode()], + serviceAccount + ) // Update EVM contract with proposed bridge-supporting COA integration - let updateResult = executeTransaction( - "../transactions/test/update_contract.cdc", + // TODO: Remove once EVM contract is updated in CLI's core contracts + updateResult = executeTransaction( + "./transactions/update_contract.cdc", ["EVM", getEVMUpdateCode()], serviceAccount ) @@ -85,32 +93,32 @@ fun setup() { createCOA(signer: bridgeAccount, fundingAmount: 1_000.0) err = Test.deployContract( - name: "BridgePermissions", - path: "../contracts/bridge/BridgePermissions.cdc", + name: "IBridgePermissions", + path: "../contracts/bridge/interfaces/IBridgePermissions.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "ICrossVM", - path: "../contracts/bridge/ICrossVM.cdc", + path: "../contracts/bridge/interfaces/ICrossVM.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "CrossVMNFT", - path: "../contracts/bridge/CrossVMNFT.cdc", + path: "../contracts/bridge/interfaces/CrossVMNFT.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "CrossVMToken", - path: "../contracts/bridge/CrossVMToken.cdc", + path: "../contracts/bridge/interfaces/CrossVMToken.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "FlowEVMBridgeHandlerInterfaces", - path: "../contracts/bridge/FlowEVMBridgeHandlerInterfaces.cdc", + path: "../contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc", arguments: [] ) Test.expect(err, Test.beNil()) @@ -161,25 +169,25 @@ fun setup() { err = Test.deployContract( name: "IEVMBridgeNFTMinter", - path: "../contracts/bridge/IEVMBridgeNFTMinter.cdc", + path: "../contracts/bridge/interfaces/IEVMBridgeNFTMinter.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "IEVMBridgeTokenMinter", - path: "../contracts/bridge/IEVMBridgeTokenMinter.cdc", + path: "../contracts/bridge/interfaces/IEVMBridgeTokenMinter.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "IFlowEVMNFTBridge", - path: "../contracts/bridge/IFlowEVMNFTBridge.cdc", + path: "../contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "IFlowEVMTokenBridge", - path: "../contracts/bridge/IFlowEVMTokenBridge.cdc", + path: "../contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc", arguments: [] ) Test.expect(err, Test.beNil()) @@ -212,7 +220,7 @@ fun setup() { // once `evm` events Types are available err = Test.deployContract( name: "EVMDeployer", - path: "../contracts/test/EVMDeployer.cdc", + path: "./contracts/EVMDeployer.cdc", arguments: [] ) Test.expect(err, Test.beNil()) @@ -338,7 +346,7 @@ fun testEnableTokenHandlerFails() { access(all) fun testDeployERC20Succeeds() { let erc20DeployResult = executeTransaction( - "../transactions/test/deploy_using_evm_deployer.cdc", + "./transactions/deploy_using_evm_deployer.cdc", ["erc20", getCompiledERC20Bytecode(), 0 as UInt], exampleERCAccount ) diff --git a/cadence/tests/flow_evm_bridge_tests.cdc b/cadence/tests/flow_evm_bridge_tests.cdc index 0d2b6763..252cf3f4 100644 --- a/cadence/tests/flow_evm_bridge_tests.cdc +++ b/cadence/tests/flow_evm_bridge_tests.cdc @@ -83,9 +83,17 @@ fun setup() { ) Test.expect(err, Test.beNil()) + // Update MetadataViews contract with proposed URI & EVMBridgedMetadata view COA integration + // TODO: Remove once MetadataViews contract is updated in CLI's core contracts + var updateResult = executeTransaction( + "./transactions/update_contract.cdc", + ["MetadataViews", getMetadataViewsUpdateCode()], + serviceAccount + ) // Update EVM contract with proposed bridge-supporting COA integration - let updateResult = executeTransaction( - "../transactions/test/update_contract.cdc", + // TODO: Remove once EVM contract is updated in CLI's core contracts + updateResult = executeTransaction( + "./transactions/update_contract.cdc", ["EVM", getEVMUpdateCode()], serviceAccount ) @@ -96,32 +104,32 @@ fun setup() { createCOA(signer: bridgeAccount, fundingAmount: 1_000.0) err = Test.deployContract( - name: "BridgePermissions", - path: "../contracts/bridge/BridgePermissions.cdc", + name: "IBridgePermissions", + path: "../contracts/bridge/interfaces/IBridgePermissions.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "ICrossVM", - path: "../contracts/bridge/ICrossVM.cdc", + path: "../contracts/bridge/interfaces/ICrossVM.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "CrossVMNFT", - path: "../contracts/bridge/CrossVMNFT.cdc", + path: "../contracts/bridge/interfaces/CrossVMNFT.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "CrossVMToken", - path: "../contracts/bridge/CrossVMToken.cdc", + path: "../contracts/bridge/interfaces/CrossVMToken.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "FlowEVMBridgeHandlerInterfaces", - path: "../contracts/bridge/FlowEVMBridgeHandlerInterfaces.cdc", + path: "../contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc", arguments: [] ) Test.expect(err, Test.beNil()) @@ -172,25 +180,25 @@ fun setup() { err = Test.deployContract( name: "IEVMBridgeNFTMinter", - path: "../contracts/bridge/IEVMBridgeNFTMinter.cdc", + path: "../contracts/bridge/interfaces/IEVMBridgeNFTMinter.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "IEVMBridgeTokenMinter", - path: "../contracts/bridge/IEVMBridgeTokenMinter.cdc", + path: "../contracts/bridge/interfaces/IEVMBridgeTokenMinter.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "IFlowEVMNFTBridge", - path: "../contracts/bridge/IFlowEVMNFTBridge.cdc", + path: "../contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc", arguments: [] ) Test.expect(err, Test.beNil()) err = Test.deployContract( name: "IFlowEVMTokenBridge", - path: "../contracts/bridge/IFlowEVMTokenBridge.cdc", + path: "../contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc", arguments: [] ) Test.expect(err, Test.beNil()) @@ -223,18 +231,18 @@ fun setup() { // once `evm` events Types are available err = Test.deployContract( name: "EVMDeployer", - path: "../contracts/test/EVMDeployer.cdc", + path: "./contracts/EVMDeployer.cdc", arguments: [] ) Test.expect(err, Test.beNil()) let erc721DeployResult = executeTransaction( - "../transactions/test/deploy_using_evm_deployer.cdc", + "./transactions/deploy_using_evm_deployer.cdc", ["erc721", getCompiledERC721Bytecode(), 0 as UInt], exampleERCAccount ) Test.expect(erc721DeployResult, Test.beSucceeded()) let erc20DeployResult = executeTransaction( - "../transactions/test/deploy_using_evm_deployer.cdc", + "./transactions/deploy_using_evm_deployer.cdc", ["erc20", getCompiledERC20Bytecode(), 0 as UInt], exampleERCAccount ) @@ -701,6 +709,53 @@ fun testBatchOnboardByEVMAddressSucceeds() { /* --- BRIDGING NFTS - Test bridging both Cadence- & EVM-native NFTs --- */ +access(all) +fun testPauseBridgeSucceeds() { + // Pause the bridge + let pauseResult = executeTransaction( + "../transactions/bridge/admin/pause/update_bridge_pause_status.cdc", + [true], + bridgeAccount + ) + Test.expect(pauseResult, Test.beSucceeded()) + var isPausedResult = executeScript( + "../scripts/bridge/is_paused.cdc", + [] + ) + Test.expect(isPausedResult, Test.beSucceeded()) + Test.assertEqual(true, isPausedResult.returnValue as! Bool? ?? panic("Problem getting pause status")) + + var aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: "cadenceExampleNFTCollection") + Test.assertEqual(1, aliceOwnedIDs.length) + + var aliceCOAAddressHex = getCOAAddressHex(atFlowAddress: alice.address) + + // Execute bridge to EVM - should fail after pausing + bridgeNFTToEVM( + signer: alice, + contractAddr: exampleNFTAccount.address, + contractName: "ExampleNFT", + nftID: aliceOwnedIDs[0], + bridgeAccountAddr: bridgeAccount.address, + beFailed: true + ) + + // Unpause bridging + let unpauseResult = executeTransaction( + "../transactions/bridge/admin/pause/update_bridge_pause_status.cdc", + [false], + bridgeAccount + ) + Test.expect(unpauseResult, Test.beSucceeded()) + + isPausedResult = executeScript( + "../scripts/bridge/is_paused.cdc", + [] + ) + Test.expect(isPausedResult, Test.beSucceeded()) + Test.assertEqual(false, isPausedResult.returnValue as! Bool? ?? panic("Problem getting pause status")) +} + access(all) fun testBridgeCadenceNativeNFTToEVMSucceeds() { var aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: "cadenceExampleNFTCollection") @@ -714,7 +769,8 @@ fun testBridgeCadenceNativeNFTToEVMSucceeds() { contractAddr: exampleNFTAccount.address, contractName: "ExampleNFT", nftID: aliceOwnedIDs[0], - bridgeAccountAddr: bridgeAccount.address + bridgeAccountAddr: bridgeAccount.address, + beFailed: false ) let associatedEVMAddressHex = getAssociatedEVMAddressHex(with: exampleNFTIdentifier) @@ -750,7 +806,8 @@ fun testBridgeCadenceNativeNFTFromEVMSucceeds() { contractAddr: exampleNFTAccount.address, contractName: "ExampleNFT", erc721ID: UInt256(mintedNFTID), - bridgeAccountAddr: bridgeAccount.address + bridgeAccountAddr: bridgeAccount.address, + beFailed: false ) // Assert ownership of the bridged NFT in EVM has transferred @@ -777,7 +834,8 @@ fun testBridgeEVMNativeNFTFromEVMSucceeds() { contractAddr: bridgeAccount.address, contractName: derivedERC721ContractName, erc721ID: erc721ID, - bridgeAccountAddr: bridgeAccount.address + bridgeAccountAddr: bridgeAccount.address, + beFailed: false ) let aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: bridgedCollectionPathIdentifier) @@ -809,7 +867,8 @@ fun testBridgeEVMNativeNFTToEVMSucceeds() { contractAddr: bridgeAccount.address, contractName: derivedERC721ContractName, nftID: aliceOwnedIDs[0], - bridgeAccountAddr: bridgeAccount.address + bridgeAccountAddr: bridgeAccount.address, + beFailed: false ) aliceOwnedIDs = getIDs(ownerAddr: alice.address, storagePathIdentifier: bridgedCollectionPathIdentifier) diff --git a/cadence/scripts/test/get_block_height.cdc b/cadence/tests/scripts/get_block_height.cdc similarity index 100% rename from cadence/scripts/test/get_block_height.cdc rename to cadence/tests/scripts/get_block_height.cdc diff --git a/cadence/scripts/test/get_deployed_address_string_from_deployer.cdc b/cadence/tests/scripts/get_deployed_address_string_from_deployer.cdc similarity index 100% rename from cadence/scripts/test/get_deployed_address_string_from_deployer.cdc rename to cadence/tests/scripts/get_deployed_address_string_from_deployer.cdc diff --git a/cadence/scripts/test/get_deployed_contract_address_string.cdc b/cadence/tests/scripts/get_deployed_contract_address_string.cdc similarity index 100% rename from cadence/scripts/test/get_deployed_contract_address_string.cdc rename to cadence/tests/scripts/get_deployed_contract_address_string.cdc diff --git a/cadence/scripts/test/is_bridge_router_configured.cdc b/cadence/tests/scripts/is_bridge_router_configured.cdc similarity index 100% rename from cadence/scripts/test/is_bridge_router_configured.cdc rename to cadence/tests/scripts/is_bridge_router_configured.cdc diff --git a/cadence/tests/serialize_metadata_tests.cdc b/cadence/tests/serialize_metadata_tests.cdc index 33340339..26911359 100644 --- a/cadence/tests/serialize_metadata_tests.cdc +++ b/cadence/tests/serialize_metadata_tests.cdc @@ -50,7 +50,7 @@ fun testSerializeNFTSucceeds() { Test.expect(mintResult, Test.beSucceeded()) let heightResult = executeScript( - "../scripts/test/get_block_height.cdc", + "./scripts/get_block_height.cdc", [] ) mintedBlockHeight = heightResult.returnValue! as! UInt64 @@ -141,3 +141,32 @@ fun testSerializeBothDisplaysSucceeds() { let serializedResult = SerializeMetadata.serializeFromDisplays(nftDisplay: nftDisplay, collectionDisplay: collectionDisplay) Test.assertEqual(expected, serializedResult!) } + + +access(all) +fun testDeriveSymbolSucceeds() { + let expectedSymbol = "TEST" + + var contractName = "Te_stContract" + + var symbolResult = SerializeMetadata.deriveSymbol(fromString: contractName) + Test.assertEqual(expectedSymbol, symbolResult) + + contractName = "T_e_stContract" + symbolResult = SerializeMetadata.deriveSymbol(fromString: contractName) + Test.assertEqual(expectedSymbol, symbolResult) + + contractName = "_t_E_+*__&__stContract" + symbolResult = SerializeMetadata.deriveSymbol(fromString: contractName) + Test.assertEqual(expectedSymbol, symbolResult) +} + +access(all) +fun testDeriveSymbolFromShortStringSucceeds() { + let expectedSymbol = "C" + + let contractName = "c" + + var symbolResult = SerializeMetadata.deriveSymbol(fromString: contractName) + Test.assertEqual(expectedSymbol, symbolResult) +} diff --git a/cadence/tests/test_helpers.cdc b/cadence/tests/test_helpers.cdc index dda61861..e88836c7 100644 --- a/cadence/tests/test_helpers.cdc +++ b/cadence/tests/test_helpers.cdc @@ -10,6 +10,8 @@ import "FlowStorageFees" /// See the python util `get_code_hex.py` to retrieve the hex-encoded Cadence either with or /// without a separator (`{{CONTRACT_NAME}}` used in templates to "chunk" template code). +access(all) let metadataViewsUpdateCode = "696d706f72742046756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030320a696d706f7274204e6f6e46756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030310a696d706f727420566965775265736f6c7665722066726f6d203078303030303030303030303030303030310a0a2f2f2f205468697320636f6e747261637420696d706c656d656e747320746865206d65746164617461207374616e646172642070726f706f7365640a2f2f2f20696e20464c49502d303633362e0a2f2f2f0a2f2f2f205265663a2068747470733a2f2f6769746875622e636f6d2f6f6e666c6f772f666c6970732f626c6f622f6d61696e2f6170706c69636174696f6e2f32303231303931362d6e66742d6d657461646174612e6d640a2f2f2f0a2f2f2f205374727563747320616e64207265736f75726365732063616e20696d706c656d656e74206f6e65206f72206d6f72650a2f2f2f206d657461646174612074797065732c2063616c6c65642076696577732e20456163682076696577207479706520726570726573656e74730a2f2f2f206120646966666572656e74206b696e64206f66206d657461646174612c207375636820617320612063726561746f722062696f6772617068790a2f2f2f206f722061204a50454720696d6167652066696c652e0a2f2f2f0a61636365737328616c6c2920636f6e7472616374204d657461646174615669657773207b0a0a202020202f2f2f20446973706c617920697320612062617369632076696577207468617420696e636c7564657320746865206e616d652c206465736372697074696f6e20616e640a202020202f2f2f207468756d626e61696c20666f7220616e206f626a6563742e204d6f7374206f626a656374732073686f756c6420696d706c656d656e74207468697320766965772e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420446973706c6179207b0a0a20202020202020202f2f2f20546865206e616d65206f6620746865206f626a6563742e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c642077696c6c20626520646973706c6179656420696e206c6973747320616e64207468657265666f72652073686f756c640a20202020202020202f2f2f2062652073686f727420616e20636f6e636973652e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f2f2041207772697474656e206465736372697074696f6e206f6620746865206f626a6563742e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c642077696c6c20626520646973706c6179656420696e20612064657461696c65642076696577206f6620746865206f626a6563742c0a20202020202020202f2f2f20736f2063616e206265206d6f726520766572626f73652028652e672e20612070617261677261706820696e7374656164206f6620612073696e676c65206c696e65292e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e670a0a20202020202020202f2f2f204120736d616c6c207468756d626e61696c20726570726573656e746174696f6e206f6620746865206f626a6563742e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c642073686f756c642062652061207765622d667269656e646c792066696c652028692e65204a5045472c20504e47290a20202020202020202f2f2f20746861742063616e20626520646973706c6179656420696e206c697374732c206c696e6b2070726576696577732c206574632e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574207468756d626e61696c3a207b46696c657d0a0a20202020202020207669657720696e6974280a2020202020202020202020206e616d653a20537472696e672c0a2020202020202020202020206465736372697074696f6e3a20537472696e672c0a2020202020202020202020207468756d626e61696c3a207b46696c657d0a202020202020202029207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020202020202073656c662e7468756d626e61696c203d207468756d626e61696c0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420446973706c617920696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c20446973706c6179207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574446973706c6179285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a20446973706c61793f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c446973706c61793e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20446973706c6179207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2047656e6572696320696e74657266616365207468617420726570726573656e747320612066696c652073746f726564206f6e206f72206f666620636861696e2e2046696c65730a202020202f2f2f2063616e206265207573656420746f207265666572656e63657320696d616765732c20766964656f7320616e64206f74686572206d656469612e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420696e746572666163652046696c65207b0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e670a202020207d0a0a202020202f2f2f205669657720746f206578706f736520612066696c6520746861742069732061636365737369626c6520617420616e204854545020286f72204854545053292055524c2e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204854545046696c653a2046696c65207b0a202020202020202061636365737328616c6c29206c65742075726c3a20537472696e670a0a20202020202020207669657720696e69742875726c3a20537472696e6729207b0a20202020202020202020202073656c662e75726c203d2075726c0a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e67207b0a20202020202020202020202072657475726e2073656c662e75726c0a20202020202020207d0a202020207d0a0a202020202f2f2f205669657720746f206578706f736520612066696c652073746f726564206f6e20495046532e0a202020202f2f2f204950465320696d6167657320617265207265666572656e63656420627920746865697220636f6e74656e74206964656e7469666965722028434944290a202020202f2f2f20726174686572207468616e206120646972656374205552492e204120636c69656e74206170706c69636174696f6e2063616e207573652074686973204349440a202020202f2f2f20746f2066696e6420616e64206c6f61642074686520696d6167652076696120616e204950465320676174657761792e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204950465346696c653a2046696c65207b0a0a20202020202020202f2f2f204349442069732074686520636f6e74656e74206964656e74696669657220666f72207468697320495046532066696c652e0a20202020202020202f2f2f0a20202020202020202f2f2f205265663a2068747470733a2f2f646f63732e697066732e696f2f636f6e63657074732f636f6e74656e742d61646472657373696e672f0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206369643a20537472696e670a0a20202020202020202f2f2f205061746820697320616e206f7074696f6e616c207061746820746f207468652066696c65207265736f7572636520696e20616e2049504653206469726563746f72792e0a20202020202020202f2f2f0a20202020202020202f2f2f2054686973206669656c64206973206f6e6c79206e6565646564206966207468652066696c6520697320696e736964652061206469726563746f72792e0a20202020202020202f2f2f0a20202020202020202f2f2f205265663a2068747470733a2f2f646f63732e697066732e696f2f636f6e63657074732f66696c652d73797374656d732f0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c657420706174683a20537472696e673f0a0a20202020202020207669657720696e6974286369643a20537472696e672c20706174683a20537472696e673f29207b0a20202020202020202020202073656c662e636964203d206369640a20202020202020202020202073656c662e70617468203d20706174680a20202020202020207d0a0a20202020202020202f2f2f20546869732066756e6374696f6e2072657475726e73207468652049504653206e61746976652055524c20666f7220746869732066696c652e0a20202020202020202f2f2f205265663a2068747470733a2f2f646f63732e697066732e696f2f686f772d746f2f616464726573732d697066732d6f6e2d7765622f236e61746976652d75726c730a20202020202020202f2f2f0a20202020202020202f2f2f204072657475726e2054686520737472696e6720636f6e7461696e696e67207468652066696c65207572690a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e67207b0a2020202020202020202020206966206c65742070617468203d2073656c662e70617468207b0a2020202020202020202020202020202072657475726e2022697066733a2f2f222e636f6e6361742873656c662e636964292e636f6e63617428222f22292e636f6e6361742870617468290a2020202020202020202020207d0a0a20202020202020202020202072657475726e2022697066733a2f2f222e636f6e6361742873656c662e636964290a20202020202020207d0a202020207d0a0a202020202f2f2f205669657720746f20726570726573656e7420612066696c65207769746820616e20636f72726573706f6964696e67206d65646961547970652e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204d65646961207b0a0a20202020202020202f2f2f2046696c6520666f7220746865206d656469610a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c65742066696c653a207b46696c657d0a0a20202020202020202f2f2f206d656469612d7479706520636f6d6573206f6e2074686520666f726d206f6620747970652f73756274797065206173206465736372696265642068657265200a20202020202020202f2f2f2068747470733a2f2f646576656c6f7065722e6d6f7a696c6c612e6f72672f656e2d55532f646f63732f5765622f485454502f4261736963735f6f665f485454502f4d494d455f74797065730a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206d65646961547970653a20537472696e670a0a20202020202020207669657720696e69742866696c653a207b46696c657d2c206d65646961547970653a20537472696e6729207b0a2020202020202020202073656c662e66696c653d66696c650a2020202020202020202073656c662e6d65646961547970653d6d65646961547970650a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720666f72206d756c7469706c65206d656469612076696577730a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204d6564696173207b0a0a20202020202020202f2f2f20416e206172626974726172792d73697a6564206c69737420666f7220616e79206e756d626572206f66204d65646961206974656d730a202020202020202061636365737328616c6c29206c6574206974656d733a205b4d656469615d0a0a20202020202020207669657720696e6974285f206974656d733a205b4d656469615d29207b0a20202020202020202020202073656c662e6974656d73203d206974656d730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204d656469617320696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204d6564696173207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744d6564696173285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204d65646961733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4d65646961733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204d6564696173207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f20726570726573656e742061206c6963656e7365206163636f7264696e6720746f2068747470733a2f2f737064782e6f72672f6c6963656e7365732f0a202020202f2f2f205468697320766965772063616e20626520757365642069662074686520636f6e74656e74206f6620616e204e4654206973206c6963656e7365642e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204c6963656e7365207b0a202020202020202061636365737328616c6c29206c657420737064784964656e7469666965723a20537472696e670a0a20202020202020207669657720696e6974285f206964656e7469666965723a20537472696e6729207b0a20202020202020202020202073656c662e737064784964656e746966696572203d206964656e7469666965720a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204c6963656e736520696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204c6963656e7365207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744c6963656e7365285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204c6963656e73653f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4c6963656e73653e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204c6963656e7365207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f206578706f736520612055524c20746f2074686973206974656d206f6e20616e2065787465726e616c20736974652e0a202020202f2f2f20546869732063616e2062652075736564206279206170706c69636174696f6e73206c696b65202e66696e6420616e6420426c6f63746f20746f20646972656374207573657273200a202020202f2f2f20746f20746865206f726967696e616c206c696e6b20666f7220616e204e4654206f7220612070726f6a656374207061676520746861742064657363726962657320746865204e465420636f6c6c656374696f6e2e0a202020202f2f2f2065672068747470733a2f2f7777772e6d792d6e66742d70726f6a6563742e636f6d2f6f766572766965772d6f662d6e66742d636f6c6c656374696f6e0a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045787465726e616c55524c207b0a202020202020202061636365737328616c6c29206c65742075726c3a20537472696e670a0a20202020202020207669657720696e6974285f2075726c3a20537472696e6729207b0a20202020202020202020202073656c662e75726c3d75726c0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f206765742045787465726e616c55524c20696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c2045787465726e616c55524c207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e2067657445787465726e616c55524c285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2045787465726e616c55524c3f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c45787465726e616c55524c3e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2045787465726e616c55524c207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2056696577207468617420646566696e65732074686520636f6d706f7361626c6520726f79616c7479207374616e646172642074686174206769766573206d61726b6574706c616365732061200a202020202f2f2f20756e696669656420696e7465726661636520746f20737570706f7274204e465420726f79616c746965732e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420526f79616c7479207b0a0a20202020202020202f2f2f2047656e657269632046756e6769626c65546f6b656e20526563656976657220666f72207468652062656e6566696369617279206f662074686520726f79616c74790a20202020202020202f2f2f2043616e206765742074686520636f6e63726574652074797065206f662074686520726563656976657220776974682072656365697665722e6765745479706528290a20202020202020202f2f2f205265636f6d6d656e646174696f6e202d2055736572732073686f756c64206372656174652061206e6577206c696e6b20666f72206120466c6f77546f6b656e200a20202020202020202f2f2f20726563656976657220666f722074686973207573696e672060676574526f79616c747952656365697665725075626c6963506174682829602c20616e64206e6f74200a20202020202020202f2f2f20757365207468652064656661756c7420466c6f77546f6b656e2072656365697665722e20546869732077696c6c20616c6c6f7720757365727320746f20757064617465200a20202020202020202f2f2f20746865206361706162696c69747920696e207468652066757475726520746f207573652061206d6f72652067656e65726963206361706162696c6974790a202020202020202061636365737328616c6c29206c65742072656365697665723a204361706162696c6974793c267b46756e6769626c65546f6b656e2e52656365697665727d3e0a0a20202020202020202f2f2f204d756c7469706c696572207573656420746f2063616c63756c6174652074686520616d6f756e74206f662073616c652076616c7565207472616e7366657272656420746f200a20202020202020202f2f2f20726f79616c74792072656365697665722e204e6f7465202d2049742073686f756c64206265206265747765656e20302e3020616e6420312e30200a20202020202020202f2f2f204578202d204966207468652073616c652076616c7565206973207820616e64206d756c7469706c69657220697320302e3536207468656e2074686520726f79616c7479200a20202020202020202f2f2f2076616c756520776f756c6420626520302e3536202a20782e0a20202020202020202f2f2f2047656e6572616c6c792070657263656e746167652067657420726570726573656e74656420696e207465726d73206f6620626173697320706f696e74730a20202020202020202f2f2f20696e20736f6c696469747920626173656420736d61727420636f6e747261637473207768696c6520636164656e6365206f6666657273206055466978363460200a20202020202020202f2f2f207468617420616c726561647920737570706f7274732074686520626173697320706f696e7473207573652063617365206265636175736520697473200a20202020202020202f2f2f206f7065726174696f6e732061726520656e746972656c792064657465726d696e697374696320696e7465676572206f7065726174696f6e7320616e6420737570706f7274200a20202020202020202f2f2f20757020746f203820706f696e7473206f6620707265636973696f6e2e0a202020202020202061636365737328616c6c29206c6574206375743a205546697836340a0a20202020202020202f2f2f204f7074696f6e616c206465736372697074696f6e3a20546869732063616e20626520746865206361757365206f6620706179696e672074686520726f79616c74792c0a20202020202020202f2f2f207468652072656c6174696f6e73686970206265747765656e20746865206077616c6c65746020616e6420746865204e46542c206f7220616e797468696e6720656c73650a20202020202020202f2f2f207468617420746865206f776e6572206d696768742077616e7420746f20737065636966792e0a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e670a0a20202020202020207669657720696e69742872656365697665723a204361706162696c6974793c267b46756e6769626c65546f6b656e2e52656365697665727d3e2c206375743a205546697836342c206465736372697074696f6e3a20537472696e6729207b0a202020202020202020202020707265207b0a20202020202020202020202020202020637574203e3d20302e3020262620637574203c3d20312e30203a20224375742076616c75652073686f756c6420626520696e2076616c69642072616e676520692e65205b302c315d220a2020202020202020202020207d0a20202020202020202020202073656c662e7265636569766572203d2072656365697665720a20202020202020202020202073656c662e637574203d206375740a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720666f72206d756c7469706c6520526f79616c74792076696577732e0a202020202f2f2f204d61726b6574706c616365732063616e20717565727920746869732060526f79616c7469657360207374727563742066726f6d204e465473200a202020202f2f2f20616e642061726520657870656374656420746f2070617920726f79616c74696573206261736564206f6e2074686573652073706563696669636174696f6e732e0a202020202f2f2f0a2020202061636365737328616c6c292073747275637420526f79616c74696573207b0a0a20202020202020202f2f2f204172726179207468617420747261636b732074686520696e646976696475616c20726f79616c746965730a20202020202020206163636573732873656c6629206c657420637574496e666f733a205b526f79616c74795d0a0a202020202020202061636365737328616c6c29207669657720696e6974285f20637574496e666f733a205b526f79616c74795d29207b0a2020202020202020202020202f2f2056616c696461746520746861742073756d206f6620616c6c20637574206d756c7469706c696572732073686f756c64206e6f742062652067726561746572207468616e20312e300a20202020202020202020202076617220746f74616c437574203d20302e300a202020202020202020202020666f7220726f79616c747920696e20637574496e666f73207b0a20202020202020202020202020202020746f74616c437574203d20746f74616c437574202b20726f79616c74792e6375740a2020202020202020202020207d0a20202020202020202020202061737365727428746f74616c437574203c3d20312e302c206d6573736167653a202253756d206f6620637574496e666f73206d756c7469706c696572732073686f756c64206e6f742062652067726561746572207468616e20312e3022290a2020202020202020202020202f2f2041737369676e2074686520637574496e666f730a20202020202020202020202073656c662e637574496e666f73203d20637574496e666f730a20202020202020207d0a0a20202020202020202f2f2f2052657475726e2074686520637574496e666f73206c6973740a20202020202020202f2f2f0a20202020202020202f2f2f204072657475726e20416e20617272617920636f6e7461696e696e6720616c6c2074686520726f79616c7469657320737472756374730a20202020202020202f2f2f0a202020202020202061636365737328616c6c2920766965772066756e20676574526f79616c7469657328293a205b526f79616c74795d207b0a20202020202020202020202072657475726e2073656c662e637574496e666f730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420526f79616c7469657320696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c20526f79616c74696573207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574526f79616c74696573285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a20526f79616c746965733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c526f79616c746965733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20526f79616c74696573207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2047657420746865207061746820746861742073686f756c64206265207573656420666f7220726563656976696e6720726f79616c746965730a202020202f2f2f20546869732069732061207061746820746861742077696c6c206576656e7475616c6c79206265207573656420666f7220612067656e6572696320737769746368626f6172642072656365697665722c0a202020202f2f2f2068656e636520746865206e616d65206275742077696c6c206f6e6c79206265207573656420666f7220726f79616c7469657320666f72206e6f772e0a202020202f2f2f0a202020202f2f2f204072657475726e20546865205075626c69635061746820666f72207468652067656e657269632046542072656365697665720a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e20676574526f79616c747952656365697665725075626c69635061746828293a205075626c696350617468207b0a202020202020202072657475726e202f7075626c69632f47656e65726963465452656365697665720a202020207d0a0a202020202f2f2f205669657720746f20726570726573656e7420612073696e676c65206669656c64206f66206d65746164617461206f6e20616e204e46542e0a202020202f2f2f2054686973206973207573656420746f2067657420747261697473206f6620696e646976696475616c206b65792f76616c756520706169727320616c6f6e67207769746820736f6d650a202020202f2f2f20636f6e7465787475616c697a656420646174612061626f7574207468652074726169740a202020202f2f2f0a2020202061636365737328616c6c2920737472756374205472616974207b0a20202020202020202f2f20546865206e616d65206f66207468652074726169742e204c696b65204261636b67726f756e642c20457965732c20486169722c206574632e0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f2054686520756e6465726c79696e672076616c7565206f66207468652074726169742c207468652072657374206f6620746865206669656c6473206f6620612074726169742070726f7669646520636f6e7465787420746f207468652076616c75652e0a202020202020202061636365737328616c6c29206c65742076616c75653a20416e795374727563740a0a20202020202020202f2f20646973706c617954797065206973207573656420746f2073686f7720736f6d6520636f6e746578742061626f757420776861742074686973206e616d6520616e642076616c756520726570726573656e740a20202020202020202f2f20666f7220696e7374616e63652c20796f7520636f756c64207365742076616c756520746f206120756e69782074696d657374616d702c20616e64207370656369667920646973706c6179547970652061732022446174652220746f2074656c6c0a20202020202020202f2f20706c6174666f726d7320746f20636f6e73756d6520746869732074726169742061732061206461746520616e64206e6f742061206e756d6265720a202020202020202061636365737328616c6c29206c657420646973706c6179547970653a20537472696e673f0a0a20202020202020202f2f205261726974792063616e20616c736f2062652075736564206469726563746c79206f6e20616e206174747269627574652e0a20202020202020202f2f0a20202020202020202f2f2054686973206973206f7074696f6e616c2062656361757365206e6f7420616c6c2061747472696275746573206e65656420746f20636f6e7472696275746520746f20746865204e46542773207261726974792e0a202020202020202061636365737328616c6c29206c6574207261726974793a205261726974793f0a0a20202020202020207669657720696e6974286e616d653a20537472696e672c2076616c75653a20416e795374727563742c20646973706c6179547970653a20537472696e673f2c207261726974793a205261726974793f29207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e76616c7565203d2076616c75650a20202020202020202020202073656c662e646973706c617954797065203d20646973706c6179547970650a20202020202020202020202073656c662e726172697479203d207261726974790a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720746f2072657475726e20616c6c2074686520747261697473206f6e20616e204e46542e0a202020202f2f2f2054686973206973207573656420746f2072657475726e2074726169747320617320696e646976696475616c206b65792f76616c756520706169727320616c6f6e6720776974680a202020202f2f2f20736f6d6520636f6e7465787475616c697a656420646174612061626f757420656163682074726169742e0a2020202061636365737328616c6c292073747275637420547261697473207b0a202020202020202061636365737328616c6c29206c6574207472616974733a205b54726169745d0a0a20202020202020207669657720696e6974285f207472616974733a205b54726169745d29207b0a20202020202020202020202073656c662e747261697473203d207472616974730a20202020202020207d0a2020202020202020202020200a20202020202020202f2f2f204164647320612073696e676c6520547261697420746f207468652054726169747320766965770a20202020202020202f2f2f200a20202020202020202f2f2f2040706172616d2054726169743a205468652074726169742073747275637420746f2062652061646465640a20202020202020202f2f2f0a202020202020202061636365737328616c6c292066756e206164645472616974285f20743a20547261697429207b0a20202020202020202020202073656c662e7472616974732e617070656e642874290a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420547261697473207669657720696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c20547261697473207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574547261697473285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a205472616974733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c5472616974733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20547261697473207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f2048656c7065722066756e6374696f6e20746f20656173696c7920636f6e7665727420612064696374696f6e61727920746f207472616974732e20466f72204e4654200a202020202f2f2f20636f6c6c656374696f6e73207468617420646f206e6f74206e65656420656974686572206f6620746865206f7074696f6e616c2076616c756573206f6620612054726169742c200a202020202f2f2f2074686973206d6574686f642073686f756c64207375666669636520746f2067697665207468656d20616e206172726179206f662076616c6964207472616974732e0a202020202f2f2f0a202020202f2f2f2040706172616d20646963743a205468652064696374696f6e61727920746f20626520636f6e76657274656420746f205472616974730a202020202f2f2f2040706172616d206578636c756465644e616d65733a20416e206f7074696f6e616c20537472696e672061727261792073706563696679696e6720746865206064696374600a202020202f2f2f2020202020202020206b657973207468617420617265206e6f742077616e74656420746f206265636f6d652060547261697473600a202020202f2f2f204072657475726e205468652067656e6572617465642054726169747320766965770a202020202f2f2f0a2020202061636365737328616c6c292066756e2064696374546f54726169747328646963743a207b537472696e673a20416e795374727563747d2c206578636c756465644e616d65733a205b537472696e675d3f293a20547261697473207b0a20202020202020202f2f20436f6c6c656374696f6e206f776e657273206d69676874206e6f742077616e7420616c6c20746865206669656c647320696e207468656972206d6574616461746120696e636c756465642e0a20202020202020202f2f2054686579206d696768742077616e7420746f2068616e646c6520736f6d65207370656369616c6c792c206f722074686579206d69676874206a757374206e6f742077616e74207468656d20696e636c7564656420617420616c6c2e0a20202020202020206966206578636c756465644e616d657320213d206e696c207b0a202020202020202020202020666f72206b20696e206578636c756465644e616d657321207b0a20202020202020202020202020202020646963742e72656d6f7665286b65793a206b290a2020202020202020202020207d0a20202020202020207d0a0a20202020202020206c6574207472616974733a205b54726169745d203d205b5d0a2020202020202020666f72206b20696e20646963742e6b657973207b0a2020202020202020202020206c6574207472616974203d205472616974286e616d653a206b2c2076616c75653a20646963745b6b5d212c20646973706c6179547970653a206e696c2c207261726974793a206e696c290a2020202020202020202020207472616974732e617070656e64287472616974290a20202020202020207d0a0a202020202020202072657475726e2054726169747328747261697473290a202020207d0a0a202020202f2f2f204f7074696f6e616c207669657720666f7220636f6c6c656374696f6e732074686174206973737565206d756c7469706c65206f626a656374730a202020202f2f2f2077697468207468652073616d65206f722073696d696c6172206d657461646174612c20666f72206578616d706c6520616e2058206f6620313030207365742e20546869730a202020202f2f2f20696e666f726d6174696f6e2069732075736566756c20666f722077616c6c65747320616e64206d61726b6574706c616365732e0a202020202f2f2f20416e204e4654206d696768742062652070617274206f66206d756c7469706c652065646974696f6e732c20776869636820697320776879207468652065646974696f6e0a202020202f2f2f20696e666f726d6174696f6e2069732072657475726e656420617320616e206172626974726172792073697a65642061727261790a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045646974696f6e207b0a0a20202020202020202f2f2f20546865206e616d65206f66207468652065646974696f6e0a20202020202020202f2f2f20466f72206578616d706c652c207468697320636f756c64206265205365742c20506c61792c205365726965732c0a20202020202020202f2f2f206f7220616e79206f746865722077617920612070726f6a65637420636f756c6420636c617373696679206974732065646974696f6e730a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e673f0a0a20202020202020202f2f2f205468652065646974696f6e206e756d626572206f6620746865206f626a6563742e0a20202020202020202f2f2f20466f7220616e20223234206f662031303020282332342f3130302922206974656d2c20746865206e756d6265722069732032342e0a202020202020202061636365737328616c6c29206c6574206e756d6265723a2055496e7436340a0a20202020202020202f2f2f20546865206d61782065646974696f6e206e756d626572206f6620746869732074797065206f66206f626a656374732e0a20202020202020202f2f2f2054686973206669656c642073686f756c64206f6e6c792062652070726f766964656420666f72206c696d697465642d65646974696f6e6564206f626a656374732e0a20202020202020202f2f2f20466f7220616e20223234206f662031303020282332342f3130302922206974656d2c206d6178206973203130302e0a20202020202020202f2f2f20466f7220616e206974656d207769746820756e6c696d697465642065646974696f6e2c206d61782073686f756c642062652073657420746f206e696c2e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c29206c6574206d61783a2055496e7436343f0a0a20202020202020207669657720696e6974286e616d653a20537472696e673f2c206e756d6265723a2055496e7436342c206d61783a2055496e7436343f29207b0a2020202020202020202020206966206d617820213d206e696c207b0a20202020202020202020202020202020617373657274286e756d626572203c3d206d6178212c206d6573736167653a2022546865206e756d6265722063616e6e6f742062652067726561746572207468616e20746865206d6178206e756d6265722122290a2020202020202020202020207d0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e6e756d626572203d206e756d6265720a20202020202020202020202073656c662e6d6178203d206d61780a20202020202020207d0a202020207d0a0a202020202f2f2f2057726170706572207669657720666f72206d756c7469706c652045646974696f6e2076696577730a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045646974696f6e73207b0a0a20202020202020202f2f2f20416e206172626974726172792d73697a6564206c69737420666f7220616e79206e756d626572206f662065646974696f6e730a20202020202020202f2f2f207468617420746865204e4654206d6967687420626520612070617274206f660a202020202020202061636365737328616c6c29206c657420696e666f4c6973743a205b45646974696f6e5d0a0a20202020202020207669657720696e6974285f20696e666f4c6973743a205b45646974696f6e5d29207b0a20202020202020202020202073656c662e696e666f4c697374203d20696e666f4c6973740a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f206765742045646974696f6e7320696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c2045646974696f6e73207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e2067657445646974696f6e73285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2045646974696f6e733f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c45646974696f6e733e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2045646974696f6e73207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720726570726573656e74696e6720612070726f6a6563742d646566696e65642073657269616c206e756d62657220666f722061207370656369666963204e46540a202020202f2f2f2050726f6a65637473206861766520646966666572656e7420646566696e6974696f6e7320666f72207768617420612073657269616c206e756d6265722073686f756c642062650a202020202f2f2f20536f6d65206d61792075736520746865204e46547320726567756c617220494420616e6420736f6d65206d617920757365206120646966666572656e740a202020202f2f2f20636c617373696669636174696f6e2073797374656d2e205468652073657269616c206e756d62657220697320657870656374656420746f20626520756e6971756520616d6f6e670a202020202f2f2f206f74686572204e4654732077697468696e20746861742070726f6a6563740a202020202f2f2f0a2020202061636365737328616c6c29207374727563742053657269616c207b0a202020202020202061636365737328616c6c29206c6574206e756d6265723a2055496e7436340a0a20202020202020207669657720696e6974285f206e756d6265723a2055496e74363429207b0a20202020202020202020202073656c662e6e756d626572203d206e756d6265720a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f206765742053657269616c20696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e20416e206f7074696f6e616c2053657269616c207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e2067657453657269616c285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a2053657269616c3f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c53657269616c3e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f2053657269616c207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f206578706f73652072617269747920696e666f726d6174696f6e20666f7220612073696e676c65207261726974790a202020202f2f2f204e6f74652074686174206120726172697479206e6565647320746f2068617665206569746865722073636f7265206f72206465736372697074696f6e206275742069742063616e200a202020202f2f2f206861766520626f74680a202020202f2f2f0a2020202061636365737328616c6c292073747275637420526172697479207b0a20202020202020202f2f2f205468652073636f7265206f6620746865207261726974792061732061206e756d6265720a202020202020202061636365737328616c6c29206c65742073636f72653a205546697836343f0a0a20202020202020202f2f2f20546865206d6178696d756d2076616c7565206f662073636f72650a202020202020202061636365737328616c6c29206c6574206d61783a205546697836343f0a0a20202020202020202f2f2f20546865206465736372697074696f6e206f662074686520726172697479206173206120737472696e672e0a20202020202020202f2f2f0a20202020202020202f2f2f205468697320636f756c64206265204c6567656e646172792c20457069632c20526172652c20556e636f6d6d6f6e2c20436f6d6d6f6e206f7220616e79206f7468657220737472696e672076616c75650a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e673f0a0a20202020202020207669657720696e69742873636f72653a205546697836343f2c206d61783a205546697836343f2c206465736372697074696f6e3a20537472696e673f29207b0a20202020202020202020202069662073636f7265203d3d206e696c202626206465736372697074696f6e203d3d206e696c207b0a2020202020202020202020202020202070616e696328224120526172697479206e6565647320746f207365742073636f72652c206465736372697074696f6e206f7220626f746822290a2020202020202020202020207d0a0a20202020202020202020202073656c662e73636f7265203d2073636f72650a20202020202020202020202073656c662e6d6178203d206d61780a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420526172697479207669657720696e2061207479706573616665207761790a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c20526172697479207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e20676574526172697479285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a205261726974793f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c5261726974793e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f20526172697479207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f204e46545669657720777261707320616c6c20436f726520766965777320616c6f6e67206069646020616e6420607575696460206669656c64732c20616e642069732075736564200a202020202f2f2f20746f2067697665206120636f6d706c6574652070696374757265206f6620616e204e46542e204d6f7374204e4654732073686f756c6420696d706c656d656e742074686973200a202020202f2f2f20766965772e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204e465456696577207b0a202020202020202061636365737328616c6c29206c65742069643a2055496e7436340a202020202020202061636365737328616c6c29206c657420757569643a2055496e7436340a202020202020202061636365737328616c6c29206c657420646973706c61793a204d6574616461746156696577732e446973706c61793f0a202020202020202061636365737328616c6c29206c65742065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c3f0a202020202020202061636365737328616c6c29206c657420636f6c6c656374696f6e446174613a204e4654436f6c6c656374696f6e446174613f0a202020202020202061636365737328616c6c29206c657420636f6c6c656374696f6e446973706c61793a204e4654436f6c6c656374696f6e446973706c61793f0a202020202020202061636365737328616c6c29206c657420726f79616c746965733a20526f79616c746965733f0a202020202020202061636365737328616c6c29206c6574207472616974733a205472616974733f0a0a20202020202020207669657720696e6974280a2020202020202020202020206964203a2055496e7436342c0a20202020202020202020202075756964203a2055496e7436342c0a202020202020202020202020646973706c6179203a204d6574616461746156696577732e446973706c61793f2c0a20202020202020202020202065787465726e616c55524c203a204d6574616461746156696577732e45787465726e616c55524c3f2c0a202020202020202020202020636f6c6c656374696f6e44617461203a204e4654436f6c6c656374696f6e446174613f2c0a202020202020202020202020636f6c6c656374696f6e446973706c6179203a204e4654436f6c6c656374696f6e446973706c61793f2c0a202020202020202020202020726f79616c74696573203a20526f79616c746965733f2c0a2020202020202020202020207472616974733a205472616974733f0a202020202020202029207b0a20202020202020202020202073656c662e6964203d2069640a20202020202020202020202073656c662e75756964203d20757569640a20202020202020202020202073656c662e646973706c6179203d20646973706c61790a20202020202020202020202073656c662e65787465726e616c55524c203d2065787465726e616c55524c0a20202020202020202020202073656c662e636f6c6c656374696f6e44617461203d20636f6c6c656374696f6e446174610a20202020202020202020202073656c662e636f6c6c656374696f6e446973706c6179203d20636f6c6c656374696f6e446973706c61790a20202020202020202020202073656c662e726f79616c74696573203d20726f79616c746965730a20202020202020202020202073656c662e747261697473203d207472616974730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f2067657420616e204e46542076696577200a202020202f2f2f0a202020202f2f2f2040706172616d2069643a20546865204e46542069640a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041204e465456696577207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744e4654566965772869643a2055496e7436342c20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204e465456696577207b0a20202020202020206c6574206e667456696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4e4654566965773e2829290a20202020202020206966206e66745669657720213d206e696c207b0a20202020202020202020202072657475726e206e6674566965772120617321204e4654566965770a20202020202020207d0a0a202020202020202072657475726e204e465456696577280a2020202020202020202020206964203a2069642c0a202020202020202020202020757569643a20766965775265736f6c7665722e757569642c0a202020202020202020202020646973706c61793a204d6574616461746156696577732e676574446973706c617928766965775265736f6c766572292c0a20202020202020202020202065787465726e616c55524c203a204d6574616461746156696577732e67657445787465726e616c55524c28766965775265736f6c766572292c0a202020202020202020202020636f6c6c656374696f6e44617461203a2073656c662e6765744e4654436f6c6c656374696f6e4461746128766965775265736f6c766572292c0a202020202020202020202020636f6c6c656374696f6e446973706c6179203a2073656c662e6765744e4654436f6c6c656374696f6e446973706c617928766965775265736f6c766572292c0a202020202020202020202020726f79616c74696573203a2073656c662e676574526f79616c7469657328766965775265736f6c766572292c0a202020202020202020202020747261697473203a2073656c662e67657454726169747328766965775265736f6c766572290a2020202020202020290a202020207d0a0a202020202f2f2f205669657720746f206578706f73652074686520696e666f726d6174696f6e206e65656465642073746f726520616e6420726574726965766520616e204e46542e0a202020202f2f2f20546869732063616e2062652075736564206279206170706c69636174696f6e7320746f2073657475702061204e465420636f6c6c656374696f6e20776974682070726f706572200a202020202f2f2f2073746f7261676520616e64207075626c6963206361706162696c69746965732e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204e4654436f6c6c656374696f6e44617461207b0a20202020202020202f2f2f205061746820696e2073746f726167652077686572652074686973204e4654206973207265636f6d6d656e64656420746f2062652073746f7265642e0a202020202020202061636365737328616c6c29206c65742073746f72616765506174683a2053746f72616765506174680a0a20202020202020202f2f2f205075626c69632070617468207768696368206d757374206265206c696e6b656420746f206578706f7365207075626c6963206361706162696c6974696573206f662074686973204e46540a20202020202020202f2f2f20696e636c7564696e67207374616e64617264204e465420696e746572666163657320616e64206d65746164617461766965777320696e74657266616365730a202020202020202061636365737328616c6c29206c6574207075626c6963506174683a205075626c6963506174680a0a20202020202020202f2f2f2054686520636f6e63726574652074797065206f662074686520636f6c6c656374696f6e2074686174206973206578706f73656420746f20746865207075626c69630a20202020202020202f2f2f206e6f77207468617420656e7469746c656d656e74732065786973742c206974206e6f206c6f6e676572206e6565647320746f206265207265737472696374656420746f206120737065636966696320696e746572666163650a202020202020202061636365737328616c6c29206c6574207075626c6963436f6c6c656374696f6e3a20547970650a0a20202020202020202f2f2f205479706520746861742073686f756c64206265206c696e6b6564206174207468652061666f72656d656e74696f6e6564207075626c696320706174680a202020202020202061636365737328616c6c29206c6574207075626c69634c696e6b6564547970653a20547970650a0a20202020202020202f2f2f2046756e6374696f6e207468617420616c6c6f7773206372656174696f6e206f6620616e20656d707479204e465420636f6c6c656374696f6e207468617420697320696e74656e64656420746f2073746f72650a20202020202020202f2f2f2074686973204e46542e0a202020202020202061636365737328616c6c29206c657420637265617465456d707479436f6c6c656374696f6e3a2066756e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d0a0a20202020202020207669657720696e6974280a20202020202020202020202073746f72616765506174683a2053746f72616765506174682c0a2020202020202020202020207075626c6963506174683a205075626c6963506174682c0a2020202020202020202020207075626c6963436f6c6c656374696f6e3a20547970652c0a2020202020202020202020207075626c69634c696e6b6564547970653a20547970652c0a202020202020202020202020637265617465456d707479436f6c6c656374696f6e46756e6374696f6e3a2066756e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d0a202020202020202029207b0a202020202020202020202020707265207b0a202020202020202020202020202020207075626c69634c696e6b6564547970652e697353756274797065286f663a20547970653c267b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d3e2829293a20225075626c69632074797065206d75737420626520612073756274797065206f66204e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e20696e746572666163652e220a2020202020202020202020207d0a20202020202020202020202073656c662e73746f72616765506174683d73746f72616765506174680a20202020202020202020202073656c662e7075626c6963506174683d7075626c6963506174680a20202020202020202020202073656c662e7075626c6963436f6c6c656374696f6e3d7075626c6963436f6c6c656374696f6e0a20202020202020202020202073656c662e7075626c69634c696e6b6564547970653d7075626c69634c696e6b6564547970650a20202020202020202020202073656c662e637265617465456d707479436f6c6c656374696f6e3d637265617465456d707479436f6c6c656374696f6e46756e6374696f6e0a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204e4654436f6c6c656374696f6e4461746120696e20612077617920746861742077696c6c2072657475726e20616e207479706564204f7074696f6e616c0a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204e4654436f6c6c656374696f6e44617461207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744e4654436f6c6c656374696f6e44617461285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204e4654436f6c6c656374696f6e446174613f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4e4654436f6c6c656374696f6e446174613e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204e4654436f6c6c656374696f6e44617461207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f205669657720746f206578706f73652074686520696e666f726d6174696f6e206e656564656420746f2073686f77636173652074686973204e465427730a202020202f2f2f20636f6c6c656374696f6e2e20546869732063616e2062652075736564206279206170706c69636174696f6e7320746f206769766520616e206f7665727669657720616e64200a202020202f2f2f206772617068696373206f6620746865204e465420636f6c6c656374696f6e2074686973204e46542062656c6f6e677320746f2e0a202020202f2f2f0a2020202061636365737328616c6c2920737472756374204e4654436f6c6c656374696f6e446973706c6179207b0a20202020202020202f2f204e616d6520746861742073686f756c642062652075736564207768656e20646973706c6179696e672074686973204e465420636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a0a20202020202020202f2f204465736372697074696f6e20746861742073686f756c64206265207573656420746f206769766520616e206f76657276696577206f66207468697320636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c6574206465736372697074696f6e3a20537472696e670a0a20202020202020202f2f2045787465726e616c206c696e6b20746f20612055524c20746f2076696577206d6f726520696e666f726d6174696f6e2061626f7574207468697320636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c65742065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c0a0a20202020202020202f2f205371756172652d73697a656420696d61676520746f20726570726573656e74207468697320636f6c6c656374696f6e2e0a202020202020202061636365737328616c6c29206c657420737175617265496d6167653a204d6574616461746156696577732e4d656469610a0a20202020202020202f2f2042616e6e65722d73697a656420696d61676520666f72207468697320636f6c6c656374696f6e2c207265636f6d6d656e64656420746f206861766520612073697a65206e6561722031323030783633302e0a202020202020202061636365737328616c6c29206c65742062616e6e6572496d6167653a204d6574616461746156696577732e4d656469610a0a20202020202020202f2f20536f6369616c206c696e6b7320746f207265616368207468697320636f6c6c656374696f6e277320736f6369616c20686f6d6570616765732e0a20202020202020202f2f20506f737369626c65206b657973206d61792062652022696e7374616772616d222c202274776974746572222c2022646973636f7264222c206574632e0a202020202020202061636365737328616c6c29206c657420736f6369616c733a207b537472696e673a204d6574616461746156696577732e45787465726e616c55524c7d0a0a20202020202020207669657720696e6974280a2020202020202020202020206e616d653a20537472696e672c0a2020202020202020202020206465736372697074696f6e3a20537472696e672c0a20202020202020202020202065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c2c0a202020202020202020202020737175617265496d6167653a204d6574616461746156696577732e4d656469612c0a20202020202020202020202062616e6e6572496d6167653a204d6574616461746156696577732e4d656469612c0a202020202020202020202020736f6369616c733a207b537472696e673a204d6574616461746156696577732e45787465726e616c55524c7d0a202020202020202029207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e6465736372697074696f6e203d206465736372697074696f6e0a20202020202020202020202073656c662e65787465726e616c55524c203d2065787465726e616c55524c0a20202020202020202020202073656c662e737175617265496d616765203d20737175617265496d6167650a20202020202020202020202073656c662e62616e6e6572496d616765203d2062616e6e6572496d6167650a20202020202020202020202073656c662e736f6369616c73203d20736f6369616c730a20202020202020207d0a202020207d0a0a202020202f2f2f2048656c70657220746f20676574204e4654436f6c6c656374696f6e446973706c617920696e20612077617920746861742077696c6c2072657475726e2061207479706564200a202020202f2f2f204f7074696f6e616c0a202020202f2f2f0a202020202f2f2f2040706172616d20766965775265736f6c7665723a2041207265666572656e636520746f20746865207265736f6c766572207265736f757263650a202020202f2f2f204072657475726e2041206f7074696f6e616c204e4654436f6c6c656374696f6e207374727563740a202020202f2f2f0a2020202061636365737328616c6c292066756e206765744e4654436f6c6c656374696f6e446973706c6179285f20766965775265736f6c7665723a20267b566965775265736f6c7665722e5265736f6c7665727d29203a204e4654436f6c6c656374696f6e446973706c61793f207b0a20202020202020206966206c65742076696577203d20766965775265736f6c7665722e7265736f6c76655669657728547970653c4e4654436f6c6c656374696f6e446973706c61793e282929207b0a2020202020202020202020206966206c65742076203d20766965772061733f204e4654436f6c6c656374696f6e446973706c6179207b0a2020202020202020202020202020202072657475726e20760a2020202020202020202020207d0a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2f2f20412073747275637420746f20726570726573656e7420612067656e6572616c2063617365205552492c207573656420746f20726570726573656e742074686520555249206f6620746865204e4654207768657265207468652074797065206f6620555249206973206e6f740a202020202f2f2f2061626c6520746f2062652064657465726d696e65642028692e652e20485454502c20495046532c206574632e290a202020202f2f2f0a2020202061636365737328616c6c292073747275637420555249203a2046696c65207b0a20202020202020202f2f2f20546865206261736520555249207072656669782c20696620616e792e204e6f74206e656564656420666f7220616c6c20555249732c206275742068656c7066756c20666f7220736f6d65207573652063617365730a20202020202020202f2f2f20466f72206578616d706c652c207570646174696e6720612077686f6c65204e465420636f6c6c656374696f6e277320696d61676520686f737420656173696c790a202020202020202061636365737328616c6c29206c657420626173655552493a20537472696e673f0a20202020202020202f2f2f20546865205552492076616c75650a20202020202020202f2f2f204e4f54453a207468697320697320736574206f6e20696e6974206173206120636f6e636174656e6174696f6e206f6620746865206261736555524920616e64207468652076616c7565206966206261736555524920213d206e696c0a20202020202020206163636573732873656c6629206c65742076616c75653a20537472696e670a0a202020202020202061636365737328616c6c2920766965772066756e2075726928293a20537472696e67207b0a20202020202020202020202072657475726e2073656c662e76616c75650a20202020202020207d0a0a2020202020202020696e697428626173655552493a20537472696e673f2c2076616c75653a20537472696e6729207b0a20202020202020202020202073656c662e62617365555249203d20626173655552490a20202020202020202020202073656c662e76616c7565203d206261736555524920213d206e696c203f2062617365555249212e636f6e6361742876616c756529203a2076616c75650a20202020202020207d0a202020207d0a0a202020202f2f2f2050726f6f66206f6620636f6e63657074206d6574616461746120746f20726570726573656e7420746865204552433732312076616c756573206f6620746865204e46540a202020202f2f2f0a2020202061636365737328616c6c29207374727563742045564d427269646765644d65746164617461207b0a20202020202020202f2f2f20546865206e616d65206f6620746865204e46540a202020202020202061636365737328616c6c29206c6574206e616d653a20537472696e670a20202020202020202f2f2f205468652073796d626f6c206f6620746865204e46540a202020202020202061636365737328616c6c29206c65742073796d626f6c3a20537472696e670a20202020202020202f2f2f2054686520555249206f6620746865206173736574202d20746869732063616e2065697468657220626520636f6e74726163742d6c6576656c206f7220746f6b656e2d6c6576656c2055524920646570656e64696e67206f6e20776865726520746865206d657461646174610a20202020202020202f2f2f206973207265717565737465642e205365652074686520566965775265736f6c76657220636f6e747261637420696e7465726661636520746f20646973636f76657220686f7720636f6e74726163742026207265736f757263652d6c6576656c206d657461646174610a20202020202020202f2f2f207265717565737473206172652068616e646c65642e0a202020202020202061636365737328616c6c29206c6574207572693a207b4d6574616461746156696577732e46696c657d0a0a2020202020202020696e6974286e616d653a20537472696e672c2073796d626f6c3a20537472696e672c207572693a207b4d6574616461746156696577732e46696c657d29207b0a20202020202020202020202073656c662e6e616d65203d206e616d650a20202020202020202020202073656c662e73796d626f6c203d2073796d626f6c0a20202020202020202020202073656c662e757269203d207572690a20202020202020207d0a202020207d0a7d0a" + access(all) let evmUpdateCode = "696d706f72742043727970746f0a696d706f7274204e6f6e46756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030310a696d706f72742046756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030320a696d706f727420466c6f77546f6b656e2066726f6d203078303030303030303030303030303030330a0a61636365737328616c6c290a636f6e74726163742045564d207b0a0a202020202f2f20456e7469746c656d656e747320656e61626c696e672066696e65722d6772616e65642061636365737320636f6e74726f6c206f6e206120436164656e63654f776e65644163636f756e740a2020202061636365737328616c6c2920656e7469746c656d656e742056616c69646174650a2020202061636365737328616c6c2920656e7469746c656d656e742057697468647261770a2020202061636365737328616c6c2920656e7469746c656d656e742043616c6c0a2020202061636365737328616c6c2920656e7469746c656d656e74204465706c6f790a2020202061636365737328616c6c2920656e7469746c656d656e74204f776e65720a2020202061636365737328616c6c2920656e7469746c656d656e74204272696467650a0a2020202061636365737328616c6c290a202020206576656e7420436164656e63654f776e65644163636f756e7443726561746564286164647265737342797465733a205b55496e74383b2032305d290a0a202020202f2f2f20464c4f57546f6b656e734465706f736974656420697320656d6974746564207768656e20464c4f5720746f6b656e7320697320627269646765640a202020202f2f2f20696e746f207468652045564d20656e7669726f6e6d656e742e204e6f746520746861742074686973206576656e74206973206e6f7420656d69747465640a202020202f2f2f20666f72207472616e73666572206f6620666c6f7720746f6b656e73206265747765656e2074776f2045564d206164647265737365732e0a2020202061636365737328616c6c290a202020206576656e7420464c4f57546f6b656e734465706f7369746564286164647265737342797465733a205b55496e74383b2032305d2c20616d6f756e743a20554669783634290a0a202020202f2f2f20464c4f57546f6b656e7357697468647261776e20697320656d6974746564207768656e20464c4f5720746f6b656e732061726520627269646765640a202020202f2f2f206f7574206f66207468652045564d20656e7669726f6e6d656e742e204e6f746520746861742074686973206576656e74206973206e6f7420656d69747465640a202020202f2f2f20666f72207472616e73666572206f6620666c6f7720746f6b656e73206265747765656e2074776f2045564d206164647265737365732e0a2020202061636365737328616c6c290a202020206576656e7420464c4f57546f6b656e7357697468647261776e286164647265737342797465733a205b55496e74383b2032305d2c20616d6f756e743a20554669783634290a0a202020202f2f2f204272696467654163636573736f725570646174656420697320656d6974746564207768656e20746865204272696467654163636573736f72204361706162696c6974790a202020202f2f2f206973207570646174656420696e207468652073746f72656420427269646765526f7574657220616c6f6e672077697468206964656e74696679696e670a202020202f2f2f20696e666f726d6174696f6e2061626f757420626f74682e0a2020202061636365737328616c6c290a202020206576656e74204272696467654163636573736f7255706461746564280a2020202020202020726f75746572547970653a20547970652c0a2020202020202020726f75746572555549443a2055496e7436342c0a2020202020202020726f75746572416464726573733a20416464726573732c0a20202020202020206163636573736f72547970653a20547970652c0a20202020202020206163636573736f72555549443a2055496e7436342c0a20202020202020206163636573736f72416464726573733a20416464726573730a20202020290a0a202020202f2f2f2045564d4164647265737320697320616e2045564d2d636f6d70617469626c6520616464726573730a2020202061636365737328616c6c290a202020207374727563742045564d41646472657373207b0a0a20202020202020202f2f2f204279746573206f662074686520616464726573730a202020202020202061636365737328616c6c290a20202020202020206c65742062797465733a205b55496e74383b2032305d0a0a20202020202020202f2f2f20436f6e737472756374732061206e65772045564d20616464726573732066726f6d2074686520676976656e206279746520726570726573656e746174696f6e0a20202020202020207669657720696e69742862797465733a205b55496e74383b2032305d29207b0a20202020202020202020202073656c662e6279746573203d2062797465730a20202020202020207d0a0a20202020202020202f2f2f2042616c616e6365206f662074686520616464726573730a202020202020202061636365737328616c6c290a2020202020202020766965772066756e2062616c616e636528293a2042616c616e6365207b0a2020202020202020202020206c65742062616c616e6365203d20496e7465726e616c45564d2e62616c616e6365280a20202020202020202020202020202020616464726573733a2073656c662e62797465730a202020202020202020202020290a20202020202020202020202072657475726e2042616c616e6365286174746f666c6f773a2062616c616e6365290a20202020202020207d0a0a20202020202020202f2f2f204e6f6e6365206f662074686520616464726573730a202020202020202061636365737328616c6c290a202020202020202066756e206e6f6e636528293a2055496e743634207b0a20202020202020202020202072657475726e20496e7465726e616c45564d2e6e6f6e6365280a20202020202020202020202020202020616464726573733a2073656c662e62797465730a202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f2f20436f6465206f662074686520616464726573730a202020202020202061636365737328616c6c290a202020202020202066756e20636f646528293a205b55496e74385d207b0a20202020202020202020202072657475726e20496e7465726e616c45564d2e636f6465280a20202020202020202020202020202020616464726573733a2073656c662e62797465730a202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f2f20436f646548617368206f662074686520616464726573730a202020202020202061636365737328616c6c290a202020202020202066756e20636f64654861736828293a205b55496e74385d207b0a20202020202020202020202072657475726e20496e7465726e616c45564d2e636f646548617368280a20202020202020202020202020202020616464726573733a2073656c662e62797465730a202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f2f204465706f736974732074686520676976656e207661756c7420696e746f207468652045564d206163636f756e7420776974682074686520676976656e20616464726573730a202020202020202061636365737328616c6c290a202020202020202066756e206465706f7369742866726f6d3a2040466c6f77546f6b656e2e5661756c7429207b0a2020202020202020202020206c657420616d6f756e74203d2066726f6d2e62616c616e63650a202020202020202020202020696620616d6f756e74203d3d20302e30207b0a2020202020202020202020202020202070616e6963282263616c6c696e67206465706f7369742066756e6374696f6e207769746820616e20656d707479207661756c74206973206e6f7420616c6c6f77656422290a2020202020202020202020207d0a202020202020202020202020496e7465726e616c45564d2e6465706f736974280a2020202020202020202020202020202066726f6d3a203c2d66726f6d2c0a20202020202020202020202020202020746f3a2073656c662e62797465730a202020202020202020202020290a202020202020202020202020656d697420464c4f57546f6b656e734465706f7369746564286164647265737342797465733a2073656c662e62797465732c20616d6f756e743a20616d6f756e74290a20202020202020207d0a202020207d0a0a2020202061636365737328616c6c290a202020207374727563742042616c616e6365207b0a0a20202020202020202f2f2f205468652062616c616e636520696e206174746f2d464c4f570a20202020202020202f2f2f204174746f2d464c4f572069732074686520736d616c6c6573742064656e6f6d696e6174696f6e206f6620464c4f5720283165313820464c4f57290a20202020202020202f2f2f2074686174206973207573656420746f2073746f7265206163636f756e742062616c616e63657320696e736964652045564d0a20202020202020202f2f2f2073696d696c617220746f207468652077617920574549206973207573656420746f2073746f72652045544820646976697369626c6520746f20313820646563696d616c20706c616365732e0a202020202020202061636365737328616c6c290a2020202020202020766172206174746f666c6f773a2055496e740a0a20202020202020202f2f2f20436f6e737472756374732061206e65772062616c616e63650a202020202020202061636365737328616c6c290a20202020202020207669657720696e6974286174746f666c6f773a2055496e7429207b0a20202020202020202020202073656c662e6174746f666c6f77203d206174746f666c6f770a20202020202020207d0a0a20202020202020202f2f2f2053657473207468652062616c616e636520627920612055466978363420283820646563696d616c20706f696e7473292c2074686520666f726d61740a20202020202020202f2f2f2074686174206973207573656420696e20436164656e636520746f2073746f726520464c4f5720746f6b656e732e0a202020202020202061636365737328616c6c290a202020202020202066756e20736574464c4f5728666c6f773a20554669783634297b0a20202020202020202020202073656c662e6174746f666c6f77203d20496e7465726e616c45564d2e63617374546f4174746f464c4f572862616c616e63653a20666c6f77290a20202020202020207d0a0a20202020202020202f2f2f204361737473207468652062616c616e636520746f2061205546697836342028726f756e64696e6720646f776e290a20202020202020202f2f2f205761726e696e67212063617374696e6720612062616c616e636520746f20612055466978363420776869636820737570706f7274732061206c6f776572206c6576656c206f6620707265636973696f6e0a20202020202020202f2f2f20283820646563696d616c20706f696e747320696e20636f6d7061726520746f20313829206d6967687420726573756c7420696e20726f756e64696e6720646f776e206572726f722e0a20202020202020202f2f2f205573652074686520746f4174746f466c6f772066756e6374696f6e20696620796f752063617265206e656564206d6f72652061636375726163792e0a202020202020202061636365737328616c6c290a2020202020202020766965772066756e20696e464c4f5728293a20554669783634207b0a20202020202020202020202072657475726e20496e7465726e616c45564d2e63617374546f464c4f572862616c616e63653a2073656c662e6174746f666c6f77290a20202020202020207d0a0a20202020202020202f2f2f2052657475726e73207468652062616c616e636520696e204174746f2d464c4f570a202020202020202061636365737328616c6c290a2020202020202020766965772066756e20696e4174746f464c4f5728293a2055496e74207b0a20202020202020202020202072657475726e2073656c662e6174746f666c6f770a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732074727565206966207468652062616c616e6365206973207a65726f0a202020202020202061636365737328616c6c290a202020202020202066756e2069735a65726f28293a20426f6f6c207b0a20202020202020202020202072657475726e2073656c662e6174746f666c6f77203d3d20300a20202020202020207d0a202020207d0a0a202020202f2f2f207265706f7274732074686520737461747573206f662065766d20657865637574696f6e2e0a2020202061636365737328616c6c2920656e756d205374617475733a2055496e7438207b0a20202020202020202f2f2f2069732028726172656c79292072657475726e6564207768656e2073746174757320697320756e6b6e6f776e0a20202020202020202f2f2f20616e6420736f6d657468696e672068617320676f6e6520766572792077726f6e672e0a202020202020202061636365737328616c6c29206361736520756e6b6e6f776e0a0a20202020202020202f2f2f2069732072657475726e6564207768656e20657865637574696f6e206f6620616e2065766d207472616e73616374696f6e2f63616c6c0a20202020202020202f2f2f20686173206661696c6564206174207468652076616c69646174696f6e20737465702028652e672e206e6f6e6365206d69736d61746368292e0a20202020202020202f2f2f20416e20696e76616c6964207472616e73616374696f6e2f63616c6c2069732072656a656374656420746f2062652065786563757465640a20202020202020202f2f2f206f7220626520696e636c7564656420696e206120626c6f636b2e0a202020202020202061636365737328616c6c29206361736520696e76616c69640a0a20202020202020202f2f2f2069732072657475726e6564207768656e20657865637574696f6e206f6620616e2065766d207472616e73616374696f6e2f63616c6c0a20202020202020202f2f2f20686173206265656e207375636365737366756c206275742074686520766d20686173207265706f7274656420616e206572726f722061730a20202020202020202f2f2f20746865206f7574636f6d65206f6620657865637574696f6e2028652e672e2072756e6e696e67206f7574206f6620676173292e0a20202020202020202f2f2f2041206661696c65642074782f63616c6c20697320696e636c7564656420696e206120626c6f636b2e0a20202020202020202f2f2f204e6f746520746861742072657375626d697373696f6e206f662061206661696c6564207472616e73616374696f6e20776f756c640a20202020202020202f2f2f20726573756c7420696e20696e76616c69642073746174757320696e20746865207365636f6e6420617474656d70742c20676976656e0a20202020202020202f2f2f20746865206e6f6e636520776f756c6420626520636f6d6520696e76616c69642e0a202020202020202061636365737328616c6c292063617365206661696c65640a0a20202020202020202f2f2f2069732072657475726e6564207768656e20657865637574696f6e206f6620616e2065766d207472616e73616374696f6e2f63616c6c0a20202020202020202f2f2f20686173206265656e207375636365737366756c20616e64206e6f206572726f72206973207265706f727465642062792074686520766d2e0a202020202020202061636365737328616c6c292063617365207375636365737366756c0a202020207d0a0a202020202f2f2f207265706f72747320746865206f7574636f6d65206f662065766d207472616e73616374696f6e2f63616c6c20657865637574696f6e20617474656d70740a2020202061636365737328616c6c292073747275637420526573756c74207b0a20202020202020202f2f2f20737461747573206f662074686520657865637574696f6e0a202020202020202061636365737328616c6c290a20202020202020206c6574207374617475733a205374617475730a0a20202020202020202f2f2f206572726f7220636f646520286572726f7220636f6465207a65726f206d65616e73206e6f206572726f72290a202020202020202061636365737328616c6c290a20202020202020206c6574206572726f72436f64653a2055496e7436340a0a20202020202020202f2f2f2072657475726e732074686520616d6f756e74206f6620676173206d65746572656420647572696e670a20202020202020202f2f2f2065766d20657865637574696f6e0a202020202020202061636365737328616c6c290a20202020202020206c657420676173557365643a2055496e7436340a0a20202020202020202f2f2f2072657475726e7320746865206461746120746861742069732072657475726e65642066726f6d0a20202020202020202f2f2f207468652065766d20666f72207468652063616c6c2e20466f7220636f612e6465706c6f790a20202020202020202f2f2f2063616c6c732069742072657475726e73207468652061646472657373206279746573206f66207468650a20202020202020202f2f2f206e65776c79206465706c6f79656420636f6e74726163742e0a202020202020202061636365737328616c6c290a20202020202020206c657420646174613a205b55496e74385d0a0a2020202020202020696e6974280a2020202020202020202020207374617475733a205374617475732c0a2020202020202020202020206572726f72436f64653a2055496e7436342c0a202020202020202020202020676173557365643a2055496e7436342c0a202020202020202020202020646174613a205b55496e74385d0a202020202020202029207b0a20202020202020202020202073656c662e737461747573203d207374617475730a20202020202020202020202073656c662e6572726f72436f6465203d206572726f72436f64650a20202020202020202020202073656c662e67617355736564203d20676173557365640a20202020202020202020202073656c662e64617461203d20646174610a20202020202020207d0a202020207d0a0a2020202061636365737328616c6c290a202020207265736f7572636520696e74657266616365204164647265737361626c65207b0a20202020202020202f2f2f205468652045564d20616464726573730a202020202020202061636365737328616c6c290a2020202020202020766965772066756e206164647265737328293a2045564d416464726573730a202020207d0a0a2020202061636365737328616c6c290a202020207265736f7572636520436164656e63654f776e65644163636f756e743a204164647265737361626c65207b0a0a20202020202020206163636573732873656c66290a2020202020202020766172206164647265737342797465733a205b55496e74383b2032305d0a0a2020202020202020696e69742829207b0a2020202020202020202020202f2f206164647265737320697320696e697469616c6c792073657420746f207a65726f0a2020202020202020202020202f2f206275742075706461746564207468726f75676820696e697441646472657373206c617465720a2020202020202020202020202f2f207765206861766520746f20646f20746869732073696e6365207765206e656564207265736f75726365206964202875756964290a2020202020202020202020202f2f20746f2063616c63756c617465207468652045564d206164647265737320666f72207468697320636164656e6365206f776e6564206163636f756e740a20202020202020202020202073656c662e616464726573734279746573203d205b302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20302c20305d0a20202020202020207d0a0a202020202020202061636365737328636f6e7472616374290a202020202020202066756e20696e697441646472657373286164647265737342797465733a205b55496e74383b2032305d29207b0a20202020202020202020202f2f206f6e6c7920616c6c6f7720736574206164647265737320666f72207468652066697273742074696d650a20202020202020202020202f2f20636865636b206164647265737320697320656d7074790a202020202020202020202020666f72206974656d20696e2073656c662e616464726573734279746573207b0a20202020202020202020202020202020617373657274286974656d203d3d20302c206d6573736167653a2022616464726573732062797465206973206e6f7420656d70747922290a2020202020202020202020207d0a202020202020202020202073656c662e616464726573734279746573203d206164647265737342797465730a20202020202020207d0a0a20202020202020202f2f2f205468652045564d2061646472657373206f662074686520636164656e6365206f776e6564206163636f756e740a202020202020202061636365737328616c6c290a2020202020202020766965772066756e206164647265737328293a2045564d41646472657373207b0a2020202020202020202020202f2f20416c77617973206372656174652061206e65772045564d4164647265737320696e7374616e63650a20202020202020202020202072657475726e2045564d416464726573732862797465733a2073656c662e616464726573734279746573290a20202020202020207d0a0a20202020202020202f2f2f204765742062616c616e6365206f662074686520636164656e6365206f776e6564206163636f756e740a202020202020202061636365737328616c6c290a2020202020202020766965772066756e2062616c616e636528293a2042616c616e6365207b0a20202020202020202020202072657475726e2073656c662e6164647265737328292e62616c616e636528290a20202020202020207d0a0a20202020202020202f2f2f204465706f736974732074686520676976656e207661756c7420696e746f2074686520636164656e6365206f776e6564206163636f756e7427732062616c616e63650a202020202020202061636365737328616c6c290a202020202020202066756e206465706f7369742866726f6d3a2040466c6f77546f6b656e2e5661756c7429207b0a20202020202020202020202073656c662e6164647265737328292e6465706f7369742866726f6d3a203c2d66726f6d290a20202020202020207d0a0a20202020202020202f2f2f205468652045564d2061646472657373206f662074686520636164656e6365206f776e6564206163636f756e7420626568696e6420616e20656e7469746c656d656e742c20616374696e672061732070726f6f66206f66206163636573730a2020202020202020616363657373284f776e6572207c2056616c6964617465290a2020202020202020766965772066756e2070726f7465637465644164647265737328293a2045564d41646472657373207b0a20202020202020202020202072657475726e2073656c662e6164647265737328290a20202020202020207d0a0a20202020202020202f2f2f20576974686472617773207468652062616c616e63652066726f6d2074686520636164656e6365206f776e6564206163636f756e7427732062616c616e63650a20202020202020202f2f2f204e6f7465207468617420616d6f756e747320736d616c6c6572207468616e2031306e4620283130652d38292063616e27742062652077697468647261776e0a20202020202020202f2f2f20676976656e207468617420466c6f7720546f6b656e205661756c747320757365205546697836347320746f2073746f72652062616c616e6365732e0a20202020202020202f2f2f2049662074686520676976656e2062616c616e636520636f6e76657273696f6e20746f2055466978363420726573756c747320696e0a20202020202020202f2f2f20726f756e64696e67206572726f722c20746869732066756e6374696f6e20776f756c64206661696c2e0a2020202020202020616363657373284f776e6572207c205769746864726177290a202020202020202066756e2077697468647261772862616c616e63653a2042616c616e6365293a2040466c6f77546f6b656e2e5661756c74207b0a20202020202020202020202069662062616c616e63652e69735a65726f2829207b0a2020202020202020202020202020202070616e6963282263616c6c696e672077697468647261772066756e6374696f6e2077697468207a65726f2062616c616e6365206973206e6f7420616c6c6f77656422290a2020202020202020202020207d0a2020202020202020202020206c6574207661756c74203c2d20496e7465726e616c45564d2e7769746864726177280a2020202020202020202020202020202066726f6d3a2073656c662e6164647265737342797465732c0a20202020202020202020202020202020616d6f756e743a2062616c616e63652e6174746f666c6f770a20202020202020202020202029206173212040466c6f77546f6b656e2e5661756c740a202020202020202020202020656d697420464c4f57546f6b656e7357697468647261776e286164647265737342797465733a2073656c662e6164647265737342797465732c20616d6f756e743a2062616c616e63652e696e464c4f572829290a20202020202020202020202072657475726e203c2d7661756c740a20202020202020207d0a0a20202020202020202f2f2f204465706c6f7973206120636f6e747261637420746f207468652045564d20656e7669726f6e6d656e742e0a20202020202020202f2f2f2052657475726e73207468652061646472657373206f6620746865206e65776c79206465706c6f79656420636f6e74726163740a2020202020202020616363657373284f776e6572207c204465706c6f79290a202020202020202066756e206465706c6f79280a202020202020202020202020636f64653a205b55496e74385d2c0a2020202020202020202020206761734c696d69743a2055496e7436342c0a20202020202020202020202076616c75653a2042616c616e63650a2020202020202020293a2045564d41646472657373207b0a2020202020202020202020206c657420616464726573734279746573203d20496e7465726e616c45564d2e6465706c6f79280a2020202020202020202020202020202066726f6d3a2073656c662e6164647265737342797465732c0a20202020202020202020202020202020636f64653a20636f64652c0a202020202020202020202020202020206761734c696d69743a206761734c696d69742c0a2020202020202020202020202020202076616c75653a2076616c75652e6174746f666c6f770a202020202020202020202020290a20202020202020202020202072657475726e2045564d416464726573732862797465733a20616464726573734279746573290a20202020202020207d0a0a20202020202020202f2f2f2043616c6c7320612066756e6374696f6e20776974682074686520676976656e20646174612e0a20202020202020202f2f2f2054686520657865637574696f6e206973206c696d697465642062792074686520676976656e20616d6f756e74206f66206761730a2020202020202020616363657373284f776e6572207c2043616c6c290a202020202020202066756e2063616c6c280a202020202020202020202020746f3a2045564d416464726573732c0a202020202020202020202020646174613a205b55496e74385d2c0a2020202020202020202020206761734c696d69743a2055496e7436342c0a20202020202020202020202076616c75653a2042616c616e63650a2020202020202020293a20526573756c74207b0a20202020202020202020202072657475726e20496e7465726e616c45564d2e63616c6c280a2020202020202020202020202020202066726f6d3a2073656c662e6164647265737342797465732c0a20202020202020202020202020202020746f3a20746f2e62797465732c0a20202020202020202020202020202020646174613a20646174612c0a202020202020202020202020202020206761734c696d69743a206761734c696d69742c0a2020202020202020202020202020202076616c75653a2076616c75652e6174746f666c6f770a202020202020202020202020292061732120526573756c740a20202020202020207d0a0a20202020202020202f2f2f20427269646765732074686520676976656e204e465420746f207468652045564d20656e7669726f6e6d656e742c20726571756972696e6720612050726f76696465722066726f6d20776869636820746f20776974686472617720612066656520746f2066756c66696c6c0a20202020202020202f2f2f207468652062726964676520726571756573740a202020202020202061636365737328616c6c290a202020202020202066756e206465706f7369744e4654280a2020202020202020202020206e66743a20407b4e6f6e46756e6769626c65546f6b656e2e4e46547d2c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a202020202020202029207b0a20202020202020202020202045564d2e626f72726f774272696467654163636573736f7228292e6465706f7369744e4654286e66743a203c2d6e66742c20746f3a2073656c662e6164647265737328292c2066656550726f76696465723a2066656550726f7669646572290a20202020202020207d0a0a20202020202020202f2f2f20427269646765732074686520676976656e204e46542066726f6d207468652045564d20656e7669726f6e6d656e742c20726571756972696e6720612050726f76696465722066726f6d20776869636820746f20776974686472617720612066656520746f2066756c66696c6c0a20202020202020202f2f2f207468652062726964676520726571756573742e204e6f74653a207468652063616c6c65722073686f756c64206f776e2074686520726571756573746564204e465420696e2045564d0a2020202020202020616363657373284f776e6572207c20427269646765290a202020202020202066756e2077697468647261774e4654280a202020202020202020202020747970653a20547970652c0a20202020202020202020202069643a2055496e743235362c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020293a20407b4e6f6e46756e6769626c65546f6b656e2e4e46547d207b0a20202020202020202020202072657475726e203c2d2045564d2e626f72726f774272696467654163636573736f7228292e77697468647261774e4654280a2020202020202020202020202020202063616c6c65723a202673656c6620617320617574682843616c6c292026436164656e63654f776e65644163636f756e742c0a20202020202020202020202020202020747970653a20747970652c0a2020202020202020202020202020202069643a2069642c0a2020202020202020202020202020202066656550726f76696465723a2066656550726f76696465720a202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f2f20427269646765732074686520676976656e205661756c7420746f207468652045564d20656e7669726f6e6d656e742c20726571756972696e6720612050726f76696465722066726f6d20776869636820746f20776974686472617720612066656520746f2066756c66696c6c0a20202020202020202f2f2f207468652062726964676520726571756573740a202020202020202061636365737328616c6c290a202020202020202066756e206465706f736974546f6b656e73280a2020202020202020202020207661756c743a20407b46756e6769626c65546f6b656e2e5661756c747d2c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a202020202020202029207b0a20202020202020202020202045564d2e626f72726f774272696467654163636573736f7228292e6465706f736974546f6b656e73287661756c743a203c2d7661756c742c20746f3a2073656c662e6164647265737328292c2066656550726f76696465723a2066656550726f7669646572290a20202020202020207d0a0a20202020202020202f2f2f20427269646765732074686520676976656e2066756e6769626c6520746f6b656e732066726f6d207468652045564d20656e7669726f6e6d656e742c20726571756972696e6720612050726f76696465722066726f6d20776869636820746f20776974686472617720610a20202020202020202f2f2f2066656520746f2066756c66696c6c207468652062726964676520726571756573742e204e6f74653a207468652063616c6c65722073686f756c64206f776e207468652072657175657374656420746f6b656e7320262073756666696369656e742062616c616e6365206f660a20202020202020202f2f2f2072657175657374656420746f6b656e7320696e2045564d0a2020202020202020616363657373284f776e6572207c20427269646765290a202020202020202066756e207769746864726177546f6b656e73280a202020202020202020202020747970653a20547970652c0a202020202020202020202020616d6f756e743a2055496e743235362c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020293a20407b46756e6769626c65546f6b656e2e5661756c747d207b0a20202020202020202020202072657475726e203c2d2045564d2e626f72726f774272696467654163636573736f7228292e7769746864726177546f6b656e73280a2020202020202020202020202020202063616c6c65723a202673656c6620617320617574682843616c6c292026436164656e63654f776e65644163636f756e742c0a20202020202020202020202020202020747970653a20747970652c0a20202020202020202020202020202020616d6f756e743a20616d6f756e742c0a2020202020202020202020202020202066656550726f76696465723a2066656550726f76696465720a202020202020202020202020290a20202020202020207d0a202020207d0a0a202020202f2f2f20437265617465732061206e657720636164656e6365206f776e6564206163636f756e740a2020202061636365737328616c6c290a2020202066756e20637265617465436164656e63654f776e65644163636f756e7428293a2040436164656e63654f776e65644163636f756e74207b0a20202020202020206c657420616363203c2d63726561746520436164656e63654f776e65644163636f756e7428290a20202020202020206c65742061646472203d20496e7465726e616c45564d2e637265617465436164656e63654f776e65644163636f756e7428757569643a206163632e75756964290a20202020202020206163632e696e697441646472657373286164647265737342797465733a2061646472290a2020202020202020656d697420436164656e63654f776e65644163636f756e7443726561746564286164647265737342797465733a2061646472290a202020202020202072657475726e203c2d6163630a202020207d0a0a202020202f2f2f2052756e7320616e206120524c502d656e636f6465642045564d207472616e73616374696f6e2c2064656475637473207468652067617320666565732c0a202020202f2f2f20616e64206465706f736974732074686520676173206665657320696e746f207468652070726f766964656420636f696e6261736520616464726573732e0a2020202061636365737328616c6c290a2020202066756e2072756e2874783a205b55496e74385d2c20636f696e626173653a2045564d41646472657373293a20526573756c74207b0a202020202020202072657475726e20496e7465726e616c45564d2e72756e280a2020202020202020202020202020202074783a2074782c0a20202020202020202020202020202020636f696e626173653a20636f696e626173652e62797465730a2020202020202020292061732120526573756c740a202020207d0a0a202020202f2f2f206d75737452756e2072756e7320746865207472616e73616374696f6e207573696e672045564d2e72756e207965742069740a202020202f2f2f20726f6c6c6261636b2069662074686520747820657865637574696f6e2073746174757320697320756e6b6e6f776e206f7220696e76616c69642e0a202020202f2f2f204e6f746520746861742074686973206d6574686f6420646f6573206e6f7420726f6c6c6261636b206966207472616e73616374696f6e0a202020202f2f2f2069732065786563757465642062757420616e20766d206572726f72206973207265706f7274656420617320746865206f7574636f6d650a202020202f2f2f206f662074686520657865637574696f6e20287374617475733a206661696c6564292e0a2020202061636365737328616c6c290a2020202066756e206d75737452756e2874783a205b55496e74385d2c20636f696e626173653a2045564d41646472657373293a20526573756c74207b0a20202020202020206c65742072756e526573756c74203d2073656c662e72756e2874783a2074782c20636f696e626173653a20636f696e62617365290a2020202020202020617373657274280a20202020202020202020202072756e526573756c742e737461747573203d3d205374617475732e6661696c6564207c7c2072756e526573756c742e737461747573203d3d205374617475732e7375636365737366756c2c0a2020202020202020202020206d6573736167653a20227478206973206e6f742076616c696420666f7220657865637574696f6e220a2020202020202020290a202020202020202072657475726e2072756e526573756c740a202020207d0a0a2020202061636365737328616c6c290a2020202066756e20656e636f6465414249285f2076616c7565733a205b416e795374727563745d293a205b55496e74385d207b0a202020202020202072657475726e20496e7465726e616c45564d2e656e636f64654142492876616c756573290a202020207d0a0a2020202061636365737328616c6c290a2020202066756e206465636f64654142492874797065733a205b547970655d2c20646174613a205b55496e74385d293a205b416e795374727563745d207b0a202020202020202072657475726e20496e7465726e616c45564d2e6465636f64654142492874797065733a2074797065732c20646174613a2064617461290a202020207d0a0a2020202061636365737328616c6c290a2020202066756e20656e636f6465414249576974685369676e6174757265280a20202020202020205f207369676e61747572653a20537472696e672c0a20202020202020205f2076616c7565733a205b416e795374727563745d0a20202020293a205b55496e74385d207b0a20202020202020206c6574206d6574686f644944203d2048617368416c676f726974686d2e4b454343414b5f3235362e68617368280a2020202020202020202020207369676e61747572652e757466380a2020202020202020292e736c6963652866726f6d3a20302c207570546f3a2034290a20202020202020206c657420617267756d656e7473203d20496e7465726e616c45564d2e656e636f64654142492876616c756573290a0a202020202020202072657475726e206d6574686f6449442e636f6e63617428617267756d656e7473290a202020207d0a0a2020202061636365737328616c6c290a2020202066756e206465636f6465414249576974685369676e6174757265280a20202020202020205f207369676e61747572653a20537472696e672c0a202020202020202074797065733a205b547970655d2c0a2020202020202020646174613a205b55496e74385d0a20202020293a205b416e795374727563745d207b0a20202020202020206c6574206d6574686f644944203d2048617368416c676f726974686d2e4b454343414b5f3235362e68617368280a2020202020202020202020207369676e61747572652e757466380a2020202020202020292e736c6963652866726f6d3a20302c207570546f3a2034290a0a2020202020202020666f72206279746520696e206d6574686f644944207b0a2020202020202020202020206966206279746520213d20646174612e72656d6f766546697273742829207b0a2020202020202020202020202020202070616e696328227369676e6174757265206d69736d6174636822290a2020202020202020202020207d0a20202020202020207d0a0a202020202020202072657475726e20496e7465726e616c45564d2e6465636f64654142492874797065733a2074797065732c20646174613a2064617461290a202020207d0a0a202020202f2f2f2056616c69646174696f6e526573756c742072657475726e732074686520726573756c74206f6620434f41206f776e6572736869702070726f6f662076616c69646174696f6e0a2020202061636365737328616c6c290a202020207374727563742056616c69646174696f6e526573756c74207b0a202020202020202061636365737328616c6c290a20202020202020206c657420697356616c69643a20426f6f6c0a0a202020202020202061636365737328616c6c290a20202020202020206c65742070726f626c656d3a20537472696e673f0a0a2020202020202020696e697428697356616c69643a20426f6f6c2c2070726f626c656d3a20537472696e673f29207b0a20202020202020202020202073656c662e697356616c6964203d20697356616c69640a20202020202020202020202073656c662e70726f626c656d203d2070726f626c656d0a20202020202020207d0a202020207d0a0a202020202f2f2f2076616c6964617465434f414f776e65727368697050726f6f662076616c696461746573206120434f41206f776e6572736869702070726f6f660a2020202061636365737328616c6c290a2020202066756e2076616c6964617465434f414f776e65727368697050726f6f66280a2020202020202020616464726573733a20416464726573732c0a2020202020202020706174683a205075626c6963506174682c0a20202020202020207369676e6564446174613a205b55496e74385d2c0a20202020202020206b6579496e64696365733a205b55496e7436345d2c0a20202020202020207369676e6174757265733a205b5b55496e74385d5d2c0a202020202020202065766d416464726573733a205b55496e74383b2032305d0a20202020293a2056616c69646174696f6e526573756c74207b0a0a20202020202020202f2f206d616b65207369676e6174757265207365742066697273740a20202020202020202f2f20636865636b206e756d626572206f66207369676e617475726573206d617463686573206e756d626572206f66206b657920696e64696365730a20202020202020206966206b6579496e64696365732e6c656e67746820213d207369676e6174757265732e6c656e677468207b0a20202020202020202020202072657475726e2056616c69646174696f6e526573756c74280a20202020202020202020202020202020697356616c69643a2066616c73652c0a2020202020202020202020202020202070726f626c656d3a20226b657920696e64696365732073697a6520646f65736e2774206d6174636820746865207369676e617475726573220a202020202020202020202020290a20202020202020207d0a0a2020202020202020766172207369676e61747572655365743a205b43727970746f2e4b65794c6973745369676e61747572655d203d205b5d0a2020202020202020666f72207369676e6174757265496e6465782c207369676e617475726520696e207369676e6174757265737b0a2020202020202020202020207369676e61747572655365742e617070656e642843727970746f2e4b65794c6973745369676e6174757265280a202020202020202020202020202020206b6579496e6465783a20496e74286b6579496e64696365735b7369676e6174757265496e6465785d292c0a202020202020202020202020202020207369676e61747572653a207369676e61747572650a20202020202020202020202029290a20202020202020207d0a0a20202020202020202f2f206665746368206163636f756e740a20202020202020206c657420616363203d206765744163636f756e742861646472657373290a0a20202020202020202f2f20636f6e737472756374696e67206b6579206c6973740a20202020202020206c6574206b65794c697374203d2043727970746f2e4b65794c69737428290a2020202020202020666f72207369676e617475726520696e207369676e6174757265536574207b0a2020202020202020202020206c6574206b6579203d206163632e6b6579732e676574286b6579496e6465783a207369676e61747572652e6b6579496e64657829210a20202020202020202020202061737365727428216b65792e69735265766f6b65642c206d6573736167653a20227265766f6b6564206b6579206973207573656422290a2020202020202020202020206b65794c6973742e616464280a20202020202020202020202020206b65792e7075626c69634b65792c0a202020202020202020202020202068617368416c676f726974686d3a206b65792e68617368416c676f726974686d2c0a20202020202020202020202020207765696768743a206b65792e7765696768742c0a2020202020202020202020290a20202020202020207d0a0a20202020202020206c657420697356616c6964203d206b65794c6973742e766572696679280a2020202020202020202020207369676e61747572655365743a207369676e61747572655365742c0a2020202020202020202020207369676e6564446174613a207369676e6564446174612c0a202020202020202020202020646f6d61696e53657061726174696f6e5461673a2022464c4f572d56302e302d75736572220a2020202020202020290a0a202020202020202069662021697356616c69647b0a20202020202020202020202072657475726e2056616c69646174696f6e526573756c74280a20202020202020202020202020202020697356616c69643a2066616c73652c0a2020202020202020202020202020202070726f626c656d3a202274686520676976656e207369676e61747572657320617265206e6f742076616c6964206f722070726f7669646520656e6f75676820776569676874220a202020202020202020202020290a20202020202020207d0a0a20202020202020206c657420636f61526566203d206163632e6361706162696c69746965732e626f72726f773c2645564d2e436164656e63654f776e65644163636f756e743e2870617468290a2020202020202020696620636f61526566203d3d206e696c207b0a2020202020202020202020202072657475726e2056616c69646174696f6e526573756c74280a2020202020202020202020202020202020697356616c69643a2066616c73652c0a202020202020202020202020202020202070726f626c656d3a2022636f756c64206e6f7420626f72726f7720627269646765206163636f756e742773207265736f75726365220a20202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f207665726966792065766d2061646472657373206d61746368696e670a20202020202020207661722061646472203d20636f61526566212e6164647265737328290a2020202020202020666f7220696e6465782c206974656d20696e20636f61526566212e6164647265737328292e6279746573207b0a2020202020202020202020206966206974656d20213d2065766d416464726573735b696e6465785d207b0a2020202020202020202020202020202072657475726e2056616c69646174696f6e526573756c74280a2020202020202020202020202020202020202020697356616c69643a2066616c73652c0a202020202020202020202020202020202020202070726f626c656d3a202265766d2061646472657373206d69736d61746368220a20202020202020202020202020202020290a2020202020202020202020207d0a20202020202020207d0a0a202020202020202072657475726e2056616c69646174696f6e526573756c74280a202020202020202020202020697356616c69643a20747275652c0a20202020202020202020202070726f626c656d3a206e696c0a2020202020202020290a202020207d0a0a202020202f2f2f20426c6f636b2072657475726e7320696e666f726d6174696f6e2061626f757420746865206c617465737420657865637574656420626c6f636b2e0a2020202061636365737328616c6c290a202020207374727563742045564d426c6f636b207b0a202020202020202061636365737328616c6c290a20202020202020206c6574206865696768743a2055496e7436340a0a202020202020202061636365737328616c6c290a20202020202020206c657420686173683a20537472696e670a0a202020202020202061636365737328616c6c290a20202020202020206c657420746f74616c537570706c793a20496e740a0a2020202020202020696e6974286865696768743a2055496e7436342c20686173683a20537472696e672c20746f74616c537570706c793a20496e7429207b0a20202020202020202020202073656c662e686569676874203d206865696768740a20202020202020202020202073656c662e68617368203d20686173680a20202020202020202020202073656c662e746f74616c537570706c79203d20746f74616c537570706c790a20202020202020207d0a202020207d0a0a202020202f2f2f2052657475726e7320746865206c617465737420657865637574656420626c6f636b2e0a2020202061636365737328616c6c290a2020202066756e206765744c6174657374426c6f636b28293a2045564d426c6f636b207b0a202020202020202072657475726e20496e7465726e616c45564d2e6765744c6174657374426c6f636b2829206173212045564d426c6f636b0a202020207d0a0a202020202f2f2f20496e7465726661636520666f722061207265736f75726365207768696368206163747320617320616e20656e747279706f696e7420746f2074686520564d206272696467650a2020202061636365737328616c6c290a202020207265736f7572636520696e74657266616365204272696467654163636573736f72207b0a0a20202020202020202f2f2f20456e64706f696e7420656e61626c696e6720746865206272696467696e67206f6620616e204e465420746f2045564d0a202020202020202061636365737328427269646765290a202020202020202066756e206465706f7369744e4654280a2020202020202020202020206e66743a20407b4e6f6e46756e6769626c65546f6b656e2e4e46547d2c0a202020202020202020202020746f3a2045564d416464726573732c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020290a0a20202020202020202f2f2f20456e64706f696e7420656e61626c696e6720746865206272696467696e67206f6620616e204e46542066726f6d2045564d0a202020202020202061636365737328427269646765290a202020202020202066756e2077697468647261774e4654280a20202020202020202020202063616c6c65723a20617574682843616c6c292026436164656e63654f776e65644163636f756e742c0a202020202020202020202020747970653a20547970652c0a20202020202020202020202069643a2055496e743235362c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020293a20407b4e6f6e46756e6769626c65546f6b656e2e4e46547d0a0a20202020202020202f2f2f20456e64706f696e7420656e61626c696e6720746865206272696467696e67206f6620612066756e6769626c6520746f6b656e207661756c7420746f2045564d0a202020202020202061636365737328427269646765290a202020202020202066756e206465706f736974546f6b656e73280a2020202020202020202020207661756c743a20407b46756e6769626c65546f6b656e2e5661756c747d2c0a202020202020202020202020746f3a2045564d416464726573732c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020290a0a20202020202020202f2f2f20456e64706f696e7420656e61626c696e6720746865206272696467696e67206f662066756e6769626c6520746f6b656e732066726f6d2045564d0a202020202020202061636365737328427269646765290a202020202020202066756e207769746864726177546f6b656e73280a20202020202020202020202063616c6c65723a20617574682843616c6c292026436164656e63654f776e65644163636f756e742c0a202020202020202020202020747970653a20547970652c0a202020202020202020202020616d6f756e743a2055496e743235362c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020293a20407b46756e6769626c65546f6b656e2e5661756c747d0a202020207d0a0a202020202f2f2f20496e746572666163652077686963682063617074757265732061204361706162696c69747920746f2074686520627269646765204163636573736f722c20736176696e672069742077697468696e2074686520427269646765526f75746572207265736f757263650a2020202061636365737328616c6c290a202020207265736f7572636520696e7465726661636520427269646765526f75746572207b0a0a20202020202020202f2f2f2052657475726e732061207265666572656e636520746f20746865204272696467654163636573736f722064657369676e6174656420666f7220696e7465726e616c206272696467652072657175657374730a2020202020202020616363657373284272696467652920766965772066756e20626f72726f774272696467654163636573736f7228293a2061757468284272696467652920267b4272696467654163636573736f727d0a0a20202020202020202f2f2f205365747320746865204272696467654163636573736f72204361706162696c69747920696e2074686520427269646765526f757465720a202020202020202061636365737328427269646765292066756e207365744272696467654163636573736f72285f206163636573736f723a204361706162696c6974793c61757468284272696467652920267b4272696467654163636573736f727d3e29207b0a202020202020202020202020707265207b0a202020202020202020202020202020206163636573736f722e636865636b28293a2022496e76616c6964204272696467654163636573736f72204361706162696c6974792070726f7669646564220a20202020202020202020202020202020656d6974204272696467654163636573736f7255706461746564280a2020202020202020202020202020202020202020726f75746572547970653a2073656c662e6765745479706528292c0a2020202020202020202020202020202020202020726f75746572555549443a2073656c662e757569642c0a2020202020202020202020202020202020202020726f75746572416464726573733a2073656c662e6f776e65723f2e61646472657373203f3f2070616e69632822526f75746572206d757374206861766520616e206f776e657220746f206265206964656e74696669656422292c0a20202020202020202020202020202020202020206163636573736f72547970653a206163636573736f722e626f72726f772829212e6765745479706528292c0a20202020202020202020202020202020202020206163636573736f72555549443a206163636573736f722e626f72726f772829212e757569642c0a20202020202020202020202020202020202020206163636573736f72416464726573733a206163636573736f722e616464726573730a20202020202020202020202020202020290a2020202020202020202020207d0a20202020202020207d0a202020207d0a0a202020202f2f2f2052657475726e732061207265666572656e636520746f20746865204272696467654163636573736f722064657369676e6174656420666f7220696e7465726e616c206272696467652072657175657374730a202020206163636573732873656c66290a20202020766965772066756e20626f72726f774272696467654163636573736f7228293a2061757468284272696467652920267b4272696467654163636573736f727d207b0a202020202020202072657475726e2073656c662e6163636f756e742e73746f726167652e626f72726f773c61757468284272696467652920267b427269646765526f757465727d3e2866726f6d3a202f73746f726167652f65766d427269646765526f75746572290a2020202020202020202020203f2e626f72726f774272696467654163636573736f7228290a2020202020202020202020203f3f2070616e69632822436f756c64206e6f7420626f72726f77207265666572656e636520746f207468652045564d2062726964676522290a202020207d0a7d0a" access(all) let compiledFactoryBytecode = "608060405234801561001057600080fd5b50338061003757604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b61004081610046565b50610096565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61484a806100a56000396000f3fe60806040523480156200001157600080fd5b5060043610620000c35760003560e01c8063715018a6116200007a578063715018a6146200018f5780638da5cb5b146200019b578063d56e0ccf14620001ad578063daa09e5414620001e4578063f2fde38b14620001fb578063f93241dd146200021257600080fd5b806304433bbc14620000c85780630a2c0ce914620000fc578063263e0c1b1462000122578063335f4c76146200014a57806340f8d42b146200016157806361a169051462000178575b600080fd5b620000df620000d936600462000c0a565b62000229565b6040516001600160a01b0390911681526020015b60405180910390f35b620001136200010d36600462000c4b565b6200025c565b604051620000f3919062000cd1565b620001396200013336600462000c4b565b62000310565b6040519015158152602001620000f3565b620001396200015b36600462000c4b565b62000724565b620000df6200017236600462000ce6565b62000752565b620000df6200018936600462000ce6565b62000853565b6200019962000942565b005b6000546001600160a01b0316620000df565b620000df620001be36600462000c0a565b80516020818301810180516001825292820191909301209152546001600160a01b031681565b62000139620001f536600462000c4b565b6200095a565b620001996200020c36600462000c4b565b620009da565b620001136200022336600462000c4b565b62000a22565b60006001826040516200023d919062000dc8565b908152604051908190036020019020546001600160a01b031692915050565b6001600160a01b0381166000908152600260205260409020805460609190620002859062000de6565b80601f0160208091040260200160405190810160405280929190818152602001828054620002b39062000de6565b8015620003045780601f10620002d85761010080835404028352916020019162000304565b820191906000526020600020905b815481529060010190602001808311620002e657829003601f168201915b50505050509050919050565b60408051600481526024810182526020810180516001600160e01b03166318160ddd60e01b1790529051600091829182916001600160a01b0386169162000358919062000dc8565b600060405180830381855afa9150503d806000811462000395576040519150601f19603f3d011682016040523d82523d6000602084013e6200039a565b606091505b5091509150811580620003ac57508051155b15620003bc575060009392505050565b604051600060248201526001600160a01b0385169060440160408051601f198184030181529181526020820180516001600160e01b03166370a0823160e01b179052516200040b919062000dc8565b600060405180830381855afa9150503d806000811462000448576040519150601f19603f3d011682016040523d82523d6000602084013e6200044d565b606091505b5090925090508115806200046057508051155b1562000470575060009392505050565b60405160006024820181905260448201526001600160a01b0385169060640160408051601f198184030181529181526020820180516001600160e01b0316636eb1769f60e11b17905251620004c6919062000dc8565b600060405180830381855afa9150503d806000811462000503576040519150601f19603f3d011682016040523d82523d6000602084013e62000508565b606091505b5090925090508115806200051b57508051155b156200052b575060009392505050565b60408051600481526024810182526020810180516001600160e01b03166306fdde0360e01b17905290516001600160a01b038616916200056b9162000dc8565b600060405180830381855afa9150503d8060008114620005a8576040519150601f19603f3d011682016040523d82523d6000602084013e620005ad565b606091505b509092509050811580620005c057508051155b15620005d0575060009392505050565b60408051600481526024810182526020810180516001600160e01b03166395d89b4160e01b17905290516001600160a01b03861691620006109162000dc8565b600060405180830381855afa9150503d80600081146200064d576040519150601f19603f3d011682016040523d82523d6000602084013e62000652565b606091505b5090925090508115806200066557508051155b1562000675575060009392505050565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290516001600160a01b03861691620006b59162000dc8565b600060405180830381855afa9150503d8060008114620006f2576040519150601f19603f3d011682016040523d82523d6000602084013e620006f7565b606091505b5090925090508115806200070a57508051155b156200071a575060009392505050565b5060019392505050565b6001600160a01b03811660009081526002602052604081208054620007499062000de6565b15159392505050565b60006200075e62000ac4565b600080546001600160a01b031687878787876040516200077e9062000b43565b6200078f9695949392919062000e22565b604051809103906000f080158015620007ac573d6000803e3d6000fd5b50905080600185604051620007c2919062000dc8565b908152604080516020928190038301902080546001600160a01b0319166001600160a01b03948516179055918316600090815260029091522062000807858262000f00565b507f99a64021330f1af36b3fd5f64a1d12b99b8ddf91fa553618c4df01ffba4c1cee81888888886040516200084195949392919062000fcd565b60405180910390a19695505050505050565b60006200085f62000ac4565b600080546001600160a01b031687878787876040516200087f9062000b51565b620008909695949392919062000e22565b604051809103906000f080158015620008ad573d6000803e3d6000fd5b50905080600185604051620008c3919062000dc8565b908152604080516020928190038301902080546001600160a01b0319166001600160a01b03948516179055918316600090815260029091522062000908858262000f00565b507fbebce54951ebf20c0dcd195a45bb2388d9ac8e38b5974e00bb63c5822dbe65f081888888886040516200084195949392919062000fcd565b6200094c62000ac4565b62000958600062000af3565b565b6040516301ffc9a760e01b81526380ac58cd60e01b60048201526000906001600160a01b038316906301ffc9a790602401602060405180830381865afa925050508015620009c7575060408051601f3d908101601f19168201909252620009c4918101906200103f565b60015b620009d457506000919050565b92915050565b620009e462000ac4565b6001600160a01b03811662000a1457604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b62000a1f8162000af3565b50565b6002602052600090815260409020805462000a3d9062000de6565b80601f016020809104026020016040519081016040528092919081815260200182805462000a6b9062000de6565b801562000abc5780601f1062000a905761010080835404028352916020019162000abc565b820191906000526020600020905b81548152906001019060200180831162000a9e57829003601f168201915b505050505081565b6000546001600160a01b03163314620009585760405163118cdaa760e01b815233600482015260240162000a0b565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6118fa806200106483390190565b611eb7806200295e83390190565b634e487b7160e01b600052604160045260246000fd5b600082601f83011262000b8757600080fd5b813567ffffffffffffffff8082111562000ba55762000ba562000b5f565b604051601f8301601f19908116603f0116810190828211818310171562000bd05762000bd062000b5f565b8160405283815286602085880101111562000bea57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121562000c1d57600080fd5b813567ffffffffffffffff81111562000c3557600080fd5b62000c438482850162000b75565b949350505050565b60006020828403121562000c5e57600080fd5b81356001600160a01b038116811462000c7657600080fd5b9392505050565b60005b8381101562000c9a57818101518382015260200162000c80565b50506000910152565b6000815180845262000cbd81602086016020860162000c7d565b601f01601f19169290920160200192915050565b60208152600062000c76602083018462000ca3565b600080600080600060a0868803121562000cff57600080fd5b853567ffffffffffffffff8082111562000d1857600080fd5b62000d2689838a0162000b75565b9650602088013591508082111562000d3d57600080fd5b62000d4b89838a0162000b75565b9550604088013591508082111562000d6257600080fd5b62000d7089838a0162000b75565b9450606088013591508082111562000d8757600080fd5b62000d9589838a0162000b75565b9350608088013591508082111562000dac57600080fd5b5062000dbb8882890162000b75565b9150509295509295909350565b6000825162000ddc81846020870162000c7d565b9190910192915050565b600181811c9082168062000dfb57607f821691505b60208210810362000e1c57634e487b7160e01b600052602260045260246000fd5b50919050565b6001600160a01b038716815260c06020820181905260009062000e489083018862000ca3565b828103604084015262000e5c818862000ca3565b9050828103606084015262000e72818762000ca3565b9050828103608084015262000e88818662000ca3565b905082810360a084015262000e9e818562000ca3565b9998505050505050505050565b601f82111562000efb576000816000526020600020601f850160051c8101602086101562000ed65750805b601f850160051c820191505b8181101562000ef75782815560010162000ee2565b5050505b505050565b815167ffffffffffffffff81111562000f1d5762000f1d62000b5f565b62000f358162000f2e845462000de6565b8462000eab565b602080601f83116001811462000f6d576000841562000f545750858301515b600019600386901b1c1916600185901b17855562000ef7565b600085815260208120601f198616915b8281101562000f9e5788860151825594840194600190910190840162000f7d565b508582101562000fbd5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b038616815260a06020820181905260009062000ff39083018762000ca3565b828103604084015262001007818762000ca3565b905082810360608401526200101d818662000ca3565b9050828103608084015262001033818562000ca3565b98975050505050505050565b6000602082840312156200105257600080fd5b8151801515811462000c7657600080fdfe6101606040523480156200001257600080fd5b50604051620018fa380380620018fa833981016040819052620000359162000357565b6040805180820190915260018152603160f81b6020820152859081908882886003620000628382620004db565b506004620000718282620004db565b5050506001600160a01b038116620000a457604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b620000af816200019f565b50620000bd826006620001f1565b61012052620000ce816007620001f1565b61014052815160208084019190912060e052815190820120610100524660a0526200015c60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c052506009620001748482620004db565b50600a620001838382620004db565b50600b620001928282620004db565b5050505050505062000601565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000602083511015620002115762000209836200022a565b905062000224565b816200021e8482620004db565b5060ff90505b92915050565b600080829050601f8151111562000258578260405163305a27a960e01b81526004016200009b9190620005a7565b80516200026582620005dc565b179392505050565b80516001600160a01b03811681146200028557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b83811015620002bd578181015183820152602001620002a3565b50506000910152565b600082601f830112620002d857600080fd5b81516001600160401b0380821115620002f557620002f56200028a565b604051601f8301601f19908116603f011681019082821181831017156200032057620003206200028a565b816040528381528660208588010111156200033a57600080fd5b6200034d846020830160208901620002a0565b9695505050505050565b60008060008060008060c087890312156200037157600080fd5b6200037c876200026d565b60208801519096506001600160401b03808211156200039a57600080fd5b620003a88a838b01620002c6565b96506040890151915080821115620003bf57600080fd5b620003cd8a838b01620002c6565b95506060890151915080821115620003e457600080fd5b620003f28a838b01620002c6565b945060808901519150808211156200040957600080fd5b620004178a838b01620002c6565b935060a08901519150808211156200042e57600080fd5b506200043d89828a01620002c6565b9150509295509295509295565b600181811c908216806200045f57607f821691505b6020821081036200048057634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620004d6576000816000526020600020601f850160051c81016020861015620004b15750805b601f850160051c820191505b81811015620004d257828155600101620004bd565b5050505b505050565b81516001600160401b03811115620004f757620004f76200028a565b6200050f816200050884546200044a565b8462000486565b602080601f8311600181146200054757600084156200052e5750858301515b600019600386901b1c1916600185901b178555620004d2565b600085815260208120601f198616915b82811015620005785788860151825594840194600190910190840162000557565b5085821015620005975787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6020815260008251806020840152620005c8816040850160208701620002a0565b601f01601f19169190910160400192915050565b80516020808301519190811015620004805760001960209190910360031b1b16919050565b60805160a05160c05160e05161010051610120516101405161129e6200065c6000396000610a8801526000610a5b01526000610918015260006108f00152600061084b015260006108750152600061089f015261129e6000f3fe608060405234801561001057600080fd5b50600436106101735760003560e01c8063715018a6116100de578063a76b4d5611610097578063dc9716eb11610071578063dc9716eb146102f8578063dd62ed3e14610300578063e8a3d48514610339578063f2fde38b1461034157600080fd5b8063a76b4d56146102ca578063a9059cbb146102d2578063d505accf146102e557600080fd5b8063715018a61461025e57806379cc6790146102665780637ecebe001461027957806384b0196e1461028c5780638da5cb5b146102a757806395d89b41146102c257600080fd5b8063313ce56711610130578063313ce567146101ee5780633644e515146101fd5780633fd4d4a81461020557806340c10f191461020d57806342966c681461022257806370a082311461023557600080fd5b806306fdde0314610178578063095ea7b3146101965780630cd9acb7146101b9578063120a88ad146101c157806318160ddd146101c957806323b872dd146101db575b600080fd5b610180610354565b60405161018d9190610fe8565b60405180910390f35b6101a96101a436600461101e565b6103e6565b604051901515815260200161018d565b610180610400565b61018061048e565b6002545b60405190815260200161018d565b6101a96101e9366004611048565b61049d565b6040516012815260200161018d565b6101cd6104c1565b6101806104d0565b61022061021b36600461101e565b6104df565b005b610220610230366004611084565b6104f5565b6101cd61024336600461109d565b6001600160a01b031660009081526020819052604090205490565b610220610502565b61022061027436600461101e565b610516565b6101cd61028736600461109d565b61052b565b610294610549565b60405161018d97969594939291906110b8565b6005546040516001600160a01b03909116815260200161018d565b61018061058f565b61018061059e565b6101a96102e036600461101e565b6105ab565b6102206102f3366004611151565b6105b9565b6101806106f8565b6101cd61030e3660046111c4565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b610180610705565b61022061034f36600461109d565b610714565b606060038054610363906111f7565b80601f016020809104026020016040519081016040528092919081815260200182805461038f906111f7565b80156103dc5780601f106103b1576101008083540402835291602001916103dc565b820191906000526020600020905b8154815290600101906020018083116103bf57829003601f168201915b5050505050905090565b6000336103f481858561074f565b60019150505b92915050565b6009805461040d906111f7565b80601f0160208091040260200160405190810160405280929190818152602001828054610439906111f7565b80156104865780601f1061045b57610100808354040283529160200191610486565b820191906000526020600020905b81548152906001019060200180831161046957829003601f168201915b505050505081565b606060098054610363906111f7565b6000336104ab858285610761565b6104b68585856107df565b506001949350505050565b60006104cb61083e565b905090565b6060600a8054610363906111f7565b6104e7610969565b6104f18282610996565b5050565b6104ff33826109cc565b50565b61050a610969565b6105146000610a02565b565b610521823383610761565b6104f182826109cc565b6001600160a01b0381166000908152600860205260408120546103fa565b60006060806000806000606061055d610a54565b610565610a81565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b606060048054610363906111f7565b600b805461040d906111f7565b6000336103f48185856107df565b834211156105e25760405163313c898160e11b8152600481018590526024015b60405180910390fd5b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988888861062f8c6001600160a01b0316600090815260086020526040902080546001810190915590565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061068a82610aae565b9050600061069a82878787610adb565b9050896001600160a01b0316816001600160a01b0316146106e1576040516325c0072360e11b81526001600160a01b0380831660048301528b1660248201526044016105d9565b6106ec8a8a8a61074f565b50505050505050505050565b600a805461040d906111f7565b6060600b8054610363906111f7565b61071c610969565b6001600160a01b03811661074657604051631e4fbdf760e01b8152600060048201526024016105d9565b6104ff81610a02565b61075c8383836001610b09565b505050565b6001600160a01b0383811660009081526001602090815260408083209386168352929052205460001981146107d957818110156107ca57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016105d9565b6107d984848484036000610b09565b50505050565b6001600160a01b03831661080957604051634b637e8f60e11b8152600060048201526024016105d9565b6001600160a01b0382166108335760405163ec442f0560e01b8152600060048201526024016105d9565b61075c838383610bde565b6000306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614801561089757507f000000000000000000000000000000000000000000000000000000000000000046145b156108c157507f000000000000000000000000000000000000000000000000000000000000000090565b6104cb604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6005546001600160a01b031633146105145760405163118cdaa760e01b81523360048201526024016105d9565b6001600160a01b0382166109c05760405163ec442f0560e01b8152600060048201526024016105d9565b6104f160008383610bde565b6001600160a01b0382166109f657604051634b637e8f60e11b8152600060048201526024016105d9565b6104f182600083610bde565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60606104cb7f00000000000000000000000000000000000000000000000000000000000000006006610d08565b60606104cb7f00000000000000000000000000000000000000000000000000000000000000006007610d08565b60006103fa610abb61083e565b8360405161190160f01b8152600281019290925260228201526042902090565b600080600080610aed88888888610db3565b925092509250610afd8282610e82565b50909695505050505050565b6001600160a01b038416610b335760405163e602df0560e01b8152600060048201526024016105d9565b6001600160a01b038316610b5d57604051634a1406b160e11b8152600060048201526024016105d9565b6001600160a01b03808516600090815260016020908152604080832093871683529290522082905580156107d957826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92584604051610bd091815260200190565b60405180910390a350505050565b6001600160a01b038316610c09578060026000828254610bfe9190611231565b90915550610c7b9050565b6001600160a01b03831660009081526020819052604090205481811015610c5c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016105d9565b6001600160a01b03841660009081526020819052604090209082900390555b6001600160a01b038216610c9757600280548290039055610cb6565b6001600160a01b03821660009081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610cfb91815260200190565b60405180910390a3505050565b606060ff8314610d2257610d1b83610f3b565b90506103fa565b818054610d2e906111f7565b80601f0160208091040260200160405190810160405280929190818152602001828054610d5a906111f7565b8015610da75780601f10610d7c57610100808354040283529160200191610da7565b820191906000526020600020905b815481529060010190602001808311610d8a57829003601f168201915b505050505090506103fa565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115610dee5750600091506003905082610e78565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015610e42573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610e6e57506000925060019150829050610e78565b9250600091508190505b9450945094915050565b6000826003811115610e9657610e96611252565b03610e9f575050565b6001826003811115610eb357610eb3611252565b03610ed15760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115610ee557610ee5611252565b03610f065760405163fce698f760e01b8152600481018290526024016105d9565b6003826003811115610f1a57610f1a611252565b036104f1576040516335e2f38360e21b8152600481018290526024016105d9565b60606000610f4883610f7a565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b600060ff8216601f8111156103fa57604051632cd44ac360e21b815260040160405180910390fd5b6000815180845260005b81811015610fc857602081850181015186830182015201610fac565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610ffb6020830184610fa2565b9392505050565b80356001600160a01b038116811461101957600080fd5b919050565b6000806040838503121561103157600080fd5b61103a83611002565b946020939093013593505050565b60008060006060848603121561105d57600080fd5b61106684611002565b925061107460208501611002565b9150604084013590509250925092565b60006020828403121561109657600080fd5b5035919050565b6000602082840312156110af57600080fd5b610ffb82611002565b60ff60f81b881681526000602060e060208401526110d960e084018a610fa2565b83810360408501526110eb818a610fa2565b606085018990526001600160a01b038816608086015260a0850187905284810360c08601528551808252602080880193509091019060005b8181101561113f57835183529284019291840191600101611123565b50909c9b505050505050505050505050565b600080600080600080600060e0888a03121561116c57600080fd5b61117588611002565b965061118360208901611002565b95506040880135945060608801359350608088013560ff811681146111a757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156111d757600080fd5b6111e083611002565b91506111ee60208401611002565b90509250929050565b600181811c9082168061120b57607f821691505b60208210810361122b57634e487b7160e01b600052602260045260246000fd5b50919050565b808201808211156103fa57634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052602160045260246000fdfea26469706673582212200fea5736ecebd6f4f71e77a0743e7169bb21209a84517a64d4e0a6b0313817b864736f6c6343000817003360806040523480156200001157600080fd5b5060405162001eb738038062001eb7833981016040819052620000349162000202565b858585600062000045838262000386565b50600162000054828262000386565b5050506001600160a01b0381166200008657604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b6200009181620000cb565b50600c620000a0848262000386565b50600d620000af838262000386565b50600e620000be828262000386565b5050505050505062000452565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b80516001600160a01b03811681146200013557600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200016257600080fd5b81516001600160401b03808211156200017f576200017f6200013a565b604051601f8301601f19908116603f01168101908282118183101715620001aa57620001aa6200013a565b8160405283815260209250866020858801011115620001c857600080fd5b600091505b83821015620001ec5785820183015181830184015290820190620001cd565b6000602085830101528094505050505092915050565b60008060008060008060c087890312156200021c57600080fd5b62000227876200011d565b60208801519096506001600160401b03808211156200024557600080fd5b620002538a838b0162000150565b965060408901519150808211156200026a57600080fd5b620002788a838b0162000150565b955060608901519150808211156200028f57600080fd5b6200029d8a838b0162000150565b94506080890151915080821115620002b457600080fd5b620002c28a838b0162000150565b935060a0890151915080821115620002d957600080fd5b50620002e889828a0162000150565b9150509295509295509295565b600181811c908216806200030a57607f821691505b6020821081036200032b57634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000381576000816000526020600020601f850160051c810160208610156200035c5750805b601f850160051c820191505b818110156200037d5782815560010162000368565b5050505b505050565b81516001600160401b03811115620003a257620003a26200013a565b620003ba81620003b38454620002f5565b8462000331565b602080601f831160018114620003f25760008415620003d95750858301515b600019600386901b1c1916600185901b1785556200037d565b600085815260208120601f198616915b82811015620004235788860151825594840194600190910190840162000402565b5085821015620004425787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b611a5580620004626000396000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c806370a0823111610104578063a76b4d56116100a2578063cd279c7c11610071578063cd279c7c146103a8578063e8a3d485146103bb578063e985e9c5146103c3578063f2fde38b146103d657600080fd5b8063a76b4d5614610372578063b49bbd941461037a578063b88d4fde14610382578063c87b56dd1461039557600080fd5b806394e29329116100de57806394e293291461034757806395d89b411461034f578063a159047b14610357578063a22cb4651461035f57600080fd5b806370a082311461031b578063715018a61461032e5780638da5cb5b1461033657600080fd5b80632f745c59116101715780634f558e791161014b5780634f558e79146102c25780634f6ccce7146102ed5780635e0a9661146103005780636352211e1461030857600080fd5b80632f745c591461028957806342842e0e1461029c57806342966c68146102af57600080fd5b8063095ea7b3116101ad578063095ea7b31461023c57806318160ddd1461025157806318e97fd11461026357806323b872dd1461027657600080fd5b806301ffc9a7146101d457806306fdde03146101fc578063081812fc14610211575b600080fd5b6101e76101e2366004611494565b6103e9565b60405190151581526020015b60405180910390f35b6102046103fa565b6040516101f39190611501565b61022461021f366004611514565b61048c565b6040516001600160a01b0390911681526020016101f3565b61024f61024a366004611549565b6104b5565b005b6009545b6040519081526020016101f3565b61024f61027136600461161f565b6104c4565b61024f610284366004611666565b6104d6565b610255610297366004611549565b610566565b61024f6102aa366004611666565b6105cb565b61024f6102bd366004611514565b6105eb565b6101e76102d0366004611514565b6000908152600260205260409020546001600160a01b0316151590565b6102556102fb366004611514565b6105f7565b610204610650565b610224610316366004611514565b61065f565b6102556103293660046116a2565b61066a565b61024f6106b2565b600b546001600160a01b0316610224565b6102046106c6565b6102046106d5565b6102046106e4565b61024f61036d3660046116bd565b610772565b61020461077d565b61020461078a565b61024f6103903660046116f9565b610797565b6102046103a3366004611514565b6107ae565b61024f6103b6366004611775565b6107b9565b6102046107d5565b6101e76103d13660046117cc565b6107e4565b61024f6103e43660046116a2565b610812565b60006103f482610850565b92915050565b606060008054610409906117ff565b80601f0160208091040260200160405190810160405280929190818152602001828054610435906117ff565b80156104825780601f1061045757610100808354040283529160200191610482565b820191906000526020600020905b81548152906001019060200180831161046557829003601f168201915b5050505050905090565b600061049782610875565b506000828152600460205260409020546001600160a01b03166103f4565b6104c08282336108ae565b5050565b6104cc6108bb565b6104c082826108e8565b6001600160a01b03821661050557604051633250574960e11b8152600060048201526024015b60405180910390fd5b6000610512838333610938565b9050836001600160a01b0316816001600160a01b031614610560576040516364283d7b60e01b81526001600160a01b03808616600483015260248201849052821660448201526064016104fc565b50505050565b60006105718361066a565b82106105a25760405163295f44f760e21b81526001600160a01b0384166004820152602481018390526044016104fc565b506001600160a01b03919091166000908152600760209081526040808320938352929052205490565b6105e683838360405180602001604052806000815250610797565b505050565b6104c060008233610938565b600061060260095490565b821061062b5760405163295f44f760e21b815260006004820152602481018390526044016104fc565b6009828154811061063e5761063e611839565b90600052602060002001549050919050565b6060600d8054610409906117ff565b60006103f482610875565b60006001600160a01b038216610696576040516322718ad960e21b8152600060048201526024016104fc565b506001600160a01b031660009081526003602052604090205490565b6106ba6108bb565b6106c4600061094d565b565b6060600c8054610409906117ff565b606060018054610409906117ff565b600d80546106f1906117ff565b80601f016020809104026020016040519081016040528092919081815260200182805461071d906117ff565b801561076a5780601f1061073f5761010080835404028352916020019161076a565b820191906000526020600020905b81548152906001019060200180831161074d57829003601f168201915b505050505081565b6104c033838361099f565b600e80546106f1906117ff565b600c80546106f1906117ff565b6107a28484846104d6565b61056084848484610a3e565b60606103f482610b67565b6107c16108bb565b6107cb8383610c70565b6105e682826108e8565b6060600e8054610409906117ff565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b61081a6108bb565b6001600160a01b03811661084457604051631e4fbdf760e01b8152600060048201526024016104fc565b61084d8161094d565b50565b60006001600160e01b0319821663780e9d6360e01b14806103f457506103f482610c8a565b6000818152600260205260408120546001600160a01b0316806103f457604051637e27328960e01b8152600481018490526024016104fc565b6105e68383836001610caf565b600b546001600160a01b031633146106c45760405163118cdaa760e01b81523360048201526024016104fc565b6000828152600660205260409020610900828261189f565b506040518281527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79060200160405180910390a15050565b6000610945848484610db5565b949350505050565b600b80546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0382166109d157604051630b61174360e31b81526001600160a01b03831660048201526024016104fc565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b6001600160a01b0383163b1561056057604051630a85bd0160e11b81526001600160a01b0384169063150b7a0290610a8090339088908790879060040161195f565b6020604051808303816000875af1925050508015610abb575060408051601f3d908101601f19168201909252610ab89181019061199c565b60015b610b24573d808015610ae9576040519150601f19603f3d011682016040523d82523d6000602084013e610aee565b606091505b508051600003610b1c57604051633250574960e11b81526001600160a01b03851660048201526024016104fc565b805181602001fd5b6001600160e01b03198116630a85bd0160e11b14610b6057604051633250574960e11b81526001600160a01b03851660048201526024016104fc565b5050505050565b6060610b7282610875565b5060008281526006602052604081208054610b8c906117ff565b80601f0160208091040260200160405190810160405280929190818152602001828054610bb8906117ff565b8015610c055780601f10610bda57610100808354040283529160200191610c05565b820191906000526020600020905b815481529060010190602001808311610be857829003601f168201915b505050505090506000610c2360408051602081019091526000815290565b90508051600003610c35575092915050565b815115610c67578082604051602001610c4f9291906119b9565b60405160208183030381529060405292505050919050565b61094584610e82565b6104c0828260405180602001604052806000815250610ef7565b60006001600160e01b03198216632483248360e11b14806103f457506103f482610f0e565b8080610cc357506001600160a01b03821615155b15610d85576000610cd384610875565b90506001600160a01b03831615801590610cff5750826001600160a01b0316816001600160a01b031614155b8015610d125750610d1081846107e4565b155b15610d3b5760405163a9fbf51f60e01b81526001600160a01b03841660048201526024016104fc565b8115610d835783856001600160a01b0316826001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45b505b5050600090815260046020526040902080546001600160a01b0319166001600160a01b0392909216919091179055565b600080610dc3858585610f5e565b90506001600160a01b038116610e2057610e1b84600980546000838152600a60205260408120829055600182018355919091527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0155565b610e43565b846001600160a01b0316816001600160a01b031614610e4357610e438185611057565b6001600160a01b038516610e5f57610e5a846110e8565b610945565b846001600160a01b0316816001600160a01b031614610945576109458585611197565b6060610e8d82610875565b506000610ea560408051602081019091526000815290565b90506000815111610ec55760405180602001604052806000815250610ef0565b80610ecf846111e7565b604051602001610ee09291906119b9565b6040516020818303038152906040525b9392505050565b610f01838361127a565b6105e66000848484610a3e565b60006001600160e01b031982166380ac58cd60e01b1480610f3f57506001600160e01b03198216635b5e139f60e01b145b806103f457506301ffc9a760e01b6001600160e01b03198316146103f4565b6000828152600260205260408120546001600160a01b0390811690831615610f8b57610f8b8184866112df565b6001600160a01b03811615610fc957610fa8600085600080610caf565b6001600160a01b038116600090815260036020526040902080546000190190555b6001600160a01b03851615610ff8576001600160a01b0385166000908152600360205260409020805460010190555b60008481526002602052604080822080546001600160a01b0319166001600160a01b0389811691821790925591518793918516917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4949350505050565b60006110628361066a565b6000838152600860205260409020549091508082146110b5576001600160a01b03841660009081526007602090815260408083208584528252808320548484528184208190558352600890915290208190555b5060009182526008602090815260408084208490556001600160a01b039094168352600781528383209183525290812055565b6009546000906110fa906001906119e8565b6000838152600a60205260408120546009805493945090928490811061112257611122611839565b90600052602060002001549050806009838154811061114357611143611839565b6000918252602080832090910192909255828152600a9091526040808220849055858252812055600980548061117b5761117b611a09565b6001900381819060005260206000200160009055905550505050565b600060016111a48461066a565b6111ae91906119e8565b6001600160a01b039093166000908152600760209081526040808320868452825280832085905593825260089052919091209190915550565b606060006111f483611343565b600101905060008167ffffffffffffffff81111561121457611214611573565b6040519080825280601f01601f19166020018201604052801561123e576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a850494508461124857509392505050565b6001600160a01b0382166112a457604051633250574960e11b8152600060048201526024016104fc565b60006112b283836000610938565b90506001600160a01b038116156105e6576040516339e3563760e11b8152600060048201526024016104fc565b6112ea83838361141b565b6105e6576001600160a01b03831661131857604051637e27328960e01b8152600481018290526024016104fc565b60405163177e802f60e01b81526001600160a01b0383166004820152602481018290526044016104fc565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b83106113825772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef810000000083106113ae576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc1000083106113cc57662386f26fc10000830492506010015b6305f5e10083106113e4576305f5e100830492506008015b61271083106113f857612710830492506004015b6064831061140a576064830492506002015b600a83106103f45760010192915050565b60006001600160a01b038316158015906109455750826001600160a01b0316846001600160a01b03161480611455575061145584846107e4565b806109455750506000908152600460205260409020546001600160a01b03908116911614919050565b6001600160e01b03198116811461084d57600080fd5b6000602082840312156114a657600080fd5b8135610ef08161147e565b60005b838110156114cc5781810151838201526020016114b4565b50506000910152565b600081518084526114ed8160208601602086016114b1565b601f01601f19169290920160200192915050565b602081526000610ef060208301846114d5565b60006020828403121561152657600080fd5b5035919050565b80356001600160a01b038116811461154457600080fd5b919050565b6000806040838503121561155c57600080fd5b6115658361152d565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b600067ffffffffffffffff808411156115a4576115a4611573565b604051601f8501601f19908116603f011681019082821181831017156115cc576115cc611573565b816040528093508581528686860111156115e557600080fd5b858560208301376000602087830101525050509392505050565b600082601f83011261161057600080fd5b610ef083833560208501611589565b6000806040838503121561163257600080fd5b82359150602083013567ffffffffffffffff81111561165057600080fd5b61165c858286016115ff565b9150509250929050565b60008060006060848603121561167b57600080fd5b6116848461152d565b92506116926020850161152d565b9150604084013590509250925092565b6000602082840312156116b457600080fd5b610ef08261152d565b600080604083850312156116d057600080fd5b6116d98361152d565b9150602083013580151581146116ee57600080fd5b809150509250929050565b6000806000806080858703121561170f57600080fd5b6117188561152d565b93506117266020860161152d565b925060408501359150606085013567ffffffffffffffff81111561174957600080fd5b8501601f8101871361175a57600080fd5b61176987823560208401611589565b91505092959194509250565b60008060006060848603121561178a57600080fd5b6117938461152d565b925060208401359150604084013567ffffffffffffffff8111156117b657600080fd5b6117c2868287016115ff565b9150509250925092565b600080604083850312156117df57600080fd5b6117e88361152d565b91506117f66020840161152d565b90509250929050565b600181811c9082168061181357607f821691505b60208210810361183357634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052603260045260246000fd5b601f8211156105e6576000816000526020600020601f850160051c810160208610156118785750805b601f850160051c820191505b8181101561189757828155600101611884565b505050505050565b815167ffffffffffffffff8111156118b9576118b9611573565b6118cd816118c784546117ff565b8461184f565b602080601f83116001811461190257600084156118ea5750858301515b600019600386901b1c1916600185901b178555611897565b600085815260208120601f198616915b8281101561193157888601518255948401946001909101908401611912565b508582101561194f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090611992908301846114d5565b9695505050505050565b6000602082840312156119ae57600080fd5b8151610ef08161147e565b600083516119cb8184602088016114b1565b8351908301906119df8183602088016114b1565b01949350505050565b818103818111156103f457634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fdfea26469706673582212206ff38700c73b602455dbb7964f2d3281f62efc77263f0f3bdb67f5e4ee5d4f3f64736f6c63430008170033a2646970667358221220d058cf8dfb17169ff3320b4ba2cb9665d5f875cda19c5c22351f341d783cad4064736f6c63430008170033" @@ -21,11 +23,11 @@ access(all) let compiledERC20Bytecode = "6101606040523480156200001257600080fd5b5 access(all) let evmBridgeRouterCode = "696d706f727420224e6f6e46756e6769626c65546f6b656e220a696d706f7274202246756e6769626c65546f6b656e220a696d706f72742022466c6f77546f6b656e220a0a696d706f7274202245564d220a0a696d706f7274202249466c6f7745564d4e4654427269646765220a696d706f7274202249466c6f7745564d546f6b656e427269646765220a0a2f2f2f205468697320636f6e747261637420646566696e65732061206d656368616e69736d20666f7220726f7574696e67206272696467652072657175657374732066726f6d207468652045564d20636f6e747261637420746f2074686520466c6f772d45564d206272696467652061732077656c6c0a2f2f2f206173207570646174696e67207468652064657369676e617465642062726964676520616464726573730a2f2f2f0a61636365737328616c6c290a636f6e74726163742045564d427269646765526f75746572207b0a0a202020202f2f2f20456e7469746c656d656e7420616c6c6f77696e6720666f72207570646174657320746f2074686520526f757465720a2020202061636365737328616c6c2920656e7469746c656d656e7420526f7574657241646d696e0a0a202020202f2f2f20456d69747465642069662f7768656e207468652062726964676520636f6e74726163742074686520726f75746572206469726563747320746f20697320757064617465640a2020202061636365737328616c6c29206576656e7420427269646765436f6e74726163745570646174656428616464726573733a20416464726573732c206e616d653a20537472696e67290a202020200a202020202f2f2f204272696467654163636573736f7220696d706c656d656e746174696f6e2075736564206279207468652045564d20636f6e747261637420746f20726f757465206272696467652063616c6c73206265747765656e20564d730a202020202f2f2f0a2020202061636365737328616c6c290a202020207265736f7572636520526f75746572203a2045564d2e4272696467654163636573736f72207b0a20202020202020202f2f2f2041646472657373206f66207468652062726964676520636f6e74726163740a202020202020202061636365737328616c6c292076617220627269646765416464726573733a20416464726573730a20202020202020202f2f2f204e616d65206f66207468652062726964676520636f6e74726163740a202020202020202061636365737328616c6c292076617220627269646765436f6e74726163744e616d653a20537472696e670a0a2020202020202020696e697428616464726573733a20416464726573732c206e616d653a20537472696e6729207b0a20202020202020202020202073656c662e62726964676541646472657373203d20616464726573730a20202020202020202020202073656c662e627269646765436f6e74726163744e616d65203d206e616d650a20202020202020207d0a0a20202020202020202f2f2f2050617373657320616c6f6e672074686520627269646765207265717565737420746f206465646963617465642062726964676520636f6e74726163740a20202020202020202f2f2f0a20202020202020202f2f2f2040706172616d206e66743a20546865204e465420746f206265206272696467656420746f2045564d0a20202020202020202f2f2f2040706172616d20746f3a205468652061646472657373206f66207468652045564d206163636f756e7420746f2072656365697665207468652062726964676564204e46540a20202020202020202f2f2f2040706172616d2066656550726f76696465723a2041207265666572656e636520746f20612046756e6769626c65546f6b656e2050726f76696465722066726f6d20776869636820746865206272696467696e67206665652069732077697468647261776e20696e2024464c4f570a20202020202020202f2f2f0a20202020202020206163636573732845564d2e427269646765290a202020202020202066756e206465706f7369744e4654280a2020202020202020202020206e66743a20407b4e6f6e46756e6769626c65546f6b656e2e4e46547d2c0a202020202020202020202020746f3a2045564d2e45564d416464726573732c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a202020202020202029207b0a20202020202020202020202073656c662e626f72726f7742726964676528292e6272696467654e4654546f45564d28746f6b656e3a203c2d6e66742c20746f3a20746f2c2066656550726f76696465723a2066656550726f7669646572290a20202020202020207d0a0a20202020202020202f2f2f2050617373657320616c6f6e672074686520627269646765207265717565737420746f20746865206465646963617465642062726964676520636f6e74726163742c2072657475726e696e67207468652062726964676564204e46540a20202020202020202f2f2f0a20202020202020202f2f2f2040706172616d2063616c6c65723a2041207265666572656e636520746f2074686520434f412077686963682063757272656e746c79206f776e7320746865204e465420696e2045564d0a20202020202020202f2f2f2040706172616d20747970653a2054686520436164656e63652074797065206f6620746865204e465420746f20626520627269646765642066726f6d2045564d0a20202020202020202f2f2f2040706172616d2069643a20546865204944206f6620746865204e465420746f20626520627269646765642066726f6d2045564d0a20202020202020202f2f2f2040706172616d2066656550726f76696465723a2041207265666572656e636520746f20612046756e6769626c65546f6b656e2050726f76696465722066726f6d20776869636820746865206272696467696e67206665652069732077697468647261776e20696e2024464c4f570a20202020202020202f2f2f0a20202020202020202f2f2f204072657475726e205468652062726964676564204e46540a20202020202020202f2f2f0a20202020202020206163636573732845564d2e427269646765290a202020202020202066756e2077697468647261774e4654280a20202020202020202020202063616c6c65723a20617574682845564d2e43616c6c29202645564d2e436164656e63654f776e65644163636f756e742c0a202020202020202020202020747970653a20547970652c0a20202020202020202020202069643a2055496e743235362c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020293a20407b4e6f6e46756e6769626c65546f6b656e2e4e46547d207b0a2020202020202020202020206c657420627269646765203d2073656c662e626f72726f7742726964676528290a2020202020202020202020202f2f20446566696e6520612063616c6c6261636b2066756e6374696f6e2c20656e61626c696e67207468652062726964676520746f20616374206f6e2074686520657068656d6572616c20434f41207265666572656e636520696e2073636f70650a202020202020202020202020766172206578656375746564203d2066616c73650a20202020202020202020202066756e2063616c6c6261636b28293a2045564d2e526573756c74207b0a20202020202020202020202020202020707265207b0a20202020202020202020202020202020202020202165786563757465643a202243616c6c6261636b2063616e206f6e6c79206265206578656375746564206f6e6365220a202020202020202020202020202020207d0a20202020202020202020202020202020706f7374207b0a202020202020202020202020202020202020202065786563757465643a202243616c6c6261636b206d757374206265206578656375746564220a202020202020202020202020202020207d0a202020202020202020202020202020206578656375746564203d20747275650a2020202020202020202020202020202072657475726e2063616c6c65722e63616c6c280a2020202020202020202020202020202020202020746f3a206272696467652e6765744173736f63696174656445564d4164647265737328776974683a2074797065290a2020202020202020202020202020202020202020202020203f3f2070616e696328224e6f2045564d2061646472657373206173736f6369617465642077697468207479706522292c0a2020202020202020202020202020202020202020646174613a2045564d2e656e636f6465414249576974685369676e6174757265280a20202020202020202020202020202020202020202020202022736166655472616e7366657246726f6d28616464726573732c616464726573732c75696e7432353629222c0a2020202020202020202020202020202020202020202020205b63616c6c65722e6164647265737328292c206272696467652e676574427269646765434f4145564d4164647265737328292c2069645d0a2020202020202020202020202020202020202020292c0a20202020202020202020202020202020202020206761734c696d69743a2031353030303030302c0a202020202020202020202020202020202020202076616c75653a2045564d2e42616c616e6365286174746f666c6f773a2030290a20202020202020202020202020202020290a2020202020202020202020207d0a2020202020202020202020202f2f2045786563757465207468652062726964676520726571756573740a20202020202020202020202072657475726e203c2d206272696467652e6272696467654e465446726f6d45564d280a202020202020202020202020202020206f776e65723a2063616c6c65722e6164647265737328292c0a20202020202020202020202020202020747970653a20747970652c0a2020202020202020202020202020202069643a2069642c0a2020202020202020202020202020202066656550726f76696465723a2066656550726f76696465722c0a2020202020202020202020202020202070726f7465637465645472616e7366657243616c6c3a2063616c6c6261636b0a202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f2f2050617373657320616c6f6e672074686520627269646765207265717565737420746f206465646963617465642062726964676520636f6e74726163740a20202020202020202f2f2f0a20202020202020202f2f2f2040706172616d207661756c743a205468652066756e6769626c6520746f6b656e207661756c7420746f206265206272696467656420746f2045564d0a20202020202020202f2f2f2040706172616d20746f3a205468652061646472657373206f66207468652045564d206163636f756e7420746f207265636569766520746865206272696467656420746f6b656e730a20202020202020202f2f2f2040706172616d2066656550726f76696465723a2041207265666572656e636520746f20612046756e6769626c65546f6b656e2050726f76696465722066726f6d20776869636820746865206272696467696e67206665652069732077697468647261776e20696e2024464c4f570a20202020202020202f2f2f0a20202020202020206163636573732845564d2e427269646765290a202020202020202066756e206465706f736974546f6b656e73280a2020202020202020202020207661756c743a20407b46756e6769626c65546f6b656e2e5661756c747d2c0a202020202020202020202020746f3a2045564d2e45564d416464726573732c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a202020202020202029207b0a20202020202020202020202073656c662e626f72726f7742726964676528292e627269646765546f6b656e73546f45564d287661756c743a203c2d7661756c742c20746f3a20746f2c2066656550726f76696465723a2066656550726f7669646572290a20202020202020207d0a0a20202020202020202f2f2f2050617373657320616c6f6e672074686520627269646765207265717565737420746f20746865206465646963617465642062726964676520636f6e74726163742c2072657475726e696e67207468652062726964676564204e46540a20202020202020202f2f2f0a20202020202020202f2f2f2040706172616d2063616c6c65723a2041207265666572656e636520746f2074686520434f412077686963682063757272656e746c79206f776e732074686520746f6b656e7320696e2045564d0a20202020202020202f2f2f2040706172616d20747970653a2054686520436164656e63652074797065206f66207468652066756e6769626c6520746f6b656e207661756c7420746f20626520627269646765642066726f6d2045564d0a20202020202020202f2f2f2040706172616d20616d6f756e743a2054686520616d6f756e74206f6620746f6b656e7320746f20626520627269646765640a20202020202020202f2f2f2040706172616d2066656550726f76696465723a2041207265666572656e636520746f20612046756e6769626c65546f6b656e2050726f76696465722066726f6d20776869636820746865206272696467696e67206665652069732077697468647261776e20696e2024464c4f570a20202020202020202f2f2f0a20202020202020202f2f2f204072657475726e205468652062726964676564204e46540a20202020202020202f2f2f0a20202020202020206163636573732845564d2e427269646765290a202020202020202066756e207769746864726177546f6b656e73280a20202020202020202020202063616c6c65723a20617574682845564d2e43616c6c29202645564d2e436164656e63654f776e65644163636f756e742c0a202020202020202020202020747970653a20547970652c0a202020202020202020202020616d6f756e743a2055496e743235362c0a20202020202020202020202066656550726f76696465723a20617574682846756e6769626c65546f6b656e2e57697468647261772920267b46756e6769626c65546f6b656e2e50726f76696465727d0a2020202020202020293a20407b46756e6769626c65546f6b656e2e5661756c747d207b0a2020202020202020202020206c657420627269646765203d2073656c662e626f72726f7742726964676528290a2020202020202020202020202f2f20446566696e6520612063616c6c6261636b2066756e6374696f6e2c20656e61626c696e67207468652062726964676520746f20616374206f6e2074686520657068656d6572616c20434f41207265666572656e636520696e2073636f70650a202020202020202020202020766172206578656375746564203d2066616c73650a20202020202020202020202066756e2063616c6c6261636b28293a2045564d2e526573756c74207b0a20202020202020202020202020202020707265207b0a20202020202020202020202020202020202020202165786563757465643a202243616c6c6261636b2063616e206f6e6c79206265206578656375746564206f6e6365220a202020202020202020202020202020207d0a20202020202020202020202020202020706f7374207b0a202020202020202020202020202020202020202065786563757465643a202243616c6c6261636b206d757374206265206578656375746564220a202020202020202020202020202020207d0a202020202020202020202020202020206578656375746564203d20747275650a2020202020202020202020202020202072657475726e2063616c6c65722e63616c6c280a2020202020202020202020202020202020202020746f3a206272696467652e6765744173736f63696174656445564d4164647265737328776974683a2074797065290a2020202020202020202020202020202020202020202020203f3f2070616e696328224e6f2045564d2061646472657373206173736f6369617465642077697468207479706522292c0a2020202020202020202020202020202020202020646174613a2045564d2e656e636f6465414249576974685369676e6174757265280a202020202020202020202020202020202020202020202020227472616e7366657228616464726573732c75696e7432353629222c0a2020202020202020202020202020202020202020202020205b6272696467652e676574427269646765434f4145564d4164647265737328292c20616d6f756e745d0a2020202020202020202020202020202020202020292c0a20202020202020202020202020202020202020206761734c696d69743a2031353030303030302c0a202020202020202020202020202020202020202076616c75653a2045564d2e42616c616e6365286174746f666c6f773a2030290a20202020202020202020202020202020290a2020202020202020202020207d0a2020202020202020202020202f2f2045786563757465207468652062726964676520726571756573740a20202020202020202020202072657475726e203c2d206272696467652e627269646765546f6b656e7346726f6d45564d280a202020202020202020202020202020206f776e65723a2063616c6c65722e6164647265737328292c0a20202020202020202020202020202020747970653a20747970652c0a20202020202020202020202020202020616d6f756e743a20616d6f756e742c0a2020202020202020202020202020202066656550726f76696465723a2066656550726f76696465722c0a2020202020202020202020202020202070726f7465637465645472616e7366657243616c6c3a2063616c6c6261636b0a202020202020202020202020290a20202020202020207d0a0a20202020202020202f2f2f2053657473207468652062726964676520636f6e74726163742074686520726f75746572206469726563747320627269646765207265717565737473207468726f7567680a20202020202020202f2f2f0a202020202020202061636365737328526f7574657241646d696e292066756e20736574427269646765436f6e747261637428616464726573733a20416464726573732c206e616d653a20537472696e6729207b0a20202020202020202020202073656c662e62726964676541646472657373203d20616464726573730a20202020202020202020202073656c662e627269646765436f6e74726163744e616d65203d206e616d650a202020202020202020202020656d697420427269646765436f6e74726163745570646174656428616464726573733a20616464726573732c206e616d653a206e616d65290a20202020202020207d0a0a20202020202020202f2f2f2052657475726e732061207265666572656e636520746f207468652062726964676520636f6e74726163740a20202020202020202f2f2f0a20202020202020206163636573732873656c66292066756e20626f72726f7742726964676528293a20267b49466c6f7745564d4e46544272696467652c2049466c6f7745564d546f6b656e4272696467657d207b0a20202020202020202020202072657475726e206765744163636f756e742873656c662e62726964676541646472657373292e636f6e7472616374732e626f72726f773c267b49466c6f7745564d4e46544272696467652c2049466c6f7745564d546f6b656e4272696467657d3e280a20202020202020202020202020202020202020206e616d653a2073656c662e627269646765436f6e74726163744e616d650a2020202020202020202020202020202029203f3f2070616e6963282242726964676520636f6e7472616374206e6f7420666f756e6422290a20202020202020207d0a202020207d0a0a20202020696e697428627269646765416464726573733a20416464726573732c20627269646765436f6e74726163744e616d653a20537472696e6729207b0a202020202020202073656c662e6163636f756e742e73746f726167652e73617665280a2020202020202020202020203c2d63726561746520526f7574657228616464726573733a20627269646765416464726573732c206e616d653a20627269646765436f6e74726163744e616d65292c0a202020202020202020202020746f3a202f73746f726167652f65766d427269646765526f757465720a2020202020202020290a202020207d0a7d0a" access(all) let bridgedNFTCodeChunks = [ - "696d706f7274204e6f6e46756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030310a696d706f7274204d6574616461746156696577732066726f6d203078303030303030303030303030303030310a696d706f727420566965775265736f6c7665722066726f6d203078303030303030303030303030303030310a696d706f72742046756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030320a696d706f727420466c6f77546f6b656e2066726f6d203078303030303030303030303030303030330a0a696d706f72742045564d2066726f6d203078303030303030303030303030303030310a0a696d706f7274204943726f7373564d2066726f6d203078303030303030303030303030303030370a696d706f7274204945564d4272696467654e46544d696e7465722066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d4272696467654e4654457363726f772066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d427269646765436f6e6669672066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d4272696467655574696c732066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d4272696467652066726f6d203078303030303030303030303030303030370a696d706f72742043726f7373564d4e46542066726f6d203078303030303030303030303030303030370a0a2f2f2f205468697320636f6e747261637420697320612074656d706c617465207573656420627920466c6f7745564d42726964676520746f20646566696e652045564d2d6e6174697665204e46547320627269646765642066726f6d20466c6f772045564d20746f20466c6f772e0a2f2f2f2055706f6e206465706c6f796d656e74206f66207468697320636f6e74726163742c2074686520636f6e7472616374206e616d65206973206465726976656420617320612066756e6374696f6e206f6620746865206173736574207479706520286865726520616e2045524337323120616b610a2f2f2f20616e204e46542920616e642074686520636f6e747261637427732045564d20616464726573732e20546865206465726976656420636f6e7472616374206e616d65206973207468656e206a6f696e65642077697468207468697320636f6e7472616374277320636f64652c0a2f2f2f207072657061726564206173206368756e6b7320696e20466c6f7745564d42726964676554656d706c61746573206265666f7265206265696e67206465706c6f79656420746f2074686520466c6f772045564d20427269646765206163636f756e742e0a2f2f2f0a2f2f2f204f6e206272696467696e672c2074686520455243373231206973207472616e7366657272656420746f2074686520627269646765277320436164656e63654f776e65644163636f756e742045564d206164647265737320616e642061206e6577204e4654206973206d696e7465642066726f6d0a2f2f2f207468697320636f6e747261637420746f20746865206272696467696e672063616c6c65722e204f6e2072657475726e20746f20466c6f772045564d2c2074686520726576657273652070726f6365737320697320666f6c6c6f776564202d2074686520746f6b656e206973206275726e65640a2f2f2f20696e207468697320636f6e747261637420616e642074686520455243373231206973207472616e7366657272656420746f2074686520646566696e656420726563697069656e742e20496e2074686973207761792c2074686520436164656e636520746f6b656e206163747320617320610a2f2f2f20726570726573656e746174696f6e206f6620626f7468207468652045564d204e465420616e642074687573206f776e6572736869702072696768747320746f2069742075706f6e206272696467696e67206261636b20746f20466c6f772045564d2e0a2f2f2f0a2f2f2f20546f20627269646765206265747765656e20564d732c20612063616c6c65722063616e20656974686572207573652074686520636f6e7472616374206d6574686f647320646566696e65642062656c6f772c206f72207573652074686520466c6f7745564d42726964676527730a2f2f2f206272696467696e67206d6574686f64732077686963682077696c6c2070726f6772616d61746963616c6c7920726f757465206272696467696e672063616c6c7320746f207468697320636f6e74726163742e0a2f2f2f0a61636365737328616c6c2920636f6e747261637420", - "203a204943726f7373564d2c204945564d4272696467654e46544d696e7465722c204e6f6e46756e6769626c65546f6b656e207b0a0a202020202f2f2f20506f696e74657220746f2074686520466163746f7279206465706c6f79656420536f6c696469747920636f6e7472616374206164647265737320646566696e696e672074686520627269646765642061737365740a2020202061636365737328616c6c29206c65742065766d4e4654436f6e7472616374416464726573733a2045564d2e45564d416464726573730a202020202f2f2f204e616d65206f6620746865204e465420636f6c6c656374696f6e20646566696e656420696e2074686520636f72726573706f6e64696e672045524337323120636f6e74726163740a2020202061636365737328616c6c29206c6574206e616d653a20537472696e670a202020202f2f2f2053796d626f6c206f6620746865204e465420636f6c6c656374696f6e20646566696e656420696e2074686520636f72726573706f6e64696e672045524337323120636f6e74726163740a2020202061636365737328616c6c29206c65742073796d626f6c3a20537472696e670a202020202f2f2f20555249206f662074686520636f6e74726163742c20696620617661696c61626c6520617320612076617220696e2063617365207468652062726964676520656e61626c65732063726f73732d564d204d657461646174612073796e63696e6720696e20746865206675747572650a2020202061636365737328616c6c292076617220636f6e74726163745552493a20537472696e673f0a202020202f2f2f2052657461696e206120436f6c6c656374696f6e20746f207265666572656e6365207768656e207265736f6c76696e6720436f6c6c656374696f6e204d657461646174610a202020206163636573732873656c6629206c657420636f6c6c656374696f6e3a2040436f6c6c656374696f6e0a202020202f2f2f204d617070696e67206f6620746f6b656e205552497320696e6465786564206f6e207468656972204552433732312049442e205468697320776f756c64206e6f74206e6f726d616c6c792062652072657461696e65642077697468696e206120436164656e6365204e46540a202020202f2f2f20636f6e74726163742c206275742073696e6365204e4654206d65746164617461206d6179206265207570646174656420696e2045564d2c20697427732072657461696e6564206865726520736f207468617420746865206272696467652063616e207570646174650a202020202f2f2f20697420616761696e73742074686520736f757263652045524337323120636f6e7472616374207768696368206973207472656174656420617320746865204e4654277320736f75726365206f662074727574682e0a2020202061636365737328616c6c29206c657420746f6b656e555249733a207b55496e743235363a20537472696e677d0a0a202020202f2f2f20546865204e4654207265736f7572636520726570726573656e74696e672074686520627269646765642045524337323120746f6b656e0a202020202f2f2f0a2020202061636365737328616c6c29207265736f75726365204e4654203a2043726f7373564d4e46542e45564d4e4654207b0a20202020202020202f2f2f2054686520436164656e6365204944206f6620746865204e46540a202020202020202061636365737328616c6c29206c65742069643a2055496e7436340a20202020202020202f2f2f2054686520455243373231204944206f6620746865204e46540a202020202020202061636365737328616c6c29206c65742065766d49443a2055496e743235360a20202020202020202f2f2f204164646974696f6e616c206f6e636861696e206d657461646174610a202020202020202061636365737328616c6c29206c6574206d657461646174613a207b537472696e673a20416e795374727563747d0a0a2020202020202020696e6974280a20202020202020202020202065766d49443a2055496e743235362c0a2020202020202020202020206d657461646174613a207b537472696e673a20416e795374727563747d0a202020202020202029207b0a20202020202020202020202073656c662e6964203d2073656c662e757569640a20202020202020202020202073656c662e65766d4944203d2065766d49440a20202020202020202020202073656c662e6d65746164617461203d206d657461646174610a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320746865206d65746164617461207669657720747970657320737570706f727465642062792074686973204e46540a202020202020202061636365737328616c6c2920766965772066756e20676574566965777328293a205b547970655d207b0a20202020202020202020202072657475726e205b0a20202020202020202020202020202020547970653c43726f7373564d4e46542e45564d427269646765644d657461646174613e28292c0a20202020202020202020202020202020547970653c4d6574616461746156696577732e53657269616c3e28292c0a20202020202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28292c0a20202020202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28290a2020202020202020202020205d0a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e206765744e616d6528293a20537472696e67207b0a20202020202020202020202072657475726e20", + "696d706f7274204e6f6e46756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030310a696d706f7274204d6574616461746156696577732066726f6d203078303030303030303030303030303030310a696d706f727420566965775265736f6c7665722066726f6d203078303030303030303030303030303030310a696d706f72742046756e6769626c65546f6b656e2066726f6d203078303030303030303030303030303030320a696d706f727420466c6f77546f6b656e2066726f6d203078303030303030303030303030303030330a0a696d706f72742045564d2066726f6d203078303030303030303030303030303030310a0a696d706f7274204943726f7373564d2066726f6d203078303030303030303030303030303030370a696d706f7274204945564d4272696467654e46544d696e7465722066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d4272696467654e4654457363726f772066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d427269646765436f6e6669672066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d4272696467655574696c732066726f6d203078303030303030303030303030303030370a696d706f727420466c6f7745564d4272696467652066726f6d203078303030303030303030303030303030370a696d706f72742043726f7373564d4e46542066726f6d203078303030303030303030303030303030370a0a2f2f2f205468697320636f6e747261637420697320612074656d706c617465207573656420627920466c6f7745564d42726964676520746f20646566696e652045564d2d6e6174697665204e46547320627269646765642066726f6d20466c6f772045564d20746f20466c6f772e0a2f2f2f2055706f6e206465706c6f796d656e74206f66207468697320636f6e74726163742c2074686520636f6e7472616374206e616d65206973206465726976656420617320612066756e6374696f6e206f6620746865206173736574207479706520286865726520616e2045524337323120616b610a2f2f2f20616e204e46542920616e642074686520636f6e747261637427732045564d20616464726573732e20546865206465726976656420636f6e7472616374206e616d65206973207468656e206a6f696e65642077697468207468697320636f6e7472616374277320636f64652c0a2f2f2f207072657061726564206173206368756e6b7320696e20466c6f7745564d42726964676554656d706c61746573206265666f7265206265696e67206465706c6f79656420746f2074686520466c6f772045564d20427269646765206163636f756e742e0a2f2f2f0a2f2f2f204f6e206272696467696e672c2074686520455243373231206973207472616e7366657272656420746f2074686520627269646765277320436164656e63654f776e65644163636f756e742045564d206164647265737320616e642061206e6577204e4654206973206d696e7465642066726f6d0a2f2f2f207468697320636f6e747261637420746f20746865206272696467696e672063616c6c65722e204f6e2072657475726e20746f20466c6f772045564d2c2074686520726576657273652070726f6365737320697320666f6c6c6f776564202d2074686520746f6b656e206973206c6f636b65640a2f2f2f20696e204e465420657363726f7720616e642074686520455243373231206973207472616e7366657272656420746f2074686520646566696e656420726563697069656e742e20496e2074686973207761792c2074686520436164656e636520746f6b656e206163747320617320610a2f2f2f20726570726573656e746174696f6e206f6620626f7468207468652045564d204e465420616e642074687573206f776e6572736869702072696768747320746f2069742075706f6e206272696467696e67206261636b20746f20466c6f772045564d2e0a2f2f2f0a2f2f2f20546f20627269646765206265747765656e20564d732c20612063616c6c65722063616e20656974686572207573652074686520696e74657266616365206578706f736564206f6e20436164656e63654f776e65644163636f756e74206f722075736520466c6f7745564d4272696467650a2f2f2f207075626c696320636f6e7472616374206d6574686f64732e0a2f2f2f0a61636365737328616c6c2920636f6e747261637420", + "203a204943726f7373564d2c204945564d4272696467654e46544d696e7465722c204e6f6e46756e6769626c65546f6b656e207b0a0a202020202f2f2f20506f696e74657220746f2074686520466163746f7279206465706c6f79656420536f6c696469747920636f6e7472616374206164647265737320646566696e696e672074686520627269646765642061737365740a2020202061636365737328616c6c29206c65742065766d4e4654436f6e7472616374416464726573733a2045564d2e45564d416464726573730a202020202f2f2f204e616d65206f6620746865204e465420636f6c6c656374696f6e20646566696e656420696e2074686520636f72726573706f6e64696e672045524337323120636f6e74726163740a2020202061636365737328616c6c29206c6574206e616d653a20537472696e670a202020202f2f2f2053796d626f6c206f6620746865204e465420636f6c6c656374696f6e20646566696e656420696e2074686520636f72726573706f6e64696e672045524337323120636f6e74726163740a2020202061636365737328616c6c29206c65742073796d626f6c3a20537472696e670a202020202f2f2f20555249206f662074686520636f6e74726163742c20696620617661696c61626c6520617320612076617220696e2063617365207468652062726964676520656e61626c65732063726f73732d564d204d657461646174612073796e63696e6720696e20746865206675747572650a2020202061636365737328616c6c292076617220636f6e74726163745552493a20537472696e673f0a202020202f2f2f2052657461696e206120436f6c6c656374696f6e20746f207265666572656e6365207768656e207265736f6c76696e6720436f6c6c656374696f6e204d657461646174610a202020206163636573732873656c6629206c657420636f6c6c656374696f6e3a2040436f6c6c656374696f6e0a202020202f2f2f204d617070696e67206f6620746f6b656e205552497320696e6465786564206f6e207468656972204552433732312049442e205468697320776f756c64206e6f74206e6f726d616c6c792062652072657461696e65642077697468696e206120436164656e6365204e46540a202020202f2f2f20636f6e74726163742c206275742073696e6365204e4654206d65746164617461206d6179206265207570646174656420696e2045564d2c20697427732072657461696e6564206865726520736f207468617420746865206272696467652063616e207570646174650a202020202f2f2f20697420616761696e73742074686520736f757263652045524337323120636f6e7472616374207768696368206973207472656174656420617320746865204e4654277320736f75726365206f662074727574682e0a2020202061636365737328616c6c29206c657420746f6b656e555249733a207b55496e743235363a20537472696e677d0a0a202020202f2f2f20546865204e4654207265736f7572636520726570726573656e74696e672074686520627269646765642045524337323120746f6b656e0a202020202f2f2f0a2020202061636365737328616c6c29207265736f75726365204e4654203a2043726f7373564d4e46542e45564d4e4654207b0a20202020202020202f2f2f2054686520436164656e6365204944206f6620746865204e46540a202020202020202061636365737328616c6c29206c65742069643a2055496e7436340a20202020202020202f2f2f2054686520455243373231204944206f6620746865204e46540a202020202020202061636365737328616c6c29206c65742065766d49443a2055496e743235360a20202020202020202f2f2f204164646974696f6e616c206f6e636861696e206d657461646174610a202020202020202061636365737328616c6c29206c6574206d657461646174613a207b537472696e673a20416e795374727563747d0a0a2020202020202020696e6974280a20202020202020202020202065766d49443a2055496e743235362c0a2020202020202020202020206d657461646174613a207b537472696e673a20416e795374727563747d0a202020202020202029207b0a20202020202020202020202073656c662e6964203d2073656c662e757569640a20202020202020202020202073656c662e65766d4944203d2065766d49440a20202020202020202020202073656c662e6d65746164617461203d206d657461646174610a20202020202020207d0a0a20202020202020202f2f2f2052657475726e7320746865206d65746164617461207669657720747970657320737570706f727465642062792074686973204e46540a202020202020202061636365737328616c6c2920766965772066756e20676574566965777328293a205b547970655d207b0a20202020202020202020202072657475726e205b0a20202020202020202020202020202020547970653c4d6574616461746156696577732e45564d427269646765644d657461646174613e28292c0a20202020202020202020202020202020547970653c4d6574616461746156696577732e53657269616c3e28292c0a20202020202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28292c0a20202020202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28290a2020202020202020202020205d0a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e206765744e616d6528293a20537472696e67207b0a20202020202020202020202072657475726e20", "2e6e616d650a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e2067657453796d626f6c28293a20537472696e67207b0a20202020202020202020202072657475726e20", "2e73796d626f6c0a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e20746f6b656e55524928293a20537472696e67207b0a20202020202020202020202072657475726e20", - "2e746f6b656e555249735b73656c662e65766d49445d203f3f2022220a20202020202020207d0a0a20202020202020202f2f2f205265736f6c7665732061206d65746164617461207669657720666f722074686973204e46540a202020202020202061636365737328616c6c292066756e207265736f6c766556696577285f20766965773a2054797065293a20416e795374727563743f207b0a2020202020202020202020207377697463682076696577207b0a202020202020202020202020202020202f2f20576520646f6e2774206b6e6f772077686174206b696e64206f662066696c65207468652055524920726570726573656e747320284950465320762048545450292c20736f2077652063616e2774207265736f6c766520446973706c617920766965770a202020202020202020202020202020202f2f20776974682074686520555249206173207468756d626e61696c202d207765206d61792061206e6577207374616e64617264207669657720666f722045564d204e465473202d207468697320697320696e746572696d0a202020202020202020202020202020206361736520547970653c43726f7373564d4e46542e45564d427269646765644d657461646174613e28293a0a202020202020202020202020202020202020202072657475726e2043726f7373564d4e46542e45564d427269646765644d65746164617461280a2020202020202020202020202020202020202020202020206e616d653a2073656c662e6765744e616d6528292c0a20202020202020202020202020202020202020202020202073796d626f6c3a2073656c662e67657453796d626f6c28292c0a2020202020202020202020202020202020202020202020207572693a2043726f7373564d4e46542e55524928626173655552493a206e696c2c2076616c75653a2073656c662e746f6b656e5552492829290a2020202020202020202020202020202020202020290a202020202020202020202020202020206361736520547970653c4d6574616461746156696577732e53657269616c3e28293a0a202020202020202020202020202020202020202072657475726e204d6574616461746156696577732e53657269616c280a20202020202020202020202020202020202020202020202073656c662e69640a2020202020202020202020202020202020202020290a202020202020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28293a0a202020202020202020202020202020202020202072657475726e20", + "2e746f6b656e555249735b73656c662e65766d49445d203f3f2022220a20202020202020207d0a0a20202020202020202f2f2f205265736f6c7665732061206d65746164617461207669657720666f722074686973204e46540a202020202020202061636365737328616c6c292066756e207265736f6c766556696577285f20766965773a2054797065293a20416e795374727563743f207b0a2020202020202020202020207377697463682076696577207b0a202020202020202020202020202020202f2f20576520646f6e2774206b6e6f772077686174206b696e64206f662066696c65207468652055524920726570726573656e747320284950465320762048545450292c20736f2077652063616e2774207265736f6c766520446973706c617920766965770a202020202020202020202020202020202f2f20776974682074686520555249206173207468756d626e61696c202d207765206d61792061206e6577207374616e64617264207669657720666f722045564d204e465473202d207468697320697320696e746572696d0a202020202020202020202020202020206361736520547970653c4d6574616461746156696577732e45564d427269646765644d657461646174613e28293a0a202020202020202020202020202020202020202072657475726e204d6574616461746156696577732e45564d427269646765644d65746164617461280a2020202020202020202020202020202020202020202020206e616d653a2073656c662e6765744e616d6528292c0a20202020202020202020202020202020202020202020202073796d626f6c3a2073656c662e67657453796d626f6c28292c0a2020202020202020202020202020202020202020202020207572693a204d6574616461746156696577732e55524928626173655552493a206e696c2c2076616c75653a2073656c662e746f6b656e5552492829290a2020202020202020202020202020202020202020290a202020202020202020202020202020206361736520547970653c4d6574616461746156696577732e53657269616c3e28293a0a202020202020202020202020202020202020202072657475726e204d6574616461746156696577732e53657269616c280a20202020202020202020202020202020202020202020202073656c662e69640a2020202020202020202020202020202020202020290a202020202020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28293a0a202020202020202020202020202020202020202072657475726e20", "2e7265736f6c7665436f6e747261637456696577280a2020202020202020202020202020202020202020202020207265736f75726365547970653a2073656c662e6765745479706528292c0a20202020202020202020202020202020202020202020202076696577547970653a20547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28290a2020202020202020202020202020202020202020290a202020202020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28293a0a202020202020202020202020202020202020202072657475726e20", "2e7265736f6c7665436f6e747261637456696577280a2020202020202020202020202020202020202020202020207265736f75726365547970653a2073656c662e6765745479706528292c0a20202020202020202020202020202020202020202020202076696577547970653a20547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28290a2020202020202020202020202020202020202020290a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f207075626c69632066756e6374696f6e207468617420616e796f6e652063616e2063616c6c20746f206372656174652061206e657720656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d207b0a20202020202020202020202072657475726e203c2d20", "2e637265617465456d707479436f6c6c656374696f6e286e6674547970653a2073656c662e676574547970652829290a20202020202020207d0a0a20202020202020202f2a202d2d2d2043726f7373564d4e465420636f6e666f726d616e6365202d2d2d202a2f0a20202020202020202f2f0a20202020202020202f2f2f2052657475726e73207468652045564d20636f6e74726163742061646472657373206f6620746865204e46540a202020202020202061636365737328616c6c2920766965772066756e2067657445564d436f6e74726163744164647265737328293a2045564d2e45564d41646472657373207b0a20202020202020202020202072657475726e20", @@ -41,12 +43,12 @@ access(all) let bridgedNFTCodeChunks = [ "2e636f6e74726163745552490a20202020202020207d0a0a20202020202020202f2f2f20476574732074686520616d6f756e74206f66204e4654732073746f72656420696e2074686520636f6c6c656374696f6e0a202020202020202061636365737328616c6c2920766965772066756e206765744c656e67746828293a20496e74207b0a20202020202020202020202072657475726e2073656c662e6f776e65644e4654732e6b6579732e6c656e6774680a20202020202020207d0a0a20202020202020202f2f2f205265747269657665732061207265666572656e636520746f20746865204e46542073746f72656420696e2074686520636f6c6c656374696f6e206279206974732049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f774e4654285f2069643a2055496e743634293a20267b4e6f6e46756e6769626c65546f6b656e2e4e46547d3f207b0a20202020202020202020202072657475726e202673656c662e6f776e65644e4654735b69645d0a20202020202020207d0a0a20202020202020202f2f2f20426f72726f77207468652076696577207265736f6c76657220666f722074686520737065636966696564204e46542049440a202020202020202061636365737328616c6c2920766965772066756e20626f72726f77566965775265736f6c7665722869643a2055496e743634293a20267b566965775265736f6c7665722e5265736f6c7665727d3f207b0a2020202020202020202020206966206c6574206e6674203d202673656c662e6f776e65644e4654735b69645d2061732026", "2e4e46543f207b0a2020202020202020202020202020202072657475726e206e667420617320267b566965775265736f6c7665722e5265736f6c7665727d0a2020202020202020202020207d0a20202020202020202020202072657475726e206e696c0a20202020202020207d0a0a20202020202020202f2f2f204372656174657320616e20656d70747920636f6c6c656374696f6e0a202020202020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d20207b0a20202020202020202020202072657475726e203c2d", "2e637265617465456d707479436f6c6c656374696f6e286e6674547970653a20547970653c40", - "2e4e46543e2829290a20202020202020207d0a202020207d0a0a202020202f2f2f20637265617465456d707479436f6c6c656374696f6e206372656174657320616e20656d70747920436f6c6c656374696f6e20666f722074686520737065636966696564204e465420747970650a202020202f2f2f20616e642072657475726e7320697420746f207468652063616c6c657220736f207468617420746865792063616e206f776e204e4654730a2020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e286e6674547970653a2054797065293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d207b0a202020202020202072657475726e203c2d2063726561746520436f6c6c656374696f6e28290a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a202020202020202020202020476574746572730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f2052657475726e73207468652045564d20636f6e74726163742061646472657373206f6620746865204e4654207468697320636f6e747261637420726570726573656e74730a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e2067657445564d436f6e74726163744164647265737328293a2045564d2e45564d41646472657373207b0a202020202020202072657475726e2073656c662e65766d4e4654436f6e7472616374416464726573730a202020207d0a0a202020202f2f2f2046756e6374696f6e20746861742072657475726e7320616c6c20746865204d6574616461746120566965777320696d706c656d656e7465642062792061204e6f6e2046756e6769626c6520546f6b656e0a202020202f2f2f0a202020202f2f2f204072657475726e20416e206172726179206f6620547970657320646566696e696e672074686520696d706c656d656e7465642076696577732e20546869732076616c75652077696c6c20626520757365642062790a202020202f2f2f202020202020202020646576656c6f7065727320746f206b6e6f7720776869636820706172616d6574657220746f207061737320746f20746865207265736f6c7665566965772829206d6574686f642e0a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e20676574436f6e74726163745669657773287265736f75726365547970653a20547970653f293a205b547970655d207b0a202020202020202072657475726e205b0a202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28292c0a202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28292c0a202020202020202020202020547970653c43726f7373564d4e46542e45564d427269646765644d657461646174613e28290a20202020202020205d0a202020207d0a0a202020202f2f2f2046756e6374696f6e2074686174207265736f6c7665732061206d65746164617461207669657720666f72207468697320636f6e74726163742e0a202020202f2f2f0a202020202f2f2f2040706172616d20766965773a205468652054797065206f6620746865206465736972656420766965772e0a202020202f2f2f204072657475726e20412073747275637475726520726570726573656e74696e67207468652072657175657374656420766965772e0a202020202f2f2f0a2020202061636365737328616c6c292066756e207265736f6c7665436f6e747261637456696577287265736f75726365547970653a20547970653f2c2076696577547970653a2054797065293a20416e795374727563743f207b0a2020202020202020737769746368207669657754797065207b0a2020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28293a0a202020202020202020202020202020206c6574206964656e746966696572203d2022", + "2e4e46543e2829290a20202020202020207d0a202020207d0a0a202020202f2f2f20637265617465456d707479436f6c6c656374696f6e206372656174657320616e20656d70747920436f6c6c656374696f6e20666f722074686520737065636966696564204e465420747970650a202020202f2f2f20616e642072657475726e7320697420746f207468652063616c6c657220736f207468617420746865792063616e206f776e204e4654730a2020202061636365737328616c6c292066756e20637265617465456d707479436f6c6c656374696f6e286e6674547970653a2054797065293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d207b0a202020202020202072657475726e203c2d2063726561746520436f6c6c656374696f6e28290a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a202020202020202020202020476574746572730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f2052657475726e73207468652045564d20636f6e74726163742061646472657373206f6620746865204e4654207468697320636f6e747261637420726570726573656e74730a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e2067657445564d436f6e74726163744164647265737328293a2045564d2e45564d41646472657373207b0a202020202020202072657475726e2073656c662e65766d4e4654436f6e7472616374416464726573730a202020207d0a0a202020202f2f2f2046756e6374696f6e20746861742072657475726e7320616c6c20746865204d6574616461746120566965777320696d706c656d656e7465642062792061204e6f6e2046756e6769626c6520546f6b656e0a202020202f2f2f0a202020202f2f2f204072657475726e20416e206172726179206f6620547970657320646566696e696e672074686520696d706c656d656e7465642076696577732e20546869732076616c75652077696c6c20626520757365642062790a202020202f2f2f202020202020202020646576656c6f7065727320746f206b6e6f7720776869636820706172616d6574657220746f207061737320746f20746865207265736f6c7665566965772829206d6574686f642e0a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e20676574436f6e74726163745669657773287265736f75726365547970653a20547970653f293a205b547970655d207b0a202020202020202072657475726e205b0a202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28292c0a202020202020202020202020547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28292c0a202020202020202020202020547970653c4d6574616461746156696577732e45564d427269646765644d657461646174613e28290a20202020202020205d0a202020207d0a0a202020202f2f2f2046756e6374696f6e2074686174207265736f6c7665732061206d65746164617461207669657720666f72207468697320636f6e74726163742e0a202020202f2f2f0a202020202f2f2f2040706172616d20766965773a205468652054797065206f6620746865206465736972656420766965772e0a202020202f2f2f204072657475726e20412073747275637475726520726570726573656e74696e67207468652072657175657374656420766965772e0a202020202f2f2f0a2020202061636365737328616c6c292066756e207265736f6c7665436f6e747261637456696577287265736f75726365547970653a20547970653f2c2076696577547970653a2054797065293a20416e795374727563743f207b0a2020202020202020737769746368207669657754797065207b0a2020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446174613e28293a0a202020202020202020202020202020206c6574206964656e746966696572203d2022", "436f6c6c656374696f6e220a202020202020202020202020202020206c657420636f6c6c656374696f6e44617461203d204d6574616461746156696577732e4e4654436f6c6c656374696f6e44617461280a202020202020202020202020202020202020202073746f72616765506174683a2053746f7261676550617468286964656e7469666965723a206964656e74696669657229212c0a20202020202020202020202020202020202020207075626c6963506174683a205075626c696350617468286964656e7469666965723a206964656e74696669657229212c0a20202020202020202020202020202020202020207075626c6963436f6c6c656374696f6e3a20547970653c26", "2e436f6c6c656374696f6e3e28292c0a20202020202020202020202020202020202020207075626c69634c696e6b6564547970653a20547970653c26", "2e436f6c6c656374696f6e3e28292c0a2020202020202020202020202020202020202020637265617465456d707479436f6c6c656374696f6e46756e6374696f6e3a202866756e28293a20407b4e6f6e46756e6769626c65546f6b656e2e436f6c6c656374696f6e7d207b0a20202020202020202020202020202020202020202020202072657475726e203c2d", "2e637265617465456d707479436f6c6c656374696f6e286e6674547970653a20547970653c40", - "2e4e46543e2829290a20202020202020202020202020202020202020207d290a20202020202020202020202020202020290a2020202020202020202020202020202072657475726e20636f6c6c656374696f6e446174610a2020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28293a0a202020202020202020202020202020206c6574206d65646961203d204d6574616461746156696577732e4d65646961280a202020202020202020202020202020202020202066696c653a204d6574616461746156696577732e4854545046696c65280a20202020202020202020202020202020202020202020202075726c3a202268747470733a2f2f6173736574732e776562736974652d66696c65732e636f6d2f3566363239346330633761386364643634336231633832302f3566363239346330633761386364613535636231633933365f466c6f775f576f72646d61726b2e737667220a2020202020202020202020202020202020202020292c0a20202020202020202020202020202020202020206d65646961547970653a2022696d6167652f7376672b786d6c220a20202020202020202020202020202020290a2020202020202020202020202020202072657475726e204d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c6179280a20202020202020202020202020202020202020206e616d653a202254686520466c6f77564d2042726964676564204e465420436f6c6c656374696f6e222c0a20202020202020202020202020202020202020206465736372697074696f6e3a20225468697320636f6c6c656374696f6e2077617320627269646765642066726f6d20466c6f772045564d2e222c0a202020202020202020202020202020202020202065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c282268747470733a2f2f6272696467652e666c6f772e636f6d2f6e667422292c0a2020202020202020202020202020202020202020737175617265496d6167653a206d656469612c0a202020202020202020202020202020202020202062616e6e6572496d6167653a206d656469612c0a2020202020202020202020202020202020202020736f6369616c733a207b7d0a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c43726f7373564d4e46542e45564d427269646765644d657461646174613e28293a0a2020202020202020202020202020202072657475726e2043726f7373564d4e46542e45564d427269646765644d65746164617461280a20202020202020202020202020202020202020206e616d653a2073656c662e6e616d652c0a202020202020202020202020202020202020202073796d626f6c3a2073656c662e73796d626f6c2c0a20202020202020202020202020202020202020207572693a2073656c662e636f6e747261637455524920213d206e696c203f2043726f7373564d4e46542e55524928626173655552493a206e696c2c2076616c75653a2073656c662e636f6e74726163745552492129203a2043726f7373564d4e46542e55524928626173655552493a206e696c2c2076616c75653a202222290a20202020202020202020202020202020290a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a2020202020202020496e7465726e616c204d6574686f64730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f20416c6c6f7773207468652062726964676520746f206d696e74204e4654732066726f6d206272696467652d646566696e6564204e465420636f6e7472616374730a202020202f2f2f0a20202020616363657373286163636f756e74290a2020202066756e206d696e744e46542869643a2055496e743235362c20746f6b656e5552493a20537472696e67293a20404e4654207b0a2020202020202020707265207b0a20202020202020202020202073656c662e746f6b656e555249735b69645d203d3d206e696c3a20224120746f6b656e20776974682074686520676976656e2045524337323120494420616c726561647920657869737473220a20202020202020207d0a202020202020202073656c662e746f6b656e555249735b69645d203d20746f6b656e5552490a202020202020202072657475726e203c2d637265617465204e4654280a20202020202020202020202065766d49443a2069642c0a2020202020202020202020206d657461646174613a207b0a20202020202020202020202020202020224272696467656420426c6f636b223a2067657443757272656e74426c6f636b28292e6865696768742c0a2020202020202020202020202020202022427269646765642054696d657374616d70223a2067657443757272656e74426c6f636b28292e74696d657374616d700a2020202020202020202020207d0a2020202020202020290a202020207d0a0a202020202f2f2f20416c6c6f7773207468652062726964676520746f207570646174652074686520555249206f662062726964676564204e4654732e205468697320617373756d65732074686174207468652045564d2d646566696e696e672070726f6a656374206d617920636f6e7461696e0a202020202f2f2f206c6f67696320286f6e636861696e206f72206f6666636861696e292077686963682075706461746573204e4654206d6574616461746120696e2074686520736f757263652045524337323120636f6e74726163742e204f6e206272696467696e672c20746865205552492063616e0a202020202f2f2f207468656e206265207570646174656420696e207468697320636f6e747261637420746f207265666c6563742074686520736f757263652045524337323120636f6e74726163742773206d657461646174612e0a202020202f2f2f0a20202020616363657373286163636f756e74290a2020202066756e20757064617465546f6b656e5552492865766d49443a2055496e743235362c206e65775552493a20537472696e6729207b0a2020202020202020707265207b0a20202020202020202020202073656c662e746f6b656e555249735b65766d49445d20213d206e696c3a20224e6f20746f6b656e20776974682074686520676976656e2045524337323120494420657869737473220a20202020202020207d0a202020202020202069662073656c662e746f6b656e555249735b65766d49445d20213d206e6577555249207b0a20202020202020202020202073656c662e746f6b656e555249735b65766d49445d203d206e65775552490a20202020202020207d0a202020207d0a0a20202020696e6974286e616d653a20537472696e672c2073796d626f6c3a20537472696e672c2065766d436f6e7472616374416464726573733a2045564d2e45564d416464726573732c20636f6e74726163745552493a20537472696e673f29207b0a202020202020202073656c662e65766d4e4654436f6e747261637441646472657373203d2065766d436f6e7472616374416464726573730a202020202020202073656c662e6e616d65203d206e616d650a202020202020202073656c662e73796d626f6c203d2073796d626f6c0a202020202020202073656c662e636f6e7472616374555249203d20636f6e74726163745552490a202020202020202073656c662e746f6b656e55524973203d207b7d0a202020202020202073656c662e636f6c6c656374696f6e203c2d2063726561746520436f6c6c656374696f6e28290a0a2020202020202020466c6f7745564d427269646765436f6e6669672e6173736f63696174655479706528547970653c40", + "2e4e46543e2829290a20202020202020202020202020202020202020207d290a20202020202020202020202020202020290a2020202020202020202020202020202072657475726e20636f6c6c656374696f6e446174610a2020202020202020202020206361736520547970653c4d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c61793e28293a0a202020202020202020202020202020206c6574206d65646961203d204d6574616461746156696577732e4d65646961280a202020202020202020202020202020202020202066696c653a204d6574616461746156696577732e4854545046696c65280a20202020202020202020202020202020202020202020202075726c3a202268747470733a2f2f6173736574732e776562736974652d66696c65732e636f6d2f3566363239346330633761386364643634336231633832302f3566363239346330633761386364613535636231633933365f466c6f775f576f72646d61726b2e737667220a2020202020202020202020202020202020202020292c0a20202020202020202020202020202020202020206d65646961547970653a2022696d6167652f7376672b786d6c220a20202020202020202020202020202020290a2020202020202020202020202020202072657475726e204d6574616461746156696577732e4e4654436f6c6c656374696f6e446973706c6179280a20202020202020202020202020202020202020206e616d653a202254686520466c6f77564d2042726964676564204e465420436f6c6c656374696f6e222c0a20202020202020202020202020202020202020206465736372697074696f6e3a20225468697320636f6c6c656374696f6e2077617320627269646765642066726f6d20466c6f772045564d2e222c0a202020202020202020202020202020202020202065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c282268747470733a2f2f6272696467652e666c6f772e636f6d2f6e667422292c0a2020202020202020202020202020202020202020737175617265496d6167653a206d656469612c0a202020202020202020202020202020202020202062616e6e6572496d6167653a206d656469612c0a2020202020202020202020202020202020202020736f6369616c733a207b7d0a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c4d6574616461746156696577732e45564d427269646765644d657461646174613e28293a0a2020202020202020202020202020202072657475726e204d6574616461746156696577732e45564d427269646765644d65746164617461280a20202020202020202020202020202020202020206e616d653a2073656c662e6e616d652c0a202020202020202020202020202020202020202073796d626f6c3a2073656c662e73796d626f6c2c0a20202020202020202020202020202020202020207572693a2073656c662e636f6e747261637455524920213d206e696c203f204d6574616461746156696577732e55524928626173655552493a206e696c2c2076616c75653a2073656c662e636f6e74726163745552492129203a204d6574616461746156696577732e55524928626173655552493a206e696c2c2076616c75653a202222290a20202020202020202020202020202020290a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a2020202020202020496e7465726e616c204d6574686f64730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f20416c6c6f7773207468652062726964676520746f206d696e74204e4654732066726f6d206272696467652d646566696e6564204e465420636f6e7472616374730a202020202f2f2f0a20202020616363657373286163636f756e74290a2020202066756e206d696e744e46542869643a2055496e743235362c20746f6b656e5552493a20537472696e67293a20404e4654207b0a2020202020202020707265207b0a20202020202020202020202073656c662e746f6b656e555249735b69645d203d3d206e696c3a20224120746f6b656e20776974682074686520676976656e2045524337323120494420616c726561647920657869737473220a20202020202020207d0a202020202020202073656c662e746f6b656e555249735b69645d203d20746f6b656e5552490a202020202020202072657475726e203c2d637265617465204e4654280a20202020202020202020202065766d49443a2069642c0a2020202020202020202020206d657461646174613a207b0a20202020202020202020202020202020224272696467656420426c6f636b223a2067657443757272656e74426c6f636b28292e6865696768742c0a2020202020202020202020202020202022427269646765642054696d657374616d70223a2067657443757272656e74426c6f636b28292e74696d657374616d700a2020202020202020202020207d0a2020202020202020290a202020207d0a0a202020202f2f2f20416c6c6f7773207468652062726964676520746f207570646174652074686520555249206f662062726964676564204e4654732e205468697320617373756d65732074686174207468652045564d2d646566696e696e672070726f6a656374206d617920636f6e7461696e0a202020202f2f2f206c6f67696320286f6e636861696e206f72206f6666636861696e292077686963682075706461746573204e4654206d6574616461746120696e2074686520736f757263652045524337323120636f6e74726163742e204f6e206272696467696e672c20746865205552492063616e0a202020202f2f2f207468656e206265207570646174656420696e207468697320636f6e747261637420746f207265666c6563742074686520736f757263652045524337323120636f6e74726163742773206d657461646174612e0a202020202f2f2f0a20202020616363657373286163636f756e74290a2020202066756e20757064617465546f6b656e5552492865766d49443a2055496e743235362c206e65775552493a20537472696e6729207b0a2020202020202020707265207b0a20202020202020202020202073656c662e746f6b656e555249735b65766d49445d20213d206e696c3a20224e6f20746f6b656e20776974682074686520676976656e2045524337323120494420657869737473220a20202020202020207d0a202020202020202069662073656c662e746f6b656e555249735b65766d49445d20213d206e6577555249207b0a20202020202020202020202073656c662e746f6b656e555249735b65766d49445d203d206e65775552490a20202020202020207d0a202020207d0a0a20202020696e6974286e616d653a20537472696e672c2073796d626f6c3a20537472696e672c2065766d436f6e7472616374416464726573733a2045564d2e45564d416464726573732c20636f6e74726163745552493a20537472696e673f29207b0a202020202020202073656c662e65766d4e4654436f6e747261637441646472657373203d2065766d436f6e7472616374416464726573730a202020202020202073656c662e6e616d65203d206e616d650a202020202020202073656c662e73796d626f6c203d2073796d626f6c0a202020202020202073656c662e636f6e7472616374555249203d20636f6e74726163745552490a202020202020202073656c662e746f6b656e55524973203d207b7d0a202020202020202073656c662e636f6c6c656374696f6e203c2d2063726561746520436f6c6c656374696f6e28290a0a2020202020202020466c6f7745564d427269646765436f6e6669672e6173736f63696174655479706528547970653c40", "2e4e46543e28292c20776974683a2073656c662e65766d4e4654436f6e747261637441646472657373290a2020202020202020466c6f7745564d4272696467654e4654457363726f772e696e697469616c697a65457363726f77280a202020202020202020202020666f72547970653a20547970653c40", "2e4e46543e28292c0a2020202020202020202020206e616d653a206e616d652c0a20202020202020202020202073796d626f6c3a2073796d626f6c2c0a202020202020202020202020657263373231416464726573733a2073656c662e65766d4e4654436f6e7472616374416464726573730a2020202020202020290a202020207d0a7d0a" ] @@ -62,16 +64,21 @@ access(all) let bridgedTokenCodeChunks = [ "2e7265736f6c7665436f6e747261637456696577287265736f75726365547970653a206e696c2c2076696577547970653a2076696577290a20202020202020207d0a0a20202020202020202f2f2f20676574537570706f727465645661756c745479706573206f7074696f6e616c6c792072657475726e732061206c697374206f66207661756c742074797065732074686174207468697320726563656976657220616363657074730a202020202020202061636365737328616c6c2920766965772066756e20676574537570706f727465645661756c74547970657328293a207b547970653a20426f6f6c7d207b0a20202020202020202020202072657475726e207b2073656c662e6765745479706528293a2074727565207d0a20202020202020207d0a0a202020202020202061636365737328616c6c2920766965772066756e206973537570706f727465645661756c745479706528747970653a2054797065293a20426f6f6c207b0a20202020202020202020202072657475726e2073656c662e676574537570706f727465645661756c74547970657328295b747970655d203f3f2066616c73650a20202020202020207d0a0a20202020202020202f2f2f2041736b732069662074686520616d6f756e742063616e2062652077697468647261776e2066726f6d2074686973207661756c740a202020202020202061636365737328616c6c2920766965772066756e206973417661696c61626c65546f576974686472617728616d6f756e743a20554669783634293a20426f6f6c207b0a20202020202020202020202072657475726e20616d6f756e74203c3d2073656c662e62616c616e63650a20202020202020207d0a0a20202020202020202f2f2f206465706f7369740a20202020202020202f2f2f0a20202020202020202f2f2f2046756e6374696f6e20746861742074616b65732061205661756c74206f626a65637420617320616e20617267756d656e7420616e6420616464730a20202020202020202f2f2f206974732062616c616e636520746f207468652062616c616e6365206f6620746865206f776e657273205661756c742e0a20202020202020202f2f2f0a20202020202020202f2f2f20497420697320616c6c6f77656420746f2064657374726f79207468652073656e74205661756c74206265636175736520746865205661756c740a20202020202020202f2f2f2077617320612074656d706f7261727920686f6c646572206f662074686520746f6b656e732e20546865205661756c7427732062616c616e6365206861730a20202020202020202f2f2f206265656e20636f6e73756d656420616e64207468657265666f72652063616e2062652064657374726f7965642e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c292066756e206465706f7369742866726f6d3a20407b46756e6769626c65546f6b656e2e5661756c747d29207b0a2020202020202020202020206c6574207661756c74203c2d2066726f6d2061732120405661756c740a20202020202020202020202073656c662e62616c616e6365203d2073656c662e62616c616e6365202b207661756c742e62616c616e63650a2020202020202020202020207661756c742e62616c616e6365203d20302e300a20202020202020202020202064657374726f79207661756c740a20202020202020207d0a0a20202020202020202f2f2f20637265617465456d7074795661756c740a20202020202020202f2f2f0a20202020202020202f2f2f2046756e6374696f6e207468617420637265617465732061206e6577205661756c74207769746820612062616c616e6365206f66207a65726f0a20202020202020202f2f2f20616e642072657475726e7320697420746f207468652063616c6c696e6720636f6e746578742e20412075736572206d7573742063616c6c20746869732066756e6374696f6e0a20202020202020202f2f2f20616e642073746f7265207468652072657475726e6564205661756c7420696e2074686569722073746f7261676520696e206f7264657220746f20616c6c6f772074686569720a20202020202020202f2f2f206163636f756e7420746f2062652061626c6520746f2072656365697665206465706f73697473206f66207468697320746f6b656e20747970652e0a20202020202020202f2f2f0a202020202020202061636365737328616c6c292066756e20637265617465456d7074795661756c7428293a20405661756c74207b0a20202020202020202020202072657475726e203c2d637265617465205661756c742862616c616e63653a20302e30290a20202020202020207d0a0a20202020202020202f2f2f2077697468647261770a20202020202020202f2f2f0a20202020202020202f2f2f2046756e6374696f6e20746861742074616b657320616e20616d6f756e7420617320616e20617267756d656e740a20202020202020202f2f2f20616e6420776974686472617773207468617420616d6f756e742066726f6d20746865205661756c742e0a20202020202020202f2f2f0a20202020202020202f2f2f20497420637265617465732061206e65772074656d706f72617279205661756c742074686174206973207573656420746f20686f6c640a20202020202020202f2f2f2074686520746f6b656e73207468617420617265206265696e67207472616e736665727265642e2049742072657475726e7320746865206e65776c790a20202020202020202f2f2f2063726561746564205661756c7420746f2074686520636f6e7465787420746861742063616c6c656420736f2069742063616e206265206465706f73697465640a20202020202020202f2f2f20656c736577686572652e0a20202020202020202f2f2f0a20202020202020206163636573732846756e6769626c65546f6b656e2e5769746864726177292066756e20776974686472617728616d6f756e743a20554669783634293a20405661756c74207b0a20202020202020202020202073656c662e62616c616e6365203d2073656c662e62616c616e6365202d20616d6f756e740a20202020202020202020202072657475726e203c2d637265617465205661756c742862616c616e63653a20616d6f756e74290a20202020202020207d0a0a20202020202020202f2f2f2043616c6c6564207768656e20612066756e6769626c6520746f6b656e206973206275726e6564207669612074686520604275726e65722e6275726e282960206d6574686f640a202020202020202061636365737328636f6e7472616374292066756e206275726e43616c6c6261636b2829207b0a20202020202020202020202069662073656c662e62616c616e6365203e20302e30207b0a20202020202020202020202020202020", "2e746f74616c537570706c79203d20", "2e746f74616c537570706c79202d2073656c662e62616c616e63650a2020202020202020202020207d0a20202020202020202020202073656c662e62616c616e6365203d20302e300a20202020202020207d0a202020207d0a0a202020202f2f2f20637265617465456d7074795661756c740a202020202f2f2f0a202020202f2f2f2046756e6374696f6e207468617420637265617465732061206e6577205661756c74207769746820612062616c616e6365206f66207a65726f20616e642072657475726e7320697420746f207468652063616c6c696e6720636f6e746578742e20412075736572206d7573742063616c6c0a202020202f2f2f20746869732066756e6374696f6e20616e642073746f7265207468652072657475726e6564205661756c7420696e2074686569722073746f7261676520696e206f7264657220746f20616c6c6f77207468656972206163636f756e7420746f2062652061626c6520746f0a202020202f2f2f2072656365697665206465706f73697473206f66207468697320746f6b656e20747970652e0a202020202f2f2f0a2020202061636365737328616c6c292066756e20637265617465456d7074795661756c74287661756c74547970653a2054797065293a2040", - "2e5661756c74207b0a202020202020202072657475726e203c2d20637265617465205661756c742862616c616e63653a20302e30290a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a202020202020202020202020476574746572730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f2052657475726e73207468652045564d20636f6e74726163742061646472657373206f66207468652066756e6769626c6520746f6b656e207468697320636f6e747261637420726570726573656e74730a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e2067657445564d436f6e74726163744164647265737328293a2045564d2e45564d41646472657373207b0a202020202020202072657475726e2073656c662e65766d546f6b656e436f6e7472616374416464726573730a202020207d0a0a202020202f2f2f2046756e6374696f6e20746861742072657475726e7320616c6c20746865204d6574616461746120566965777320696d706c656d656e74656420627920746869732066756e6769626c6520746f6b656e20636f6e74726163742e0a202020202f2f2f0a202020202f2f2f204072657475726e20416e206172726179206f6620547970657320646566696e696e672074686520696d706c656d656e7465642076696577732e20546869732076616c75652077696c6c20626520757365642062790a202020202f2f2f202020202020202020646576656c6f7065727320746f206b6e6f7720776869636820706172616d6574657220746f207061737320746f20746865207265736f6c7665566965772829206d6574686f642e0a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e20676574436f6e74726163745669657773287265736f75726365547970653a20547970653f293a205b547970655d207b0a202020202020202072657475726e205b0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654566965773e28292c0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793e28292c0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613e28292c0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e546f74616c537570706c793e28292c0a202020202020202020202020547970653c43726f7373564d4e46542e45564d427269646765644d657461646174613e28290a20202020202020205d0a202020207d0a0a202020202f2f2f2046756e6374696f6e2074686174207265736f6c7665732061206d65746164617461207669657720666f72207468697320636f6e74726163742e0a202020202f2f2f0a202020202f2f2f2040706172616d20766965773a205468652054797065206f6620746865206465736972656420766965772e0a202020202f2f2f0a202020202f2f2f204072657475726e20412073747275637475726520726570726573656e74696e67207468652072657175657374656420766965772e0a202020202f2f2f0a2020202061636365737328616c6c292066756e207265736f6c7665436f6e747261637456696577287265736f75726365547970653a20547970653f2c2076696577547970653a2054797065293a20416e795374727563743f207b0a2020202020202020737769746368207669657754797065207b0a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654566965773e28293a0a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e465456696577280a20202020202020202020202020202020202020206674446973706c61793a2073656c662e7265736f6c7665436f6e747261637456696577287265736f75726365547970653a206e696c2c2076696577547970653a20547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793e282929206173212046756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793f2c0a202020202020202020202020202020202020202066745661756c74446174613a2073656c662e7265736f6c7665436f6e747261637456696577287265736f75726365547970653a206e696c2c2076696577547970653a20547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613e282929206173212046756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613f0a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793e28293a0a202020202020202020202020202020206c6574206d65646961203d204d6574616461746156696577732e4d65646961280a20202020202020202020202020202020202020202020202066696c653a204d6574616461746156696577732e4854545046696c65280a20202020202020202020202020202020202020202020202075726c3a202268747470733a2f2f6173736574732e776562736974652d66696c65732e636f6d2f3566363239346330633761386364643634336231633832302f3566363239346330633761386364613535636231633933365f466c6f775f576f72646d61726b2e737667220a2020202020202020202020202020202020202020292c0a20202020202020202020202020202020202020206d65646961547970653a2022696d6167652f7376672b786d6c220a20202020202020202020202020202020290a202020202020202020202020202020206c6574206d6564696173203d204d6574616461746156696577732e4d6564696173285b6d656469615d290a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c6179280a20202020202020202020202020202020202020202f2f20544f444f3a20446563696465206f6e20686f772077652077616e7420746f20726570726573656e74206272696467656420746f6b656e206d656469610a20202020202020202020202020202020202020206e616d653a2073656c662e6e616d652c0a202020202020202020202020202020202020202073796d626f6c3a2073656c662e73796d626f6c2c0a20202020202020202020202020202020202020206465736372697074696f6e3a2022546869732066756e6769626c6520746f6b656e2077617320627269646765642066726f6d20466c6f772045564d2e222c0a202020202020202020202020202020202020202065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c282268747470733a2f2f6272696467652e666c6f772e636f6d2f66756e6769626c652d746f6b656e22292c0a20202020202020202020202020202020202020206c6f676f733a206d65646961732c0a2020202020202020202020202020202020202020736f6369616c733a207b7d0a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613e28293a0a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c7444617461280a202020202020202020202020202020202020202073746f72616765506174683a202f73746f726167652f", + "2e5661756c74207b0a202020202020202072657475726e203c2d20637265617465205661756c742862616c616e63653a20302e30290a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a202020202020202020202020476574746572730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f2052657475726e73207468652045564d20636f6e74726163742061646472657373206f66207468652066756e6769626c6520746f6b656e207468697320636f6e747261637420726570726573656e74730a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e2067657445564d436f6e74726163744164647265737328293a2045564d2e45564d41646472657373207b0a202020202020202072657475726e2073656c662e65766d546f6b656e436f6e7472616374416464726573730a202020207d0a0a202020202f2f2f2046756e6374696f6e20746861742072657475726e7320616c6c20746865204d6574616461746120566965777320696d706c656d656e74656420627920746869732066756e6769626c6520746f6b656e20636f6e74726163742e0a202020202f2f2f0a202020202f2f2f204072657475726e20416e206172726179206f6620547970657320646566696e696e672074686520696d706c656d656e7465642076696577732e20546869732076616c75652077696c6c20626520757365642062790a202020202f2f2f202020202020202020646576656c6f7065727320746f206b6e6f7720776869636820706172616d6574657220746f207061737320746f20746865207265736f6c7665566965772829206d6574686f642e0a202020202f2f2f0a2020202061636365737328616c6c2920766965772066756e20676574436f6e74726163745669657773287265736f75726365547970653a20547970653f293a205b547970655d207b0a202020202020202072657475726e205b0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654566965773e28292c0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793e28292c0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613e28292c0a202020202020202020202020547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e546f74616c537570706c793e28292c0a202020202020202020202020547970653c4d6574616461746156696577732e45564d427269646765644d657461646174613e28290a20202020202020205d0a202020207d0a0a202020202f2f2f2046756e6374696f6e2074686174207265736f6c7665732061206d65746164617461207669657720666f72207468697320636f6e74726163742e0a202020202f2f2f0a202020202f2f2f2040706172616d20766965773a205468652054797065206f6620746865206465736972656420766965772e0a202020202f2f2f0a202020202f2f2f204072657475726e20412073747275637475726520726570726573656e74696e67207468652072657175657374656420766965772e0a202020202f2f2f0a2020202061636365737328616c6c292066756e207265736f6c7665436f6e747261637456696577287265736f75726365547970653a20547970653f2c2076696577547970653a2054797065293a20416e795374727563743f207b0a2020202020202020737769746368207669657754797065207b0a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654566965773e28293a0a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e465456696577280a20202020202020202020202020202020202020206674446973706c61793a2073656c662e7265736f6c7665436f6e747261637456696577287265736f75726365547970653a206e696c2c2076696577547970653a20547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793e282929206173212046756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793f2c0a202020202020202020202020202020202020202066745661756c74446174613a2073656c662e7265736f6c7665436f6e747261637456696577287265736f75726365547970653a206e696c2c2076696577547970653a20547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613e282929206173212046756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613f0a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c61793e28293a0a202020202020202020202020202020206c6574206d65646961203d204d6574616461746156696577732e4d65646961280a20202020202020202020202020202020202020202020202066696c653a204d6574616461746156696577732e4854545046696c65280a20202020202020202020202020202020202020202020202075726c3a202268747470733a2f2f6173736574732e776562736974652d66696c65732e636f6d2f3566363239346330633761386364643634336231633832302f3566363239346330633761386364613535636231633933365f466c6f775f576f72646d61726b2e737667220a2020202020202020202020202020202020202020292c0a20202020202020202020202020202020202020206d65646961547970653a2022696d6167652f7376672b786d6c220a20202020202020202020202020202020290a202020202020202020202020202020206c6574206d6564696173203d204d6574616461746156696577732e4d6564696173285b6d656469615d290a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e4654446973706c6179280a20202020202020202020202020202020202020202f2f20544f444f3a20446563696465206f6e20686f772077652077616e7420746f20726570726573656e74206272696467656420746f6b656e206d656469610a20202020202020202020202020202020202020206e616d653a2073656c662e6e616d652c0a202020202020202020202020202020202020202073796d626f6c3a2073656c662e73796d626f6c2c0a20202020202020202020202020202020202020206465736372697074696f6e3a2022546869732066756e6769626c6520746f6b656e2077617320627269646765642066726f6d20466c6f772045564d2e222c0a202020202020202020202020202020202020202065787465726e616c55524c3a204d6574616461746156696577732e45787465726e616c55524c282268747470733a2f2f6272696467652e666c6f772e636f6d2f66756e6769626c652d746f6b656e22292c0a20202020202020202020202020202020202020206c6f676f733a206d65646961732c0a2020202020202020202020202020202020202020736f6369616c733a207b7d0a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c74446174613e28293a0a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e46545661756c7444617461280a202020202020202020202020202020202020202073746f72616765506174683a202f73746f726167652f", "5661756c742c0a20202020202020202020202020202020202020207265636569766572506174683a202f7075626c69632f", "52656365697665722c0a20202020202020202020202020202020202020206d65746164617461506174683a202f7075626c69632f", "5661756c742c0a202020202020202020202020202020202020202072656365697665724c696e6b6564547970653a20547970653c26", "2e5661756c743e28292c0a20202020202020202020202020202020202020206d657461646174614c696e6b6564547970653a20547970653c26", "2e5661756c743e28292c0a2020202020202020202020202020202020202020637265617465456d7074795661756c7446756e6374696f6e3a202866756e28293a20407b46756e6769626c65546f6b656e2e5661756c747d207b0a20202020202020202020202020202020202020202020202072657475726e203c2d73656c662e637265617465456d7074795661756c74287661756c74547970653a20547970653c40", - "2e5661756c743e2829290a20202020202020202020202020202020202020207d290a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e546f74616c537570706c793e28293a0a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e546f74616c537570706c79280a2020202020202020202020202020202020202020746f74616c537570706c793a2073656c662e746f74616c537570706c790a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c43726f7373564d4e46542e45564d427269646765644d657461646174613e28293a0a2020202020202020202020202020202072657475726e2043726f7373564d4e46542e45564d427269646765644d65746164617461280a20202020202020202020202020202020202020206e616d653a2073656c662e6e616d652c0a202020202020202020202020202020202020202073796d626f6c3a2073656c662e73796d626f6c2c0a20202020202020202020202020202020202020207572693a2073656c662e636f6e747261637455524920213d206e696c203f2043726f7373564d4e46542e55524928626173655552493a206e696c2c2076616c75653a2073656c662e636f6e74726163745552492129203a2043726f7373564d4e46542e55524928626173655552493a206e696c2c2076616c75653a202222290a20202020202020202020202020202020290a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a2020202020202020496e7465726e616c204d6574686f64730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f20416c6c6f7773207468652062726964676520746f206d696e7420746f6b656e732066726f6d206272696467652d646566696e65642066756e6769626c6520746f6b656e20636f6e7472616374730a202020202f2f2f0a20202020616363657373286163636f756e74292066756e206d696e74546f6b656e7328616d6f756e743a20554669783634293a20407b46756e6769626c65546f6b656e2e5661756c747d207b0a202020202020202073656c662e746f74616c537570706c79203d2073656c662e746f74616c537570706c79202b20616d6f756e740a202020202020202072657475726e203c2d20637265617465205661756c742862616c616e63653a20616d6f756e74290a202020207d0a0a20202020696e6974286e616d653a20537472696e672c2073796d626f6c3a20537472696e672c20646563696d616c733a2055496e74382c2065766d436f6e7472616374416464726573733a2045564d2e45564d416464726573732c20636f6e74726163745552493a20537472696e673f29207b0a202020202020202073656c662e65766d546f6b656e436f6e747261637441646472657373203d2065766d436f6e7472616374416464726573730a202020202020202073656c662e6e616d65203d206e616d650a202020202020202073656c662e73796d626f6c203d2073796d626f6c0a202020202020202073656c662e646563696d616c73203d20646563696d616c730a202020202020202073656c662e636f6e7472616374555249203d20636f6e74726163745552490a202020202020202073656c662e746f74616c537570706c79203d20302e300a202020202020202073656c662e7661756c74203c2d20637265617465205661756c742862616c616e63653a20302e30290a0a2020202020202020466c6f7745564d427269646765436f6e6669672e6173736f63696174655479706528547970653c40", + "2e5661756c743e2829290a20202020202020202020202020202020202020207d290a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c46756e6769626c65546f6b656e4d6574616461746156696577732e546f74616c537570706c793e28293a0a2020202020202020202020202020202072657475726e2046756e6769626c65546f6b656e4d6574616461746156696577732e546f74616c537570706c79280a2020202020202020202020202020202020202020746f74616c537570706c793a2073656c662e746f74616c537570706c790a20202020202020202020202020202020290a2020202020202020202020206361736520547970653c4d6574616461746156696577732e45564d427269646765644d657461646174613e28293a0a2020202020202020202020202020202072657475726e204d6574616461746156696577732e45564d427269646765644d65746164617461280a20202020202020202020202020202020202020206e616d653a2073656c662e6e616d652c0a202020202020202020202020202020202020202073796d626f6c3a2073656c662e73796d626f6c2c0a20202020202020202020202020202020202020207572693a2073656c662e636f6e747261637455524920213d206e696c203f204d6574616461746156696577732e55524928626173655552493a206e696c2c2076616c75653a2073656c662e636f6e74726163745552492129203a204d6574616461746156696577732e55524928626173655552493a206e696c2c2076616c75653a202222290a20202020202020202020202020202020290a20202020202020207d0a202020202020202072657475726e206e696c0a202020207d0a0a202020202f2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0a2020202020202020496e7465726e616c204d6574686f64730a202020202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2f0a0a202020202f2f2f20416c6c6f7773207468652062726964676520746f206d696e7420746f6b656e732066726f6d206272696467652d646566696e65642066756e6769626c6520746f6b656e20636f6e7472616374730a202020202f2f2f0a20202020616363657373286163636f756e74292066756e206d696e74546f6b656e7328616d6f756e743a20554669783634293a20407b46756e6769626c65546f6b656e2e5661756c747d207b0a202020202020202073656c662e746f74616c537570706c79203d2073656c662e746f74616c537570706c79202b20616d6f756e740a202020202020202072657475726e203c2d20637265617465205661756c742862616c616e63653a20616d6f756e74290a202020207d0a0a20202020696e6974286e616d653a20537472696e672c2073796d626f6c3a20537472696e672c20646563696d616c733a2055496e74382c2065766d436f6e7472616374416464726573733a2045564d2e45564d416464726573732c20636f6e74726163745552493a20537472696e673f29207b0a202020202020202073656c662e65766d546f6b656e436f6e747261637441646472657373203d2065766d436f6e7472616374416464726573730a202020202020202073656c662e6e616d65203d206e616d650a202020202020202073656c662e73796d626f6c203d2073796d626f6c0a202020202020202073656c662e646563696d616c73203d20646563696d616c730a202020202020202073656c662e636f6e7472616374555249203d20636f6e74726163745552490a202020202020202073656c662e746f74616c537570706c79203d20302e300a202020202020202073656c662e7661756c74203c2d20637265617465205661756c742862616c616e63653a20302e30290a0a2020202020202020466c6f7745564d427269646765436f6e6669672e6173736f63696174655479706528547970653c40", "2e5661756c743e28292c20776974683a2073656c662e65766d546f6b656e436f6e747261637441646472657373290a2020202020202020466c6f7745564d427269646765546f6b656e457363726f772e696e697469616c697a65457363726f77280a202020202020202020202020776974683a203c2d637265617465205661756c742862616c616e63653a20302e30292c0a2020202020202020202020206e616d653a206e616d652c0a20202020202020202020202073796d626f6c3a2073796d626f6c2c0a202020202020202020202020646563696d616c733a20646563696d616c732c0a20202020202020202020202065766d546f6b656e416464726573733a2073656c662e65766d546f6b656e436f6e7472616374416464726573730a2020202020202020290a202020207d0a7d0a" ] +access(all) +fun getMetadataViewsUpdateCode(): String { + return metadataViewsUpdateCode +} + access(all) fun getEVMUpdateCode(): String { return evmUpdateCode @@ -162,7 +169,7 @@ fun getAssociatedEVMAddressHex(with typeIdentifier: String): String { access(all) fun getDeployedAddressFromDeployer(name: String): String { let erc721AddressResult = _executeScript( - "../scripts/test/get_deployed_address_string_from_deployer.cdc", + "./scripts/get_deployed_address_string_from_deployer.cdc", [name] ) Test.expect(erc721AddressResult, Test.beSucceeded()) @@ -319,13 +326,19 @@ fun bridgeNFTToEVM( contractAddr: Address, contractName: String, nftID: UInt64, - bridgeAccountAddr: Address + bridgeAccountAddr: Address, + beFailed: Bool ) { let bridgeResult = _executeTransaction( "../transactions/bridge/nft/bridge_nft_to_evm.cdc", [contractAddr, contractName, nftID], signer ) + if beFailed { + Test.expect(bridgeResult, Test.beFailed()) + return + } + Test.expect(bridgeResult, Test.beSucceeded()) var events = Test.eventsOfType(Type()) @@ -345,13 +358,19 @@ fun bridgeNFTFromEVM( contractAddr: Address, contractName: String, erc721ID: UInt256, - bridgeAccountAddr: Address + bridgeAccountAddr: Address, + beFailed: Bool ) { let bridgeResult = _executeTransaction( "../transactions/bridge/nft/bridge_nft_from_evm.cdc", [contractAddr, contractName, erc721ID], signer ) + if beFailed { + Test.expect(bridgeResult, Test.beFailed()) + return + } + Test.expect(bridgeResult, Test.beSucceeded()) var events = Test.eventsOfType(Type()) @@ -364,7 +383,13 @@ fun bridgeNFTFromEVM( } access(all) -fun bridgeTokensToEVM(signer: Test.TestAccount, contractAddr: Address, contractName: String, amount: UFix64, beFailed: Bool) { +fun bridgeTokensToEVM( + signer: Test.TestAccount, + contractAddr: Address, + contractName: String, + amount: UFix64, + beFailed: Bool +) { let bridgeResult = _executeTransaction( "../transactions/bridge/tokens/bridge_tokens_to_evm.cdc", [contractAddr, contractName, amount], @@ -377,7 +402,13 @@ fun bridgeTokensToEVM(signer: Test.TestAccount, contractAddr: Address, contractN } access(all) -fun bridgeTokensFromEVM(signer: Test.TestAccount, contractAddr: Address, contractName: String, amount: UInt256, beFailed: Bool) { +fun bridgeTokensFromEVM( + signer: Test.TestAccount, + contractAddr: Address, + contractName: String, + amount: UInt256, + beFailed: Bool +) { let bridgeResult = _executeTransaction( "../transactions/bridge/tokens/bridge_tokens_from_evm.cdc", [contractAddr, contractName, amount], diff --git a/cadence/transactions/test/add_contract.cdc b/cadence/tests/transactions/add_contract.cdc similarity index 100% rename from cadence/transactions/test/add_contract.cdc rename to cadence/tests/transactions/add_contract.cdc diff --git a/cadence/transactions/test/deploy_using_evm_deployer.cdc b/cadence/tests/transactions/deploy_using_evm_deployer.cdc similarity index 100% rename from cadence/transactions/test/deploy_using_evm_deployer.cdc rename to cadence/tests/transactions/deploy_using_evm_deployer.cdc diff --git a/cadence/transactions/test/update_contract.cdc b/cadence/tests/transactions/update_contract.cdc similarity index 100% rename from cadence/transactions/test/update_contract.cdc rename to cadence/tests/transactions/update_contract.cdc diff --git a/cadence/transactions/bridge/admin/pause/update_bridge_pause_status.cdc b/cadence/transactions/bridge/admin/pause/update_bridge_pause_status.cdc new file mode 100644 index 00000000..92a742e8 --- /dev/null +++ b/cadence/transactions/bridge/admin/pause/update_bridge_pause_status.cdc @@ -0,0 +1,25 @@ +import "FlowEVMBridgeConfig" + +/// Sets the pause status of the FlowEVM Bridge as specified. +/// +/// @param pause: A boolean indicating whether the FlowEVM Bridge should be paused or unpaused. +/// +/// @emits FlowEVMBridgeConfig.PauseStatusUpdated(paused: true) +/// +transaction(pause: Bool) { + + let admin: auth(FlowEVMBridgeConfig.Pause) &FlowEVMBridgeConfig.Admin + + prepare(signer: auth(BorrowValue) &Account) { + self.admin = signer.storage.borrow(from: FlowEVMBridgeConfig.adminStoragePath) + ?? panic("Could not borrow FlowEVMBridgeConfig Admin reference") + } + + execute { + if pause { + self.admin.pauseBridge() + } else { + self.admin.unpauseBridge() + } + } +} diff --git a/flow.json b/flow.json index 48151cd6..165a4824 100644 --- a/flow.json +++ b/flow.json @@ -8,8 +8,8 @@ "testing": "0000000000000007" } }, - "BridgePermissions": { - "source": "./cadence/contracts/bridge/BridgePermissions.cdc", + "IBridgePermissions": { + "source": "./cadence/contracts/bridge/interfaces/IBridgePermissions.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -25,7 +25,7 @@ } }, "CrossVMNFT": { - "source": "./cadence/contracts/bridge/CrossVMNFT.cdc", + "source": "./cadence/contracts/bridge/interfaces/CrossVMNFT.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -33,7 +33,7 @@ } }, "CrossVMToken": { - "source": "./cadence/contracts/bridge/CrossVMToken.cdc", + "source": "./cadence/contracts/bridge/interfaces/CrossVMToken.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -48,7 +48,7 @@ } }, "EVMDeployer": { - "source": "./cadence/contracts/test/EVMDeployer.cdc", + "source": "./tests/contracts/EVMDeployer.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "testing": "0000000000000009" @@ -108,7 +108,7 @@ } }, "FlowEVMBridgeHandlerInterfaces": { - "source": "./cadence/contracts/bridge/FlowEVMBridgeHandlerInterfaces.cdc", + "source": "./cadence/contracts/bridge/interfaces/FlowEVMBridgeHandlerInterfaces.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "testing": "0000000000000007" @@ -185,7 +185,7 @@ "FungibleTokenMetadataViews": { "source": "./cadence/contracts/standards/FungibleTokenMetadataViews.cdc", "aliases": { - "emulator": "f8d6e0586b0a20c7", + "emulator": "0xee82856bf20e2aa6", "mainnet": "f233dcee88fe0abe", "previewnet": "a0225e7000ac82a9", "testing": "0000000000000002", @@ -193,7 +193,7 @@ } }, "ICrossVM": { - "source": "./cadence/contracts/bridge/ICrossVM.cdc", + "source": "./cadence/contracts/bridge/interfaces/ICrossVM.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -201,7 +201,7 @@ } }, "IEVMBridgeNFTMinter": { - "source": "./cadence/contracts/bridge/IEVMBridgeNFTMinter.cdc", + "source": "./cadence/contracts/bridge/interfaces/IEVMBridgeNFTMinter.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -209,7 +209,7 @@ } }, "IEVMBridgeTokenMinter": { - "source": "./cadence/contracts/bridge/IEVMBridgeTokenMinter.cdc", + "source": "./cadence/contracts/bridge/interfaces/IEVMBridgeTokenMinter.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -217,7 +217,7 @@ } }, "IFlowEVMNFTBridge": { - "source": "./cadence/contracts/bridge/IFlowEVMNFTBridge.cdc", + "source": "./cadence/contracts/bridge/interfaces/IFlowEVMNFTBridge.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -225,7 +225,7 @@ } }, "IFlowEVMTokenBridge": { - "source": "./cadence/contracts/bridge/IFlowEVMTokenBridge.cdc", + "source": "./cadence/contracts/bridge/interfaces/IFlowEVMTokenBridge.cdc", "aliases": { "emulator": "f8d6e0586b0a20c7", "previewnet": "634acef27f871527", @@ -347,7 +347,7 @@ "ScopedFTProviders", "Serialize", "SerializeMetadata", - "BridgePermissions", + "IBridgePermissions", "ICrossVM", "CrossVMNFT", "CrossVMToken", diff --git a/local/normalize_coverage_report.sh b/local/normalize_coverage_report.sh index a649541f..cd47455e 100644 --- a/local/normalize_coverage_report.sh +++ b/local/normalize_coverage_report.sh @@ -1,7 +1,7 @@ sed -i 's/A.0000000000000007.Serialize/cadence\/contracts\/utils\/Serialize.cdc/' coverage.lcov sed -i 's/A.0000000000000007.SerializeMetadata/cadence\contracts\/SerializeMetadata.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.BridgePermissions/cadence\/contracts\/bridge\/BridgePermissions.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.ICrossVM/cadence\/contracts\/bridge\/ICrossVM.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.IBridgePermissions/cadence\/contracts\/bridge\/interfaces\/IBridgePermissions.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.ICrossVM/cadence\/contracts\/bridge\/interfaces\/ICrossVM.cdc/' coverage.lcov sed -i 's/A.0000000000000007.CrossVMNFT/cadence\/contracts\/bridge\/CrossVMNFT.cdc/' coverage.lcov sed -i 's/A.0000000000000007.CrossVMToken/cadence\/contracts\/bridge\/CrossVMToken.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeConfig/cadence\/contracts\/bridge\/FlowEVMBridgeConfig.cdc/' coverage.lcov @@ -10,11 +10,11 @@ sed -i 's/A.0000000000000007.EVMUtils/cadence\/contracts\/bridge\/EVMUtils.cdc/' sed -i 's/A.0000000000000007.FlowEVMBridgeNFTEscrow/cadence\/contracts\/bridge\/FlowEVMBridgeNFTEscrow.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeTokenEscrow/cadence\/contracts\/bridge\/FlowEVMBridgeTokenEscrow.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeTemplates/cadence\/contracts\/bridge\/FlowEVMBridgeTemplates.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.IEVMBridgeNFTMinter/cadence\/contracts\/bridge\/IEVMBridgeNFTMinter.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.IEVMBridgeTokenMinter/cadence\/contracts\/bridge\/IEVMBridgeTokenMinter.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.IFlowEVMNFTBridge/cadence\/contracts\/bridge\/IFlowEVMNFTBridge.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.IFlowEVMTokenBridge/cadence\/contracts\/bridge\/IFlowEVMTokenBridge.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.IEVMBridgeNFTMinter/cadence\/contracts\/bridge\/interfaces\/IEVMBridgeNFTMinter.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.IEVMBridgeTokenMinter/cadence\/contracts\/bridge\/interfaces\/IEVMBridgeTokenMinter.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.IFlowEVMNFTBridge/cadence\/contracts\/bridge\/interfaces\/IFlowEVMNFTBridge.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.IFlowEVMTokenBridge/cadence\/contracts\/bridge\/interfaces\/IFlowEVMTokenBridge.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridge/cadence\/contracts\/bridge\/FlowEVMBridge.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeAccessor/cadence\/contracts\/bridge\/FlowEVMBridgeAccessor.cdc/' coverage.lcov -sed -i 's/A.0000000000000007.FlowEVMBridgeHandlerInterfaces/cadence\/contracts\/bridge\/FlowEVMBridgeHandlerInterfaces.cdc/' coverage.lcov +sed -i 's/A.0000000000000007.FlowEVMBridgeHandlerInterfaces/cadence\/contracts\/bridge\/interfaces\/FlowEVMBridgeHandlerInterfaces.cdc/' coverage.lcov sed -i 's/A.0000000000000007.FlowEVMBridgeHandlers/cadence\/contracts\/bridge\/FlowEVMBridgeHandlers.cdc/' coverage.lcov \ No newline at end of file diff --git a/local/setup_emulator.1.sh b/local/setup_emulator.1.sh deleted file mode 100644 index a7cb9bdf..00000000 --- a/local/setup_emulator.1.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Create COA in emulator-account -flow-c1 transactions send ./cadence/transactions/evm/create_account.cdc 100.0 - -# Deploy supporting utils -flow-c1 accounts add-contract ./cadence/contracts/utils/ArrayUtils.cdc -flow-c1 accounts add-contract ./cadence/contracts/utils/StringUtils.cdc -flow-c1 accounts add-contract ./cadence/contracts/utils/ScopedFTProviders.cdc -flow-c1 accounts add-contract ./cadence/contracts/utils/Serialize.cdc -flow-c1 accounts add-contract ./cadence/contracts/utils/SerializeMetadata.cdc - -flow-c1 accounts update-contract ./cadence/contracts/standards/EVM.cdc - -# Deploy initial bridge contracts -flow-c1 accounts add-contract ./cadence/contracts/bridge/BridgePermissions.cdc -flow-c1 accounts add-contract ./cadence/contracts/bridge/ICrossVM.cdc -flow-c1 accounts add-contract ./cadence/contracts/bridge/CrossVMNFT.cdc -flow-c1 accounts add-contract ./cadence/contracts/bridge/FlowEVMBridgeConfig.cdc - -# Deploy FlowEVMBridgeUtils also deploying FlowEVMBridgeFactory to EVM in init -flow-c1 accounts add-contract ./cadence/contracts/bridge/FlowEVMBridgeUtils.cdc \ - --args-json "$(cat ./cadence/args/deploy-factory-args.json)" - -flow-c1 accounts add-contract ./cadence/contracts/bridge/FlowEVMBridgeNFTEscrow.cdc -flow-c1 accounts add-contract ./cadence/contracts/bridge/FlowEVMBridgeTemplates.cdc - -# Add the templated contract code chunks for FlowEVMBridgedNFTTemplate.cdc contents -flow-c1 transactions send ./cadence/transactions/bridge/admin/upsert_contract_code_chunks.cdc \ - --args-json "$(cat ./cadence/args/bridged-nft-code-chunks-args.json)" --gas-limit 1600 - -flow-c1 accounts add-contract ./cadence/contracts/bridge/IEVMBridgeNFTMinter.cdc - -# Deploy main bridge interface & contract -flow-c1 accounts add-contract ./cadence/contracts/bridge/IFlowEVMNFTBridge.cdc -flow-c1 accounts add-contract ./cadence/contracts/bridge/FlowEVMBridge.cdc diff --git a/local/setup_emulator.2.sh b/local/setup_emulator.2.sh deleted file mode 100644 index 91a6a674..00000000 --- a/local/setup_emulator.2.sh +++ /dev/null @@ -1,39 +0,0 @@ -# Deploy the bridge router directing calls from COAs to the dedicated bridge -flow-c1 accounts add-contract ./cadence/contracts/bridge/EVMBridgeRouter.cdc 0xf8d6e0586b0a20c7 FlowEVMBridge - -# Create `example-nft` account 179b6b1cb6755e31 with private key 96dfbadf086daa187100a24b1fd2b709b702954bbd030a394148e11bcbb799ef -flow-c1 accounts create --key "351e1310301a7374430f6077d7b1b679c9574f8e045234eac09568ceb15c4f5d937104b4c3180df1e416da20c9d58aac576ffc328a342198a5eae4a29a13c47a" - -# Create `user` account 0xf3fcd2c1a78f5eee with private key bce84aae316aec618888e5bdd24a3c8b8af46896c1ebe457e2f202a4a9c43075 -flow-c1 accounts create --key "c695fa608bd40821552fae13bb710c917309690ed69c22866abad19d276c99296379358321d0123d7074c817dd646ae8f651734526179eaed9f33eba16601ff6" - -# Create `erc721` account 0xe03daebed8ca0615 with private key bf602a4cdffb5610a008622f6601ba7059f8a6f533d7489457deb3d45875acb0 -flow-c1 accounts create --key "9103fd9106a83a2ede667e2486848e13e5854ea512af9bbec9ad2aec155bd5b5c146b53a6c3fd619c591ae0cd730acb875e5b6e074047cf31d620b53c55a4fb4" - -# Give the user some FLOW -flow-c1 transactions send ./cadence/transactions/flow-token/transfer_flow.cdc 0xf3fcd2c1a78f5eee 100.0 - -# Give the erc721 some FLOW -flow-c1 transactions send ./cadence/transactions/flow-token/transfer_flow.cdc 0xe03daebed8ca0615 100.0 - -# Create a COA for the user -flow-c1 transactions send ./cadence/transactions/evm/create_account.cdc 10.0 --signer user - -# Create a COA for the erc721 -flow-c1 transactions send ./cadence/transactions/evm/create_account.cdc 10.0 --signer erc721 - -# user transfers Flow-c1 to the COA -flow-c1 transactions send ./cadence/transactions/evm/deposit.cdc 10.0 --signer user - -# erc721 transfers Flow-c1 to the COA -flow-c1 transactions send ./cadence/transactions/evm/deposit.cdc 10.0 --signer erc721 - -# Setup User with Example NFT collection - Will break flow.json config due to bug in CLI - break here and update flow.json manually -flow-c1 accounts add-contract ./cadence/contracts/example-assets/ExampleNFT.cdc --signer example-nft - -flow-c1 transactions send ./cadence/transactions/example-assets/setup_collection.cdc --signer user -flow-c1 transactions send ./cadence/transactions/example-assets/mint_nft.cdc f3fcd2c1a78f5eee example description thumbnail '[]' '[]' '[]' --signer example-nft - -# Deploy ExampleERC721 contract with erc721's COA as owner - NOTE THE `deployedContractAddress` EMITTED IN THE RESULTING EVENT -flow-c1 transactions send ./cadence/transactions/evm/deploy.cdc \ - --args-json "$(cat ./cadence/args/deploy-erc721-args.json)" --signer erc721 \ No newline at end of file diff --git a/local/setup_emulator.3.sh b/local/setup_emulator.3.sh deleted file mode 100644 index 6485be51..00000000 --- a/local/setup_emulator.3.sh +++ /dev/null @@ -1,4 +0,0 @@ -# Mint an ERC721 with ID 42 to the user's COA -flow-c1 transactions send ./cadence/transactions/example-assets/safe_mint_erc721.cdc \ - 42 "URI" 200000 \ - --signer erc721 diff --git a/solidity/src/FlowBridgeFactory.sol b/solidity/src/FlowBridgeFactory.sol index dee9ee75..93361415 100644 --- a/solidity/src/FlowBridgeFactory.sol +++ b/solidity/src/FlowBridgeFactory.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.17; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import "./FlowBridgedERC721.sol"; -import "./FlowBridgedERC20.sol"; -import "./IBridgePermissions.sol"; +import "./templates/FlowBridgedERC721.sol"; +import "./templates/FlowBridgedERC20.sol"; +import "./interfaces/IBridgePermissions.sol"; contract FlowBridgeFactory is Ownable { mapping(string => address) public flowIdentifierToContract; diff --git a/solidity/src/ExampleERC20.sol b/solidity/src/example-assets/ExampleERC20.sol similarity index 100% rename from solidity/src/ExampleERC20.sol rename to solidity/src/example-assets/ExampleERC20.sol diff --git a/solidity/src/ExampleERC721.sol b/solidity/src/example-assets/ExampleERC721.sol similarity index 100% rename from solidity/src/ExampleERC721.sol rename to solidity/src/example-assets/ExampleERC721.sol diff --git a/solidity/src/BridgePermissions.sol b/solidity/src/interfaces/BridgePermissions.sol similarity index 100% rename from solidity/src/BridgePermissions.sol rename to solidity/src/interfaces/BridgePermissions.sol diff --git a/solidity/src/IBridgePermissions.sol b/solidity/src/interfaces/IBridgePermissions.sol similarity index 100% rename from solidity/src/IBridgePermissions.sol rename to solidity/src/interfaces/IBridgePermissions.sol diff --git a/solidity/src/FlowBridgedERC20.sol b/solidity/src/templates/FlowBridgedERC20.sol similarity index 100% rename from solidity/src/FlowBridgedERC20.sol rename to solidity/src/templates/FlowBridgedERC20.sol diff --git a/solidity/src/FlowBridgedERC721.sol b/solidity/src/templates/FlowBridgedERC721.sol similarity index 100% rename from solidity/src/FlowBridgedERC721.sol rename to solidity/src/templates/FlowBridgedERC721.sol diff --git a/solidity/test/FlowBridgeFactory.t.sol b/solidity/test/FlowBridgeFactory.t.sol index f826cc79..5b87090a 100644 --- a/solidity/test/FlowBridgeFactory.t.sol +++ b/solidity/test/FlowBridgeFactory.t.sol @@ -4,8 +4,8 @@ pragma solidity ^0.8.17; import {Test} from "forge-std/Test.sol"; import {FlowBridgeFactory} from "../src/FlowBridgeFactory.sol"; -import {FlowBridgedERC721} from "../src/FlowBridgedERC721.sol"; -import {FlowBridgedERC20} from "../src/FlowBridgedERC20.sol"; +import {FlowBridgedERC721} from "../src/templates/FlowBridgedERC721.sol"; +import {FlowBridgedERC20} from "../src/templates/FlowBridgedERC20.sol"; contract FlowBridgeFactoryTest is Test { FlowBridgeFactory internal factory;