Search
Search

Transaction: 2zaRs7a...n7Uk

Signed by
Receiver
Status
Succeeded
Transaction Fee
0.00115 
Deposit Value
0 
Gas Used
11 Tgas
Attached Gas
100 Tgas
Created
January 11, 2024 at 5:11:07pm
Hash
2zaRs7agtD6uKHGm5mbPyVApjCGGwZURShS5dbnSn7Uk

Actions

Called method: 'set' in contract: social.near
Arguments:
{ "data": { "mattlock.near": { "widget": { "zk-bridge": { "": "// constants\n\nconst ETHEREUM_CHAIN_ID = 1;\nconst ZKSYNC_CHAIN_ID = 324;\nconst GOERLI_CHAIN_ID = 5;\nconst ZKSYNC_GOERLI_CHAIN_ID = 280;\nconst L1_MESSENGER_ADDRESS = \"0x0000000000000000000000000000000000008008\";\nconst l2TxGasLimit = \"900000\";\nconst l2TxGasLimitWithdraw = \"6000000\";\nconst l2TxGasPerPubdataByte = \"800\";\nconst l2MaxGasPrice = \"2\";\nconst depositDisabledMsg =\n \"For deposits, please switch to Ethereum mainnet or Goerli testnet.\";\nconst withdrawDisabledMsg =\n \"For withdrawals, please switch to zkSync mainnet or zkSync testnet.\";\nconst sortByBlockNumber = (a, b) => b.blockNumber - a.blockNumber;\nconst l2DepositFee = ethers.utils.formatUnits(\n Big(l2MaxGasPrice)\n .mul(ethers.utils.parseUnits(l2TxGasLimit, \"gwei\"))\n .toString(),\n \"wei\"\n);\nconst catchApproveError = (e) => {\n console.error(\"approve error:\", e);\n if (e.message) {\n if (/rejected/gi.test(e.message)) {\n e.message = \"You rejected the transaction.\";\n }\n State.update({ isLoading: false, log: e.message });\n setTimeout(() => State.update({ log: null }), 3000);\n return;\n }\n State.update({ isLoading: false });\n};\nconst approvedTx = (tx, zkSync) => {\n State.update({\n log: \"Approved\",\n explorerLink: `https://${network === \"testnet\" ? \"goerli.\" : \"\"}${\n zkSync ? \"explorer.zksync.io/\" : \"etherscan.io/\"\n }tx/${tx.hash}`,\n isLoading: false,\n });\n};\n\n// state\nconst defaultDeposit = {\n network: {\n id: \"l1\",\n name: \"Ethereum\",\n },\n assets: [\n {\n id: \"eth\",\n name: \"ETH\",\n selected: true,\n balance: \"0.00\",\n },\n {\n id: \"usdc\",\n name: \"USDC\",\n selected: false,\n balance: \"0.00\",\n },\n ],\n};\nconst defaultWithdraw = {\n network: {\n id: \"l2\",\n name: \"zkSync Era\",\n },\n assets: [\n {\n id: \"eth\",\n name: \"ETH\",\n selected: false,\n balance: \"0.00\",\n },\n {\n id: \"usdc\",\n name: \"USDC\",\n selected: true,\n balance: \"0.00\",\n },\n ],\n};\nif (!state.initialized) {\n initState({\n initialized: true,\n deposit: defaultDeposit,\n withdraw: defaultWithdraw,\n amount: \"0.0\",\n deposits: [],\n withdrawals: [],\n ethDeposits: [],\n ethWithdrawals: [],\n });\n return \"\";\n}\n\n// providers\nconst ethereumProvider = new ethers.providers.JsonRpcProvider(\n \"https://rpc.ankr.com/eth\"\n);\nconst zksyncProvider = new ethers.providers.JsonRpcProvider(\n \"https://mainnet.era.zksync.io\"\n);\nconst goerliProvider = new ethers.providers.JsonRpcProvider(\n \"https://rpc.ankr.com/eth_goerli\"\n);\nconst zksyncGoerliProvider = new ethers.providers.JsonRpcProvider(\n \"https://testnet.era.zksync.dev\"\n);\nconst providersByChainId = {\n [ETHEREUM_CHAIN_ID]: ethereumProvider,\n [ZKSYNC_CHAIN_ID]: zksyncProvider,\n [GOERLI_CHAIN_ID]: goerliProvider,\n [ZKSYNC_GOERLI_CHAIN_ID]: zksyncGoerliProvider,\n};\n\n// get account\nconst sender = Ethers.send(\"eth_requestAccounts\", [])[0];\nif (!sender) {\n return (\n <div className=\"w3button\">\n <Web3Connect connectLabel=\"Connect to a wallet\" />\n </div>\n );\n}\n\nif (!state.chainId) {\n Ethers.provider()\n .getNetwork()\n .then(({ chainId }) => {\n let network = \"incorrect\";\n if (chainId === GOERLI_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID) {\n network = \"testnet\";\n }\n if (chainId === ETHEREUM_CHAIN_ID || chainId === ZKSYNC_CHAIN_ID) {\n network = \"mainnet\";\n }\n console.log(\"chainId\", chainId, network);\n let log, depositDisabled;\n if (chainId === ZKSYNC_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID) {\n log = depositDisabledMsg;\n depositDisabled = true;\n }\n State.update({ chainId, network, log });\n });\n return \"\";\n}\nconst { chainId, network } = state;\n\nif (!network) {\n return \"\";\n}\n\nif (network === \"incorrect\") {\n return (\n <p>Please switch to Ethereum or zkSync mainnet (or Goerli testnets)</p>\n );\n}\n\n// https://era.zksync.io/docs/dev/building-on-zksync/useful-address.html\nconst contracts = {\n mainnet: {\n l1Provider: ethereumProvider,\n l2Provider: zksyncProvider,\n bridge: {\n L1ETHBridgeProxy: \"0x32400084C286CF3E17e7B677ea9583e60a000324\",\n L1ERC20BridgeProxy: \"0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063\",\n L2ERC20Bridge: \"0x11f943b2c77b743AB90f4A0Ae7d5A4e7FCA3E102\",\n },\n eth: {\n decimals: 18,\n deposit: \"0x32400084C286CF3E17e7B677ea9583e60a000324\",\n withdraw: \"0x000000000000000000000000000000000000800A\", // l2 token\n },\n weth: {\n deposit: \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", // l1 token\n withdraw: \"0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91\", // l2 token\n decimals: 18,\n },\n usdc: {\n deposit: \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", // l1 token\n withdraw: \"0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4\", // l2 token\n decimals: 6,\n },\n },\n testnet: {\n l1Provider: goerliProvider,\n l2Provider: zksyncGoerliProvider,\n bridge: {\n L1ERC20BridgeProxy: \"0x927DdFcc55164a59E0F33918D13a2D559bC10ce7\",\n L2ERC20Bridge: \"0x00ff932A6d70E2B8f1Eb4919e1e09C1923E7e57b\",\n },\n eth: {\n deposit: \"0x1908e2BF4a88F91E4eF0DC72f02b8Ea36BEa2319\",\n withdraw: \"0x000000000000000000000000000000000000800A\",\n decimals: 18,\n },\n weth: {\n // deposit: \"0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6\",\n deposit: \"0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4\",\n withdraw: undefined, // not found yet\n decimals: 18,\n },\n usdc: {\n // deposit: \"0x07865c6e87b9f70255377e024ace6630c1eaa37f\",\n deposit: \"0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4\",\n withdraw: \"0x0faF6df7054946141266420b43783387A78d82A9\",\n decimals: 6,\n },\n },\n};\n\nconst tokens = {\n eth: {\n decimals: 18,\n },\n usdc: {\n decimals: 6,\n },\n};\n\n// fetch ABIs\n\nconst zkL2Abi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/628e679517ba187b2ddca79de3b33673/raw/8a12d5abfb375f2b6a3003e649c0bd6dfaf68e52/zksyncL2Abi.json\"\n);\n\nconst zkAbi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/35ebdd13e5dfcf612c11e7087e9d1e59/raw/3967fe7d0cf16f813c069cc6021f9663bb1650a6/zksyncL1Abi.json\"\n);\n\nconst zkEthAbi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/80b8323c91669cb5c662fc649a8d74dc/raw/70168542489641d19d0157a87e6b01528bac1063/zkEthAbi.json\"\n);\n\nconst zkEthTokenAbi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/52a6b1213903abcb8268a2415ab33b52/raw/b63c89cad21112fbd3f460ed2d42338e650360b7/l2EthToken.json\"\n);\n\nconst erc20Abi = fetch(\n \"https://gist.githubusercontent.com/veox/8800debbf56e24718f9f483e1e40c35c/raw/f853187315486225002ba56e5283c1dba0556e6f/erc20.abi.json\"\n);\n\n// wait for all abi to load\nif (\n !zkAbi.ok ||\n !erc20Abi.ok ||\n !zkL2Abi.ok ||\n !zkEthAbi.ok ||\n !zkEthTokenAbi.ok\n) {\n return \"\";\n}\n\nconst erc20IFace = new ethers.utils.Interface(erc20Abi.body);\nconst zkL1EthIFace = new ethers.utils.Interface(zkEthAbi.body);\nconst zkL2EthTokenIFace = new ethers.utils.Interface(zkEthTokenAbi.body);\nconst zkL1IFace = new ethers.utils.Interface(zkAbi.body);\nconst zkL2IFace = new ethers.utils.Interface(zkL2Abi.body);\n\n// create contract instances for reads\n\nconst L1Bridge = new ethers.Contract(\n contracts[network].bridge.L1ERC20BridgeProxy,\n zkAbi.body,\n contracts[network].l1Provider\n);\n\nconst L1EthBridge = new ethers.Contract(\n contracts[network].eth.deposit,\n zkEthAbi.body,\n contracts[network].l1Provider\n);\n\nconst L2BridgeEth = new ethers.Contract(\n contracts[network].eth.withdraw,\n zkEthTokenAbi.body,\n contracts[network].l2Provider\n);\n\nconst L2Bridge = new ethers.Contract(\n contracts[network].bridge.L2ERC20Bridge,\n zkL2Abi.body,\n contracts[network].l2Provider\n);\n\n// proof testing\n\nfunction getWithdrawalLog(txHash, cb, index) {\n if (!index) index = 0;\n contracts[network].l2Provider\n .send(\"eth_getTransactionReceipt\", [txHash])\n .then((receipt) => {\n if (!receipt) return cb(null);\n\n const log = receipt.logs.filter(\n (log) =>\n log.address == L1_MESSENGER_ADDRESS &&\n log.topics[0] ==\n ethers.utils.id(\"L1MessageSent(address,bytes32,bytes)\")\n )[index];\n\n cb({\n log,\n l1BatchTxId: receipt.l1BatchTxIndex,\n });\n });\n}\n\nfunction getWithdrawalL2ToL1Log(txHash, cb, index) {\n if (!index) index = 0;\n contracts[network].l2Provider\n .send(\"eth_getTransactionReceipt\", [txHash])\n .then((receipt) => {\n if (!receipt) return cb(null);\n const messages = Array.from(receipt.l2ToL1Logs.entries()).filter(\n ([_, log]) => log.sender == L1_MESSENGER_ADDRESS\n );\n const [l2ToL1LogIndex, l2ToL1Log] = messages[index];\n\n cb({\n l2ToL1LogIndex,\n l2ToL1Log,\n });\n });\n}\n\nfunction getWithdrawArgs(txHash, cb, rawProof, index) {\n if (!index) index = 0;\n\n getWithdrawalLog(\n txHash,\n ({ log, log: { l1BatchNumber, data }, l1BatchTxId }) => {\n // console.log(\"getWithdrawalLog\", log);\n getWithdrawalL2ToL1Log(txHash, ({ l2ToL1LogIndex }) => {\n // console.log(\"getWithdrawalL2ToL1Log\", l2ToL1LogIndex);\n contracts[network].l2Provider\n .send(\"zks_getL2ToL1LogProof\", [txHash, l2ToL1LogIndex])\n .then((proof) => {\n if (!proof) {\n return console.log(\"log proof not found\");\n }\n const abiCoder = new ethers.utils.AbiCoder();\n const message = abiCoder.decode([\"bytes\"], data)[0];\n\n cb({\n l1BatchNumber,\n l2MessageIndex: proof.id,\n l2TxNumberInBlock: l1BatchTxId,\n message,\n sender,\n proof: rawProof ? proof : proof.proof,\n });\n });\n });\n }\n );\n}\n\nfunction isWithdrawalFinalized(txHash, isEth, cb, returnArgs) {\n if (!isEth) isEth = false;\n getWithdrawArgs(\n txHash,\n (res) => {\n if (!res) {\n if (returnArgs) {\n return cb({\n finalized: false,\n });\n }\n cb(false);\n }\n const args = [ethers.BigNumber.from(res.l1BatchNumber), res.proof.id];\n (isEth\n ? L1EthBridge.isEthWithdrawalFinalized(...args)\n : L1Bridge.isWithdrawalFinalized(...args)\n ).then((finalized) => {\n if (returnArgs) {\n return cb({\n finalized,\n withdrawalArgs: {\n ...res,\n proof: res.proof.proof,\n },\n });\n }\n cb(finalized);\n });\n },\n true\n );\n}\n\n// TX logs - getting deposits and withdrawals\n\nif (!state.initLogs) {\n State.update({ initLogs: true });\n\n // eth deposits\n\n L2BridgeEth.queryFilter(L2BridgeEth.filters.Transfer(sender, sender)).then(\n (ethDeposits) => {\n //TODO disambig eth deposits\n ethDeposits.forEach((d) => (d.isEth = true));\n\n State.update({\n ethDeposits,\n });\n }\n );\n\n L2BridgeEth.queryFilter(L2BridgeEth.filters.Withdrawal(null, sender)).then(\n (withdrawals) => {\n const ethWithdrawals = [],\n { length } = withdrawals;\n let ret = 0;\n const check = (i) => {\n const w = { ...withdrawals[i] };\n isWithdrawalFinalized(\n w.transactionHash,\n true,\n (res) => {\n Object.assign(w, res, { isEth: true });\n ethWithdrawals.push(w);\n ret++;\n if (ret === length) {\n State.update({\n ethWithdrawals,\n });\n }\n },\n true\n );\n };\n for (let i = 0; i < length; i++) check(i);\n }\n );\n\n // erc20\n\n L2Bridge.queryFilter(L2Bridge.filters.FinalizeDeposit(sender)).then(\n (deposits) => {\n State.update({\n deposits,\n });\n }\n );\n\n // TODO get finalized status for erc20 withdrawals\n\n L2Bridge.queryFilter(L2Bridge.filters.WithdrawalInitiated(sender)).then(\n (_withdrawals) => {\n const withdrawals = [],\n { length } = _withdrawals;\n let ret = 0;\n const check = (i) => {\n const w = { ..._withdrawals[i] };\n isWithdrawalFinalized(\n w.transactionHash,\n false,\n (res) => {\n Object.assign(w, res, { isEth: false });\n withdrawals.push(w);\n ret++;\n if (ret === length) {\n State.update({\n withdrawals,\n });\n }\n },\n true\n );\n };\n for (let i = 0; i < length; i++) check(i);\n }\n );\n\n return \"\";\n}\n\n// deposits\n\nconst handleDepositEth = (data) => {\n const amount = ethers.utils.parseUnits(data.amount);\n const value = amount.add(ethers.utils.parseUnits(l2DepositFee, \"wei\"));\n\n const encodedData = zkL1EthIFace.encodeFunctionData(\n \"requestL2Transaction(address,uint256,bytes,uint256,uint256,bytes[],address)\",\n [sender, amount, \"0x\", l2TxGasLimit, l2TxGasPerPubdataByte, [], sender]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network].eth.deposit,\n data: encodedData,\n value,\n gasLimit: ethers.BigNumber.from(\"500000\"),\n })\n .then(approvedTx)\n .catch(catchApproveError);\n};\n\n// TODO deposit on txBridge is missing the final address arg\n// NOT \"deposit(address,address,uint256,uint256,uint256,address)\"\n// BUT \"deposit(address,address,uint256,uint256,uint256)\"\n\nconst handleDeposit = (data) => {\n console.log(\"handleDeposit\", data);\n State.update({ isLoading: true, log: undefined, explorerLink: undefined });\n\n if (data.assetId === \"eth\") {\n return handleDepositEth(data);\n }\n\n handleApprove(data, (approveSuccess) => {\n if (!approveSuccess) {\n return console.log(\"approval was not successful\");\n }\n\n const l1Token = contracts[network][data.assetId].deposit;\n const amountBig = ethers.utils.parseUnits(\n data.amount,\n tokens[data.assetId].decimals\n );\n\n const value = ethers.utils.parseUnits(l2DepositFee, \"wei\");\n\n const encodedData = zkL1IFace.encodeFunctionData(\n \"deposit(address,address,uint256,uint256,uint256,address)\",\n [sender, l1Token, amountBig, l2TxGasLimit, l2TxGasPerPubdataByte, sender]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network].bridge.L1ERC20BridgeProxy,\n data: encodedData,\n value,\n gasLimit: ethers.BigNumber.from(\"500000\"),\n })\n .then(approvedTx)\n .catch(catchApproveError);\n });\n};\n\nconst handleApprove = (data, callback) => {\n console.log(\"handleApprove\", data);\n const contract = new ethers.Contract(\n contracts[network][data.assetId].deposit,\n erc20Abi.body,\n Ethers.provider().getSigner()\n );\n const { decimals } = tokens[data.assetId];\n const amountBig = ethers.utils.parseUnits(data.amount, decimals);\n\n contract\n .allowance(sender, contracts[network].bridge.L1ERC20BridgeProxy)\n .then((rawAllowance) => {\n console.log(\"rawAllowance\", rawAllowance);\n const hasAllowance = amountBig.lte(rawAllowance);\n if (hasAllowance) {\n return callback(true);\n }\n console.log(\"amountBig\", amountBig);\n contract\n .approve(contracts[network].bridge.L1ERC20BridgeProxy, amountBig)\n .then((tx) => {\n approvedTx(tx);\n callback(true);\n })\n .catch((e) => {\n catchApproveError(e);\n callback(false);\n });\n });\n};\n\n// withdrawals\n\nconst handleFinalizeEthWithdrawal = (i) => {\n const { l1BatchNumber, l2MessageIndex, l2TxNumberInBlock, message, proof } =\n allWithdrawals[i].withdrawalArgs;\n\n const contract = new ethers.Contract(\n contracts[network].eth.deposit,\n zkEthAbi.body,\n Ethers.provider().getSigner()\n );\n\n console.log(l1BatchNumber, l2MessageIndex, l2TxNumberInBlock, message, proof);\n\n contract\n .finalizeEthWithdrawal(\n l1BatchNumber,\n l2MessageIndex,\n l2TxNumberInBlock,\n message,\n proof,\n {}\n )\n .then((res) => console.log(res));\n};\n\nconst handleWithdrawEth = (data) => {\n const value = ethers.utils.parseUnits(data.amount);\n\n const encodedData = zkL2EthTokenIFace.encodeFunctionData(\n \"withdraw(address)\",\n [sender]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network][data.assetId].withdraw,\n data: encodedData,\n value,\n gasLimit: ethers.BigNumber.from(l2TxGasLimitWithdraw),\n })\n .then((tx) => {\n approvedTx(tx, true);\n })\n .catch(catchApproveError);\n};\n\nconst handleWithdraw = (data) => {\n console.log(\"handleWithdraw\", data);\n State.update({ isLoading: true, log: undefined, explorerLink: undefined });\n\n if (data.assetId === \"eth\") {\n return handleWithdrawEth(data);\n }\n\n const l2Token = contracts[network][data.assetId].withdraw;\n const amountBig = ethers.utils.parseUnits(\n data.amount,\n tokens[data.assetId].decimals\n );\n console.log(\"encodedData\", sender, l2Token, amountBig);\n const encodedData = zkL2IFace.encodeFunctionData(\n \"withdraw(address,address,uint256)\",\n [sender, l2Token, amountBig]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network].bridge.L2ERC20Bridge,\n data: encodedData,\n gasLimit: ethers.BigNumber.from(l2TxGasLimitWithdraw),\n })\n .then((tx) => {\n approvedTx(tx, true);\n })\n .catch(catchApproveError);\n};\n\n// balances\n\nconst getTokenBalance = (sender, isL1, tokenAddress, decimals, callback) => {\n if (!sender) return;\n const encodedData = erc20IFace.encodeFunctionData(\"balanceOf\", [sender]);\n const provider = isL1\n ? contracts[network].l1Provider\n : contracts[network].l2Provider;\n\n provider\n .call({\n to: tokenAddress,\n data: encodedData,\n })\n .then((rawBalance) => {\n if (rawBalance === \"0x\") {\n return callback(0);\n }\n const receiverBalanceHex = erc20IFace.decodeFunctionResult(\n \"balanceOf\",\n rawBalance\n );\n const balance = Big(receiverBalanceHex.toString())\n .div(Big(10).pow(decimals))\n .toFixed(2)\n .replace(/\\d(?=(\\d{3})+\\.)/g, \"$&,\");\n\n callback(balance);\n });\n};\n\nconst tab = !state.tab || state.tab === \"deposit\" ? \"deposit\" : \"withdraw\";\nconst clone = (o) => JSON.parse(JSON.stringify(o));\nconst { deposit, withdraw } = state;\n\nif (sender && !state.balancesUpdated) {\n // l1\n contracts[network].l1Provider.getBalance(sender).then((balance) => {\n const cloned = clone(deposit || defaultDeposit);\n const formatted = ethers.utils.formatUnits(balance);\n cloned.assets[0].balance = formatted.substring(\n 0,\n formatted.indexOf(\".\") + 5\n );\n State.update({ deposit: cloned });\n\n // USDC\n getTokenBalance(\n sender,\n true,\n contracts[network].usdc.deposit,\n tokens.usdc.decimals,\n (balance) => {\n cloned.assets[1].balance = balance;\n State.update({ deposit: cloned });\n }\n );\n });\n\n //l2;\n contracts[network].l2Provider\n .send(\"eth_getBalance\", [sender])\n .then((balance) => {\n const cloned = clone(withdraw || defaultWithdraw);\n const formatted = ethers.utils.formatUnits(balance);\n cloned.assets[0].balance = formatted.substring(\n 0,\n formatted.indexOf(\".\") + 5\n );\n State.update({ withdraw: cloned });\n\n // USDC\n getTokenBalance(\n sender,\n false,\n contracts[network].usdc.withdraw,\n tokens.usdc.decimals,\n (balance) => {\n cloned.assets[1].balance = balance;\n State.update({ withdraw: cloned });\n }\n );\n });\n\n State.update({ balancesUpdated: true });\n return \"\";\n}\n\n// bridge-ui functions\n\nconst onAction = (data) => {\n if (!data.amount) return;\n if (data.action === \"deposit\") handleDeposit(data);\n if (data.action === \"withdraw\") handleWithdraw(data);\n};\n\nconst onTabChange = (tab) => {\n let log = null;\n\n const depositDisabled =\n tab === \"deposit\" &&\n (chainId === ZKSYNC_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID);\n const withdrawDisabled =\n tab === \"withdraw\" &&\n (chainId === ETHEREUM_CHAIN_ID || chainId === GOERLI_CHAIN_ID);\n\n if (depositDisabled) {\n log = depositDisabledMsg;\n }\n if (withdrawDisabled) {\n log = withdrawDisabledMsg;\n }\n\n State.update({\n deposit: clone(withdraw),\n withdraw: clone(deposit),\n depositDisabled,\n withdrawDisabled,\n tab,\n log,\n });\n};\n\nconst { deposits, withdrawals, ethDeposits, ethWithdrawals } = state;\nconst allDeposits = [...deposits, ...ethDeposits].sort(sortByBlockNumber);\nconst allWithdrawals = [...withdrawals, ...ethWithdrawals].sort(\n sortByBlockNumber\n);\n\nconst renderTxLink = (tx) => {\n const isZk =\n chainId === ZKSYNC_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID;\n return (\n <a\n href={`https://${\n network === \"testnet\" ? \"goerli.\" : \"\"\n }explorer.zksync.io/tx/${tx}`}\n target=\"_blank\"\n >\n {tx.substring(0, 6)} ... {tx.substring(tx.length - 4)}\n </a>\n );\n};\n\nconst renderTx = (tx, i) => {\n const { transactionHash: h, finalized, isEth } = tx;\n return (\n <>\n <p style={{ textAlign: \"left\" }}>\n {isEth ? \"ETH \" : \"USDC\"}\n {renderTxLink(h)}\n {typeof finalized === \"boolean\" && (\n <>\n <span>{finalized ? \"(finalized)\" : \"(not finalized)\"}</span>\n {!finalized && false && (\n <p style={{ marginTop: 16 }}>\n <button onClick={() => handleFinalizeEthWithdrawal(i)}>\n Finalize\n </button>\n </p>\n )}\n </>\n )}\n </p>\n </>\n );\n};\n\nreturn (\n <>\n <Widget\n src=\"mattlock.near/widget/bridge-ui\"\n props={{\n ...state,\n onTabChange,\n onAction,\n title: \"zkBridge\",\n }}\n />\n <div style={{ textAlign: \"center\" }}>\n <div style={{ width: 300, margin: \"auto\" }}>\n <h4 style={{ marginTop: 16 }}>Withdrawals</h4>\n {allWithdrawals.map(renderTx)}\n <h4 style={{ marginTop: 16 }}>Deposits</h4>\n {allDeposits.map(renderTx)}\n </div>\n </div>\n </>\n);\n" } } } } }

Transaction Execution Plan

Convert Transaction To Receipt
Gas Burned:
2 Tgas
Tokens Burned:
0.00025 
Receipt:
Predecessor ID:
Receiver ID:
Gas Burned:
9 Tgas
Tokens Burned:
0.0009 
Called method: 'set' in contract: social.near
Arguments:
{ "data": { "mattlock.near": { "widget": { "zk-bridge": { "": "// constants\n\nconst ETHEREUM_CHAIN_ID = 1;\nconst ZKSYNC_CHAIN_ID = 324;\nconst GOERLI_CHAIN_ID = 5;\nconst ZKSYNC_GOERLI_CHAIN_ID = 280;\nconst L1_MESSENGER_ADDRESS = \"0x0000000000000000000000000000000000008008\";\nconst l2TxGasLimit = \"900000\";\nconst l2TxGasLimitWithdraw = \"6000000\";\nconst l2TxGasPerPubdataByte = \"800\";\nconst l2MaxGasPrice = \"2\";\nconst depositDisabledMsg =\n \"For deposits, please switch to Ethereum mainnet or Goerli testnet.\";\nconst withdrawDisabledMsg =\n \"For withdrawals, please switch to zkSync mainnet or zkSync testnet.\";\nconst sortByBlockNumber = (a, b) => b.blockNumber - a.blockNumber;\nconst l2DepositFee = ethers.utils.formatUnits(\n Big(l2MaxGasPrice)\n .mul(ethers.utils.parseUnits(l2TxGasLimit, \"gwei\"))\n .toString(),\n \"wei\"\n);\nconst catchApproveError = (e) => {\n console.error(\"approve error:\", e);\n if (e.message) {\n if (/rejected/gi.test(e.message)) {\n e.message = \"You rejected the transaction.\";\n }\n State.update({ isLoading: false, log: e.message });\n setTimeout(() => State.update({ log: null }), 3000);\n return;\n }\n State.update({ isLoading: false });\n};\nconst approvedTx = (tx, zkSync) => {\n State.update({\n log: \"Approved\",\n explorerLink: `https://${network === \"testnet\" ? \"goerli.\" : \"\"}${\n zkSync ? \"explorer.zksync.io/\" : \"etherscan.io/\"\n }tx/${tx.hash}`,\n isLoading: false,\n });\n};\n\n// state\nconst defaultDeposit = {\n network: {\n id: \"l1\",\n name: \"Ethereum\",\n },\n assets: [\n {\n id: \"eth\",\n name: \"ETH\",\n selected: true,\n balance: \"0.00\",\n },\n {\n id: \"usdc\",\n name: \"USDC\",\n selected: false,\n balance: \"0.00\",\n },\n ],\n};\nconst defaultWithdraw = {\n network: {\n id: \"l2\",\n name: \"zkSync Era\",\n },\n assets: [\n {\n id: \"eth\",\n name: \"ETH\",\n selected: false,\n balance: \"0.00\",\n },\n {\n id: \"usdc\",\n name: \"USDC\",\n selected: true,\n balance: \"0.00\",\n },\n ],\n};\nif (!state.initialized) {\n initState({\n initialized: true,\n deposit: defaultDeposit,\n withdraw: defaultWithdraw,\n amount: \"0.0\",\n deposits: [],\n withdrawals: [],\n ethDeposits: [],\n ethWithdrawals: [],\n });\n return \"\";\n}\n\n// providers\nconst ethereumProvider = new ethers.providers.JsonRpcProvider(\n \"https://rpc.ankr.com/eth\"\n);\nconst zksyncProvider = new ethers.providers.JsonRpcProvider(\n \"https://mainnet.era.zksync.io\"\n);\nconst goerliProvider = new ethers.providers.JsonRpcProvider(\n \"https://rpc.ankr.com/eth_goerli\"\n);\nconst zksyncGoerliProvider = new ethers.providers.JsonRpcProvider(\n \"https://testnet.era.zksync.dev\"\n);\nconst providersByChainId = {\n [ETHEREUM_CHAIN_ID]: ethereumProvider,\n [ZKSYNC_CHAIN_ID]: zksyncProvider,\n [GOERLI_CHAIN_ID]: goerliProvider,\n [ZKSYNC_GOERLI_CHAIN_ID]: zksyncGoerliProvider,\n};\n\n// get account\nconst sender = Ethers.send(\"eth_requestAccounts\", [])[0];\nif (!sender) {\n return (\n <div className=\"w3button\">\n <Web3Connect connectLabel=\"Connect to a wallet\" />\n </div>\n );\n}\n\nif (!state.chainId) {\n Ethers.provider()\n .getNetwork()\n .then(({ chainId }) => {\n let network = \"incorrect\";\n if (chainId === GOERLI_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID) {\n network = \"testnet\";\n }\n if (chainId === ETHEREUM_CHAIN_ID || chainId === ZKSYNC_CHAIN_ID) {\n network = \"mainnet\";\n }\n console.log(\"chainId\", chainId, network);\n let log, depositDisabled;\n if (chainId === ZKSYNC_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID) {\n log = depositDisabledMsg;\n depositDisabled = true;\n }\n State.update({ chainId, network, log });\n });\n return \"\";\n}\nconst { chainId, network } = state;\n\nif (!network) {\n return \"\";\n}\n\nif (network === \"incorrect\") {\n return (\n <p>Please switch to Ethereum or zkSync mainnet (or Goerli testnets)</p>\n );\n}\n\n// https://era.zksync.io/docs/dev/building-on-zksync/useful-address.html\nconst contracts = {\n mainnet: {\n l1Provider: ethereumProvider,\n l2Provider: zksyncProvider,\n bridge: {\n L1ETHBridgeProxy: \"0x32400084C286CF3E17e7B677ea9583e60a000324\",\n L1ERC20BridgeProxy: \"0x57891966931Eb4Bb6FB81430E6cE0A03AAbDe063\",\n L2ERC20Bridge: \"0x11f943b2c77b743AB90f4A0Ae7d5A4e7FCA3E102\",\n },\n eth: {\n decimals: 18,\n deposit: \"0x32400084C286CF3E17e7B677ea9583e60a000324\",\n withdraw: \"0x000000000000000000000000000000000000800A\", // l2 token\n },\n weth: {\n deposit: \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", // l1 token\n withdraw: \"0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91\", // l2 token\n decimals: 18,\n },\n usdc: {\n deposit: \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", // l1 token\n withdraw: \"0x3355df6D4c9C3035724Fd0e3914dE96A5a83aaf4\", // l2 token\n decimals: 6,\n },\n },\n testnet: {\n l1Provider: goerliProvider,\n l2Provider: zksyncGoerliProvider,\n bridge: {\n L1ERC20BridgeProxy: \"0x927DdFcc55164a59E0F33918D13a2D559bC10ce7\",\n L2ERC20Bridge: \"0x00ff932A6d70E2B8f1Eb4919e1e09C1923E7e57b\",\n },\n eth: {\n deposit: \"0x1908e2BF4a88F91E4eF0DC72f02b8Ea36BEa2319\",\n withdraw: \"0x000000000000000000000000000000000000800A\",\n decimals: 18,\n },\n weth: {\n // deposit: \"0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6\",\n deposit: \"0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4\",\n withdraw: undefined, // not found yet\n decimals: 18,\n },\n usdc: {\n // deposit: \"0x07865c6e87b9f70255377e024ace6630c1eaa37f\",\n deposit: \"0xd35CCeEAD182dcee0F148EbaC9447DA2c4D449c4\",\n withdraw: \"0x0faF6df7054946141266420b43783387A78d82A9\",\n decimals: 6,\n },\n },\n};\n\nconst tokens = {\n eth: {\n decimals: 18,\n },\n usdc: {\n decimals: 6,\n },\n};\n\n// fetch ABIs\n\nconst zkL2Abi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/628e679517ba187b2ddca79de3b33673/raw/8a12d5abfb375f2b6a3003e649c0bd6dfaf68e52/zksyncL2Abi.json\"\n);\n\nconst zkAbi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/35ebdd13e5dfcf612c11e7087e9d1e59/raw/3967fe7d0cf16f813c069cc6021f9663bb1650a6/zksyncL1Abi.json\"\n);\n\nconst zkEthAbi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/80b8323c91669cb5c662fc649a8d74dc/raw/70168542489641d19d0157a87e6b01528bac1063/zkEthAbi.json\"\n);\n\nconst zkEthTokenAbi = fetch(\n \"https://gist.githubusercontent.com/mattlockyer/52a6b1213903abcb8268a2415ab33b52/raw/b63c89cad21112fbd3f460ed2d42338e650360b7/l2EthToken.json\"\n);\n\nconst erc20Abi = fetch(\n \"https://gist.githubusercontent.com/veox/8800debbf56e24718f9f483e1e40c35c/raw/f853187315486225002ba56e5283c1dba0556e6f/erc20.abi.json\"\n);\n\n// wait for all abi to load\nif (\n !zkAbi.ok ||\n !erc20Abi.ok ||\n !zkL2Abi.ok ||\n !zkEthAbi.ok ||\n !zkEthTokenAbi.ok\n) {\n return \"\";\n}\n\nconst erc20IFace = new ethers.utils.Interface(erc20Abi.body);\nconst zkL1EthIFace = new ethers.utils.Interface(zkEthAbi.body);\nconst zkL2EthTokenIFace = new ethers.utils.Interface(zkEthTokenAbi.body);\nconst zkL1IFace = new ethers.utils.Interface(zkAbi.body);\nconst zkL2IFace = new ethers.utils.Interface(zkL2Abi.body);\n\n// create contract instances for reads\n\nconst L1Bridge = new ethers.Contract(\n contracts[network].bridge.L1ERC20BridgeProxy,\n zkAbi.body,\n contracts[network].l1Provider\n);\n\nconst L1EthBridge = new ethers.Contract(\n contracts[network].eth.deposit,\n zkEthAbi.body,\n contracts[network].l1Provider\n);\n\nconst L2BridgeEth = new ethers.Contract(\n contracts[network].eth.withdraw,\n zkEthTokenAbi.body,\n contracts[network].l2Provider\n);\n\nconst L2Bridge = new ethers.Contract(\n contracts[network].bridge.L2ERC20Bridge,\n zkL2Abi.body,\n contracts[network].l2Provider\n);\n\n// proof testing\n\nfunction getWithdrawalLog(txHash, cb, index) {\n if (!index) index = 0;\n contracts[network].l2Provider\n .send(\"eth_getTransactionReceipt\", [txHash])\n .then((receipt) => {\n if (!receipt) return cb(null);\n\n const log = receipt.logs.filter(\n (log) =>\n log.address == L1_MESSENGER_ADDRESS &&\n log.topics[0] ==\n ethers.utils.id(\"L1MessageSent(address,bytes32,bytes)\")\n )[index];\n\n cb({\n log,\n l1BatchTxId: receipt.l1BatchTxIndex,\n });\n });\n}\n\nfunction getWithdrawalL2ToL1Log(txHash, cb, index) {\n if (!index) index = 0;\n contracts[network].l2Provider\n .send(\"eth_getTransactionReceipt\", [txHash])\n .then((receipt) => {\n if (!receipt) return cb(null);\n const messages = Array.from(receipt.l2ToL1Logs.entries()).filter(\n ([_, log]) => log.sender == L1_MESSENGER_ADDRESS\n );\n const [l2ToL1LogIndex, l2ToL1Log] = messages[index];\n\n cb({\n l2ToL1LogIndex,\n l2ToL1Log,\n });\n });\n}\n\nfunction getWithdrawArgs(txHash, cb, rawProof, index) {\n if (!index) index = 0;\n\n getWithdrawalLog(\n txHash,\n ({ log, log: { l1BatchNumber, data }, l1BatchTxId }) => {\n // console.log(\"getWithdrawalLog\", log);\n getWithdrawalL2ToL1Log(txHash, ({ l2ToL1LogIndex }) => {\n // console.log(\"getWithdrawalL2ToL1Log\", l2ToL1LogIndex);\n contracts[network].l2Provider\n .send(\"zks_getL2ToL1LogProof\", [txHash, l2ToL1LogIndex])\n .then((proof) => {\n if (!proof) {\n return console.log(\"log proof not found\");\n }\n const abiCoder = new ethers.utils.AbiCoder();\n const message = abiCoder.decode([\"bytes\"], data)[0];\n\n cb({\n l1BatchNumber,\n l2MessageIndex: proof.id,\n l2TxNumberInBlock: l1BatchTxId,\n message,\n sender,\n proof: rawProof ? proof : proof.proof,\n });\n });\n });\n }\n );\n}\n\nfunction isWithdrawalFinalized(txHash, isEth, cb, returnArgs) {\n if (!isEth) isEth = false;\n getWithdrawArgs(\n txHash,\n (res) => {\n if (!res) {\n if (returnArgs) {\n return cb({\n finalized: false,\n });\n }\n cb(false);\n }\n const args = [ethers.BigNumber.from(res.l1BatchNumber), res.proof.id];\n (isEth\n ? L1EthBridge.isEthWithdrawalFinalized(...args)\n : L1Bridge.isWithdrawalFinalized(...args)\n ).then((finalized) => {\n if (returnArgs) {\n return cb({\n finalized,\n withdrawalArgs: {\n ...res,\n proof: res.proof.proof,\n },\n });\n }\n cb(finalized);\n });\n },\n true\n );\n}\n\n// TX logs - getting deposits and withdrawals\n\nif (!state.initLogs) {\n State.update({ initLogs: true });\n\n // eth deposits\n\n L2BridgeEth.queryFilter(L2BridgeEth.filters.Transfer(sender, sender)).then(\n (ethDeposits) => {\n //TODO disambig eth deposits\n ethDeposits.forEach((d) => (d.isEth = true));\n\n State.update({\n ethDeposits,\n });\n }\n );\n\n L2BridgeEth.queryFilter(L2BridgeEth.filters.Withdrawal(null, sender)).then(\n (withdrawals) => {\n const ethWithdrawals = [],\n { length } = withdrawals;\n let ret = 0;\n const check = (i) => {\n const w = { ...withdrawals[i] };\n isWithdrawalFinalized(\n w.transactionHash,\n true,\n (res) => {\n Object.assign(w, res, { isEth: true });\n ethWithdrawals.push(w);\n ret++;\n if (ret === length) {\n State.update({\n ethWithdrawals,\n });\n }\n },\n true\n );\n };\n for (let i = 0; i < length; i++) check(i);\n }\n );\n\n // erc20\n\n L2Bridge.queryFilter(L2Bridge.filters.FinalizeDeposit(sender)).then(\n (deposits) => {\n State.update({\n deposits,\n });\n }\n );\n\n // TODO get finalized status for erc20 withdrawals\n\n L2Bridge.queryFilter(L2Bridge.filters.WithdrawalInitiated(sender)).then(\n (_withdrawals) => {\n const withdrawals = [],\n { length } = _withdrawals;\n let ret = 0;\n const check = (i) => {\n const w = { ..._withdrawals[i] };\n isWithdrawalFinalized(\n w.transactionHash,\n false,\n (res) => {\n Object.assign(w, res, { isEth: false });\n withdrawals.push(w);\n ret++;\n if (ret === length) {\n State.update({\n withdrawals,\n });\n }\n },\n true\n );\n };\n for (let i = 0; i < length; i++) check(i);\n }\n );\n\n return \"\";\n}\n\n// deposits\n\nconst handleDepositEth = (data) => {\n const amount = ethers.utils.parseUnits(data.amount);\n const value = amount.add(ethers.utils.parseUnits(l2DepositFee, \"wei\"));\n\n const encodedData = zkL1EthIFace.encodeFunctionData(\n \"requestL2Transaction(address,uint256,bytes,uint256,uint256,bytes[],address)\",\n [sender, amount, \"0x\", l2TxGasLimit, l2TxGasPerPubdataByte, [], sender]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network].eth.deposit,\n data: encodedData,\n value,\n gasLimit: ethers.BigNumber.from(\"500000\"),\n })\n .then(approvedTx)\n .catch(catchApproveError);\n};\n\n// TODO deposit on txBridge is missing the final address arg\n// NOT \"deposit(address,address,uint256,uint256,uint256,address)\"\n// BUT \"deposit(address,address,uint256,uint256,uint256)\"\n\nconst handleDeposit = (data) => {\n console.log(\"handleDeposit\", data);\n State.update({ isLoading: true, log: undefined, explorerLink: undefined });\n\n if (data.assetId === \"eth\") {\n return handleDepositEth(data);\n }\n\n handleApprove(data, (approveSuccess) => {\n if (!approveSuccess) {\n return console.log(\"approval was not successful\");\n }\n\n const l1Token = contracts[network][data.assetId].deposit;\n const amountBig = ethers.utils.parseUnits(\n data.amount,\n tokens[data.assetId].decimals\n );\n\n const value = ethers.utils.parseUnits(l2DepositFee, \"wei\");\n\n const encodedData = zkL1IFace.encodeFunctionData(\n \"deposit(address,address,uint256,uint256,uint256,address)\",\n [sender, l1Token, amountBig, l2TxGasLimit, l2TxGasPerPubdataByte, sender]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network].bridge.L1ERC20BridgeProxy,\n data: encodedData,\n value,\n gasLimit: ethers.BigNumber.from(\"500000\"),\n })\n .then(approvedTx)\n .catch(catchApproveError);\n });\n};\n\nconst handleApprove = (data, callback) => {\n console.log(\"handleApprove\", data);\n const contract = new ethers.Contract(\n contracts[network][data.assetId].deposit,\n erc20Abi.body,\n Ethers.provider().getSigner()\n );\n const { decimals } = tokens[data.assetId];\n const amountBig = ethers.utils.parseUnits(data.amount, decimals);\n\n contract\n .allowance(sender, contracts[network].bridge.L1ERC20BridgeProxy)\n .then((rawAllowance) => {\n console.log(\"rawAllowance\", rawAllowance);\n const hasAllowance = amountBig.lte(rawAllowance);\n if (hasAllowance) {\n return callback(true);\n }\n console.log(\"amountBig\", amountBig);\n contract\n .approve(contracts[network].bridge.L1ERC20BridgeProxy, amountBig)\n .then((tx) => {\n approvedTx(tx);\n callback(true);\n })\n .catch((e) => {\n catchApproveError(e);\n callback(false);\n });\n });\n};\n\n// withdrawals\n\nconst handleFinalizeEthWithdrawal = (i) => {\n const { l1BatchNumber, l2MessageIndex, l2TxNumberInBlock, message, proof } =\n allWithdrawals[i].withdrawalArgs;\n\n const contract = new ethers.Contract(\n contracts[network].eth.deposit,\n zkEthAbi.body,\n Ethers.provider().getSigner()\n );\n\n console.log(l1BatchNumber, l2MessageIndex, l2TxNumberInBlock, message, proof);\n\n contract\n .finalizeEthWithdrawal(\n l1BatchNumber,\n l2MessageIndex,\n l2TxNumberInBlock,\n message,\n proof,\n {}\n )\n .then((res) => console.log(res));\n};\n\nconst handleWithdrawEth = (data) => {\n const value = ethers.utils.parseUnits(data.amount);\n\n const encodedData = zkL2EthTokenIFace.encodeFunctionData(\n \"withdraw(address)\",\n [sender]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network][data.assetId].withdraw,\n data: encodedData,\n value,\n gasLimit: ethers.BigNumber.from(l2TxGasLimitWithdraw),\n })\n .then((tx) => {\n approvedTx(tx, true);\n })\n .catch(catchApproveError);\n};\n\nconst handleWithdraw = (data) => {\n console.log(\"handleWithdraw\", data);\n State.update({ isLoading: true, log: undefined, explorerLink: undefined });\n\n if (data.assetId === \"eth\") {\n return handleWithdrawEth(data);\n }\n\n const l2Token = contracts[network][data.assetId].withdraw;\n const amountBig = ethers.utils.parseUnits(\n data.amount,\n tokens[data.assetId].decimals\n );\n console.log(\"encodedData\", sender, l2Token, amountBig);\n const encodedData = zkL2IFace.encodeFunctionData(\n \"withdraw(address,address,uint256)\",\n [sender, l2Token, amountBig]\n );\n\n Ethers.provider()\n .getSigner()\n .sendTransaction({\n to: contracts[network].bridge.L2ERC20Bridge,\n data: encodedData,\n gasLimit: ethers.BigNumber.from(l2TxGasLimitWithdraw),\n })\n .then((tx) => {\n approvedTx(tx, true);\n })\n .catch(catchApproveError);\n};\n\n// balances\n\nconst getTokenBalance = (sender, isL1, tokenAddress, decimals, callback) => {\n if (!sender) return;\n const encodedData = erc20IFace.encodeFunctionData(\"balanceOf\", [sender]);\n const provider = isL1\n ? contracts[network].l1Provider\n : contracts[network].l2Provider;\n\n provider\n .call({\n to: tokenAddress,\n data: encodedData,\n })\n .then((rawBalance) => {\n if (rawBalance === \"0x\") {\n return callback(0);\n }\n const receiverBalanceHex = erc20IFace.decodeFunctionResult(\n \"balanceOf\",\n rawBalance\n );\n const balance = Big(receiverBalanceHex.toString())\n .div(Big(10).pow(decimals))\n .toFixed(2)\n .replace(/\\d(?=(\\d{3})+\\.)/g, \"$&,\");\n\n callback(balance);\n });\n};\n\nconst tab = !state.tab || state.tab === \"deposit\" ? \"deposit\" : \"withdraw\";\nconst clone = (o) => JSON.parse(JSON.stringify(o));\nconst { deposit, withdraw } = state;\n\nif (sender && !state.balancesUpdated) {\n // l1\n contracts[network].l1Provider.getBalance(sender).then((balance) => {\n const cloned = clone(deposit || defaultDeposit);\n const formatted = ethers.utils.formatUnits(balance);\n cloned.assets[0].balance = formatted.substring(\n 0,\n formatted.indexOf(\".\") + 5\n );\n State.update({ deposit: cloned });\n\n // USDC\n getTokenBalance(\n sender,\n true,\n contracts[network].usdc.deposit,\n tokens.usdc.decimals,\n (balance) => {\n cloned.assets[1].balance = balance;\n State.update({ deposit: cloned });\n }\n );\n });\n\n //l2;\n contracts[network].l2Provider\n .send(\"eth_getBalance\", [sender])\n .then((balance) => {\n const cloned = clone(withdraw || defaultWithdraw);\n const formatted = ethers.utils.formatUnits(balance);\n cloned.assets[0].balance = formatted.substring(\n 0,\n formatted.indexOf(\".\") + 5\n );\n State.update({ withdraw: cloned });\n\n // USDC\n getTokenBalance(\n sender,\n false,\n contracts[network].usdc.withdraw,\n tokens.usdc.decimals,\n (balance) => {\n cloned.assets[1].balance = balance;\n State.update({ withdraw: cloned });\n }\n );\n });\n\n State.update({ balancesUpdated: true });\n return \"\";\n}\n\n// bridge-ui functions\n\nconst onAction = (data) => {\n if (!data.amount) return;\n if (data.action === \"deposit\") handleDeposit(data);\n if (data.action === \"withdraw\") handleWithdraw(data);\n};\n\nconst onTabChange = (tab) => {\n let log = null;\n\n const depositDisabled =\n tab === \"deposit\" &&\n (chainId === ZKSYNC_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID);\n const withdrawDisabled =\n tab === \"withdraw\" &&\n (chainId === ETHEREUM_CHAIN_ID || chainId === GOERLI_CHAIN_ID);\n\n if (depositDisabled) {\n log = depositDisabledMsg;\n }\n if (withdrawDisabled) {\n log = withdrawDisabledMsg;\n }\n\n State.update({\n deposit: clone(withdraw),\n withdraw: clone(deposit),\n depositDisabled,\n withdrawDisabled,\n tab,\n log,\n });\n};\n\nconst { deposits, withdrawals, ethDeposits, ethWithdrawals } = state;\nconst allDeposits = [...deposits, ...ethDeposits].sort(sortByBlockNumber);\nconst allWithdrawals = [...withdrawals, ...ethWithdrawals].sort(\n sortByBlockNumber\n);\n\nconst renderTxLink = (tx) => {\n const isZk =\n chainId === ZKSYNC_CHAIN_ID || chainId === ZKSYNC_GOERLI_CHAIN_ID;\n return (\n <a\n href={`https://${\n network === \"testnet\" ? \"goerli.\" : \"\"\n }explorer.zksync.io/tx/${tx}`}\n target=\"_blank\"\n >\n {tx.substring(0, 6)} ... {tx.substring(tx.length - 4)}\n </a>\n );\n};\n\nconst renderTx = (tx, i) => {\n const { transactionHash: h, finalized, isEth } = tx;\n return (\n <>\n <p style={{ textAlign: \"left\" }}>\n {isEth ? \"ETH \" : \"USDC\"}\n {renderTxLink(h)}\n {typeof finalized === \"boolean\" && (\n <>\n <span>{finalized ? \"(finalized)\" : \"(not finalized)\"}</span>\n {!finalized && false && (\n <p style={{ marginTop: 16 }}>\n <button onClick={() => handleFinalizeEthWithdrawal(i)}>\n Finalize\n </button>\n </p>\n )}\n </>\n )}\n </p>\n </>\n );\n};\n\nreturn (\n <>\n <Widget\n src=\"mattlock.near/widget/bridge-ui\"\n props={{\n ...state,\n onTabChange,\n onAction,\n title: \"zkBridge\",\n }}\n />\n <div style={{ textAlign: \"center\" }}>\n <div style={{ width: 300, margin: \"auto\" }}>\n <h4 style={{ marginTop: 16 }}>Withdrawals</h4>\n {allWithdrawals.map(renderTx)}\n <h4 style={{ marginTop: 16 }}>Deposits</h4>\n {allDeposits.map(renderTx)}\n </div>\n </div>\n </>\n);\n" } } } } }
Empty result
No logs
Receipt:
Predecessor ID:
Receiver ID:
Gas Burned:
223 Ggas
Tokens Burned:
0 
Transferred 0.01816  to mattlock.near
Empty result
No logs