使用Polygon zkEVM Bridge 站点,将网络添加到我们的钱包,并将 Goerli Token 桥接到 zkEVM Testnet:

Goerli Token转0.01 ETH到zkEVM Testnet

交易:0x10210572d6b4924af7ef946136295e9b209e1fa0 -> 0xf6beeebb578e214ca9e23b0e9683454ff88ed2a7
tx hash: https://goerli.etherscan.io/tx/0x07aa6363c5854180b41b8c4584314ca6d6af989d48332375f06f9caef3270dd7

执行合约

bridgeAsset(uint32 destinationNetwork,address destinationAddress,uint256 amount,address token,bool forceUpdateGlobalExitRoot,bytes permitData)

执行参数

Name Type Data
0 destinationNetwork uint32 1
1 destinationAddress address 0x10210572d6b4924Af7Ef946136295e9b209E1FA0
2 amount uint256 10000000000000000
3 token address 0x0000000000000000000000000000000000000000
4 forceUpdateGlobalExitRoot bool true
5 permitData bytes

0xF6BEEeBB578e214CA9E23B0e9683454Ff88Ed2A7为代理合约,使用EIP-1967 Transparent Proxy
逻辑合约地址:0x39e780D8800f7396e8B7530A8925B14025AedC77
关键逻辑合约代码:https://goerli.etherscan.io/address/0x39e780d8800f7396e8b7530a8925b14025aedc77#code

合约bridgeAsset接口源码

/**
     * @notice Deposit add a new leaf to the merkle tree
     * @param destinationNetwork Network destination
     * @param destinationAddress Address destination
     * @param amount Amount of tokens
     * @param token Token address, 0 address is reserved for ether
     * @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
     * @param permitData Raw data of the call `permit` of the token
     */
    function bridgeAsset(
        uint32 destinationNetwork,
        address destinationAddress,
        uint256 amount,
        address token,
        bool forceUpdateGlobalExitRoot,
        bytes calldata permitData
    ) public payable virtual ifNotEmergencyState nonReentrant {
        if (
            destinationNetwork == networkID ||
            destinationNetwork >= _CURRENT_SUPPORTED_NETWORKS
        ) {
            revert DestinationNetworkInvalid();
        }

        address originTokenAddress;
        uint32 originNetwork;
        bytes memory metadata;
        uint256 leafAmount = amount;

        if (token == address(0)) {
            // Ether transfer
            if (msg.value != amount) {
                revert AmountDoesNotMatchMsgValue();
            }

            // Ether is treated as ether from mainnet
            originNetwork = _MAINNET_NETWORK_ID;
        } else {
            // Check msg.value is 0 if tokens are bridged
            if (msg.value != 0) {
                revert MsgValueNotZero();
            }

            TokenInformation memory tokenInfo = wrappedTokenToTokenInfo[token];

            if (tokenInfo.originTokenAddress != address(0)) {
                // The token is a wrapped token from another network

                // Burn tokens
                TokenWrapped(token).burn(msg.sender, amount);

                originTokenAddress = tokenInfo.originTokenAddress;
                originNetwork = tokenInfo.originNetwork;
            } else {
                // Use permit if any
                if (permitData.length != 0) {
                    _permit(token, amount, permitData);
                }

                // In order to support fee tokens check the amount received, not the transferred
                uint256 balanceBefore = IERC20Upgradeable(token).balanceOf(
                    address(this)
                );
                IERC20Upgradeable(token).safeTransferFrom(
                    msg.sender,
                    address(this),
                    amount
                );
                uint256 balanceAfter = IERC20Upgradeable(token).balanceOf(
                    address(this)
                );

                // Override leafAmount with the received amount
                leafAmount = balanceAfter - balanceBefore;

                originTokenAddress = token;
                originNetwork = networkID;

                // Encode metadata
                metadata = abi.encode(
                    _safeName(token),
                    _safeSymbol(token),
                    _safeDecimals(token)
                );
            }
        }

        emit BridgeEvent(
            _LEAF_TYPE_ASSET,
            originNetwork,
            originTokenAddress,
            destinationNetwork,
            destinationAddress,
            leafAmount,
            metadata,
            uint32(depositCount)
        );

        _deposit(
            getLeafValue(
                _LEAF_TYPE_ASSET,
                originNetwork,
                originTokenAddress,
                destinationNetwork,
                destinationAddress,
                leafAmount,
                keccak256(metadata)
            )
        );
        // Update the new root to the global exit root manager if set by the user
        if (forceUpdateGlobalExitRoot) {
            _updateGlobalExitRoot();
        }
    }

// TODO 代码分析

等待zkevm-test处理完成

zkevm-test处理完成

Bridge Details

zkevm-test 对应交易

交易 0xa49d20f2f5a26d4a8e6fe44409f862d744f5b1aa -> 0xf6beeebb578e214ca9e23b0e9683454ff88ed2a7
tx hash: https://testnet-zkevm.polygonscan.com/tx/0x830ac94e69eb9e3aacd384d4db8a46e7830b8f99b12b5c6b5176bd8326fddc4b

执行方法

claimAsset(bytes32[32] smtProof,uint32 index,bytes32 mainnetExitRoot,bytes32 rollupExitRoot,uint32 originNetwork,address originTokenAddress,uint32 destinationNetwork,address destinationAddress,uint256 amount,bytes metadata)

执行参数

# Name Type Data
0 smtProof bytes32[32] 0x0000000000000000000000000000000000000000000000000000000000000000 0x5c4dafd55279a81649518c48d62f1e9105c9e7f5a8933aa2fce126d207755c14 0x890afe4ec8996c6ddd3977287b067f0f67dd25cd11cda85b22a93deac5675cdd 0xb6b78f0847e6ecdfc18ee568a1349fae5f336bdce9716be04db241b68b8dd60b 0x18dd01806c4a8a02ac0303f33fe609a944d81197cb52e8b8e159fbf9e45f12ed 0x0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d 0x4ac259557eb77da7cf4309feb02d02d0884d94e56bbf71f539bb3871e5c5f0db 0xffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83 0x08f9cb503a809cc64017397ec880786e6713c3ba6a85c181d02ae7536e9b58e3 0xcefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0 0xf9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5 0xa647f1787afe7ec81c77a49649ba32057b0550470d3c9318dda05fcb8168d914 0xe1f88e3362870dcdb9c43a352e2b031a0c71080b78cc2a5732c3a5c1c0c8e3b4 0xc1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb 0x5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc 0xda7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2 0x645b9978ad76c57db977fe48efb0b43110e3ebe78f9badd9047aac6bcd8d66a5 0xe1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a 0x5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0 0xb46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0 0xc65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2 0xf4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9 0x5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377 0x4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652 0xcdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef 0x0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d 0xb8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0 0x838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e 0x662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e 0x388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322 0x93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735 0x8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9
1 index uint32 72030
2 mainnetExitRoot bytes32 0x8ac4bfc9b4381015f4d41ca9d127a0a4065827d9c61ae290ffbf401ae446a97a
3 rollupExitRoot bytes32 0xd226edaccb2a0c0dc760d2a670a84862a271af7916e0a289b286f61435378411
4 originNetwork uint32 0
5 originTokenAddress address 0x0000000000000000000000000000000000000000
6 destinationNetwork uint32 1

合约地址0xf6beeebb578e214ca9e23b0e9683454ff88ed2a7 为 EIP-1967 Transparent Proxy代理合约,代理的逻辑合约地址为0x39e780d8800f7396e8b7530a8925b14025aedc77
合约源码:https://testnet-zkevm.polygonscan.com/address/0x39e780d8800f7396e8b7530a8925b14025aedc77#code

/**
     * @notice Verify merkle proof and withdraw tokens/ether
     * @param smtProof Smt proof
     * @param index Index of the leaf
     * @param mainnetExitRoot Mainnet exit root
     * @param rollupExitRoot Rollup exit root
     * @param originNetwork Origin network
     * @param originTokenAddress  Origin token address, 0 address is reserved for ether
     * @param destinationNetwork Network destination
     * @param destinationAddress Address destination
     * @param amount Amount of tokens
     * @param metadata Abi encoded metadata if any, empty otherwise
     */
    function claimAsset(
        bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof,
        uint32 index,
        bytes32 mainnetExitRoot,
        bytes32 rollupExitRoot,
        uint32 originNetwork,
        address originTokenAddress,
        uint32 destinationNetwork,
        address destinationAddress,
        uint256 amount,
        bytes calldata metadata
    ) external ifNotEmergencyState {
        // Verify leaf exist and it does not have been claimed
        _verifyLeaf(
            smtProof,
            index,
            mainnetExitRoot,
            rollupExitRoot,
            originNetwork,
            originTokenAddress,
            destinationNetwork,
            destinationAddress,
            amount,
            metadata,
            _LEAF_TYPE_ASSET
        );

        // Transfer funds
        if (originTokenAddress == address(0)) {
            // Transfer ether
            /* solhint-disable avoid-low-level-calls */
            (bool success, ) = destinationAddress.call{value: amount}(
                new bytes(0)
            );
            if (!success) {
                revert EtherTransferFailed();
            }
        } else {
            // Transfer tokens
            if (originNetwork == networkID) {
                // The token is an ERC20 from this network
                IERC20Upgradeable(originTokenAddress).safeTransfer(
                    destinationAddress,
                    amount
                );
            } else {
                // The tokens is not from this network
                // Create a wrapper for the token if not exist yet
                bytes32 tokenInfoHash = keccak256(
                    abi.encodePacked(originNetwork, originTokenAddress)
                );
                address wrappedToken = tokenInfoToWrappedToken[tokenInfoHash];

                if (wrappedToken == address(0)) {
                    // Get ERC20 metadata
                    (
                        string memory name,
                        string memory symbol,
                        uint8 decimals
                    ) = abi.decode(metadata, (string, string, uint8));

                    // Create a new wrapped erc20 using create2
                    TokenWrapped newWrappedToken = (new TokenWrapped){
                        salt: tokenInfoHash
                    }(name, symbol, decimals);

                    // Mint tokens for the destination address
                    newWrappedToken.mint(destinationAddress, amount);

                    // Create mappings
                    tokenInfoToWrappedToken[tokenInfoHash] = address(
                        newWrappedToken
                    );

                    wrappedTokenToTokenInfo[
                        address(newWrappedToken)
                    ] = TokenInformation(originNetwork, originTokenAddress);

                    emit NewWrappedToken(
                        originNetwork,
                        originTokenAddress,
                        address(newWrappedToken),
                        metadata
                    );
                } else {
                    // Use the existing wrapped erc20
                    TokenWrapped(wrappedToken).mint(destinationAddress, amount);
                }
            }
        }

        emit ClaimEvent(
            index,
            originNetwork,
            originTokenAddress,
            destinationAddress,
            amount
        );
    }

// TODO 代码分析

确认余额

https://testnet-zkevm.polygonscan.com/address/0x10210572d6b4924af7ef946136295e9b209e1fa0
// Note: Our ETH balance display is temporarily unavailable. Please check back later.
testnet-zkevm.polygonscan.com 浏览器余额出问题了,直接MetaMask查看

以正确到账

//TODO testnet-zkevm反向Goerli的转账跟踪