Search
Search

Transaction: 7NtvgU6...RSz5

Signed by
Status
Succeeded
Transaction Fee
0.00111 
Deposit Value
0 
Gas Used
11 Tgas
Attached Gas
100 Tgas
Created
March 08, 2024 at 3:52:57pm
Hash
7NtvgU6hsA3HLaAU8cCaNhPhfDAGzJjUu6kRdEYjRSz5

Actions

Called method: 'set' in contract: v1.social08.testnet
Arguments:
{ "data": { "bot.testnet": { "widget": { "chainsig-sign-eth-tx": { "": "const { accountId } = context;\nif (!accountId) return <h4>Please Sign In with your Near Account</h4>;\n\n// get pending tx or user args path, to address\nconst baseTx = Storage.privateGet(\"baseTx\");\nconst txPayload = Storage.privateGet(\"txPayload\");\nconst to = Storage.privateGet(\"to\");\nconst path = Storage.privateGet(\"path\");\nconst amount = Storage.privateGet(\"amount\");\nconst useMock = Storage.privateGet(\"useMock\");\nconst loading = <p>Loading...</p>;\n\nif (to === null || path === null || amount === null) {\n return loading;\n}\n\n// works but why?\n\nconst gasPricePreFetch = fetch(\n `https://sepolia.beaconcha.in/api/v1/execution/gasnow`\n);\n\n// settings\nconst mpcMockContract = `signer.canhazgas.testnet`;\nconst mpcContract = `multichain-testnet-2.testnet`;\nconst chainId = 11155111; // SEPOLIA\nconst nearGas = 300000000000000;\nconst gasLimit = 21000;\n\ninitState({\n amount: \"0.1\",\n to,\n path: path || \"ethereum,1\",\n amount: amount || \"0.01\",\n useMock: useMock || false,\n});\n\n// helpers\n\nconst flashAlert = (alert, dur) => {\n State.update({\n alert,\n });\n setTimeout(() => State.update({ alert: null }), dur || 3000);\n};\n\nconst refreshBalance = () => {\n State.update({\n balance: \"loading...\",\n });\n getEthereumAddress(state.path, state.address);\n};\n\nconst getSepoliaProvider = () => {\n return new ethers.providers.JsonRpcProvider(\n \"https://ethereum-sepolia.publicnode.com\"\n );\n};\n\n// spoof Ethereum publicKey to match mock contract\nconst getEthereumAddress = (path, address) => {\n // using mock contract\n if (!address) {\n const signingKey = new ethers.utils.SigningKey(\n ethers.utils.sha256(ethers.utils.toUtf8Bytes(accountId + \",\" + path))\n );\n address = ethers.utils.computeAddress(signingKey.privateKey);\n }\n\n console.log(\"getEthereumAddress\", address);\n\n getSepoliaProvider()\n .getBalance(address)\n .then((balance) => {\n State.update({\n address,\n balance: ethers.utils.formatEther(balance),\n });\n });\n};\n\nif (!state.address && state.useMock) {\n getEthereumAddress(state.path);\n return loading;\n}\n\nif (!state.mpcKey) {\n State.update({\n mpcKey: Near.view(\n state.useMock ? mpcMockContract : mpcContract,\n \"public_key\"\n ),\n });\n return loading;\n}\n\n// returning from signing with txHash in url props\n\nlet txHash = props.transactionHashes;\nif (txHash) {\n txHash = txHash.split(\",\")[0];\n}\n\nconst decodeTx = () => {\n console.log(\"decode\", { baseTx, txPayload });\n if (!baseTx || !txPayload) return;\n\n const res = fetch(`https://rpc.testnet.near.org`, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: \"dontcare\",\n method: \"EXPERIMENTAL_tx_status\",\n params: [txHash, accountId],\n }),\n });\n\n if (!res || !res.ok) {\n return setTimeout(decodeTx, 500);\n }\n\n const args = JSON.parse(\n atob(res.body.result.transaction.actions[0].FunctionCall.args)\n );\n const sigRes = JSON.parse(atob(res.body.result.status.SuccessValue));\n\n console.log(\"decode 2\", {\n res,\n args,\n sigRes,\n txPayload,\n });\n\n if (JSON.stringify(args.payload) !== JSON.stringify(txPayload)) return;\n\n if (!state.useMock) {\n args.payload.reverse();\n }\n\n const sig = {\n r: \"0x\" + sigRes[0].substring(2).toLowerCase(),\n s: \"0x\" + sigRes[1].toLowerCase(),\n v,\n };\n let addressRecovered = false;\n for (let v = 0; v < 2; v++) {\n sig.v = v + chainId * 2 + 35;\n const recoveredAddress = ethers.utils\n .recoverAddress(args.payload, sig)\n .toLowerCase();\n if (recoveredAddress === state.address) {\n addressRecovered = true;\n break;\n }\n }\n\n if (!addressRecovered) {\n console.log(\"signature failed to recover to correct address\");\n return;\n }\n\n const signedTx = ethers.utils.serializeTransaction(baseTx, sig);\n console.log(\"signed tx\", signedTx);\n\n getSepoliaProvider()\n .send(\"eth_sendRawTransaction\", [signedTx])\n .then((hash) => {\n flashAlert(\n \"TX Sent! Explorer link will appear soon and balance will update automatically in 30s\"\n );\n setTimeout(() => {\n flashAlert(\n <a href={`https://sepolia.etherscan.io/tx/${hash}`} target=\"_blank\">\n Explorer Link\n </a>,\n 60000\n );\n }, 4000);\n setTimeout(refreshBalance, 50000);\n setTimeout(refreshBalance, 120000);\n })\n .catch((e) => {\n if (/nonce too low/gi.test(JSON.stringify(e))) {\n console.log(\"tx has been tried, removing localStorage\");\n Storage.privateSet(\"baseTx\", undefined);\n Storage.privateSet(\"txPayload\", undefined);\n return;\n }\n if (/gas too low|underpriced/gi.test(JSON.stringify(e))) {\n console.log(e);\n flashAlert(\n \"Insufficient funds or gas too low. Try sending a smaller amount.\"\n );\n return;\n }\n console.log(e);\n });\n};\n\nconsole.log(\"decode tx\", {\n decoded: state.decoded,\n txHash,\n address: state.address,\n});\n\nif (!state.decoded && txHash && state.address) {\n decodeTx();\n State.update({ decoded: true });\n return loading;\n}\n\n// Use MPC to sign\n\nconst sign = () => {\n let to = state.to;\n try {\n to = ethers.utils.getAddress(to);\n } catch (e) {\n return flashAlert(\n \"Invalid to address. Please add a proper Ethereum address to send ETH to.\"\n );\n }\n\n getSepoliaProvider()\n .getTransactionCount(state.address)\n .then((nonce) => {\n // Ethereum TX\n const amount = state.amount;\n // 2 gwei + some randomness\n const gasPriceFetch = fetch(\n `https://sepolia.beaconcha.in/api/v1/execution/gasnow`\n );\n const gasPriceData = gasPriceFetch || gasPricePreFetch;\n const { rapid, fast, standard } = gasPriceData.body.data;\n const gasPrice = Math.max(rapid, fast, standard);\n if (!gasPrice) {\n return flashAlert(\n \"Unable to get gas price. Please refresh and try again.\"\n );\n }\n\n const value = ethers.utils.hexlify(ethers.utils.parseUnits(amount));\n\n if (value === \"0x00\") {\n return flashAlert(\"Amount is zero. Please try a non-zero amount.\");\n }\n\n const baseTx = {\n to,\n nonce,\n data: [],\n value: value,\n gasLimit,\n gasPrice,\n chainId,\n };\n\n // check balance\n if (\n !state.balance ||\n new BN(ethers.utils.parseUnits(state.balance).toString()).lt(\n new BN(ethers.utils.parseUnits(amount).toString()).add(\n new BN(gasPrice).mul(new BN(gasLimit))\n )\n )\n ) {\n return flashAlert(\"Insufficient funds\");\n }\n\n Storage.privateSet(\"baseTx\", baseTx);\n const unsignedTx = ethers.utils.serializeTransaction(baseTx);\n const txHash = ethers.utils.keccak256(unsignedTx);\n const payload = Object.values(ethers.utils.arrayify(txHash));\n if (!state.useMock) payload.reverse();\n Storage.privateSet(\"txPayload\", payload);\n\n Near.call(\n state.useMock ? mpcMockContract : mpcContract,\n \"sign\",\n {\n payload,\n path: state.path,\n },\n nearGas\n );\n });\n};\n\nconst Theme = styled.div`\n box-sizing: border-box;\n margin: auto;\n text-align: center;\n\n .alert {\n background-color: #eeeeff\n }\n\n .container {\n text-align: left;\n width: 516px;\n }\n .group {\n display: flex;\n justify-content: flex-start;\n margin-bottom: 16px;\n line-height: 32px;\n > div, > input {\n margin-right: 16px;\n }\n > input {\n border: 1px solid #ddd;\n border-radius: 8px !important;\n padding: 0 4px;\n width: 100px;\n }\n > div:nth-child(1) {\n width: 40px;\n }\n > .address {\n width: 416px;\n }\n }\n`;\n\nreturn (\n <Theme>\n <div className=\"container\">\n {!state.useMock && (\n <iframe\n style={{ display: \"none\" }}\n src={\"https://near-mpc-kdf-iframe.pages.dev/\"}\n message={state.message}\n onMessage={(res) => {\n if (res.loaded) {\n State.update({\n message: { publicKey: state.mpcKey, accountId, path },\n });\n }\n if (res.address) {\n getEthereumAddress(state.path, res.address);\n }\n }}\n />\n )}\n <h4>Send ETH using Near Account</h4>\n\n {state.alert && <p className=\"alert\">{state.alert}</p>}\n\n <p>\n Sending Ethereum Address:\n <br />\n <a\n href={`https://sepolia.etherscan.io/address/${state.address}`}\n target=\"_blank\"\n >\n {state.address}\n </a>\n <br />\n Balance: {state.balance}{\" \"}\n {state.balance === \"0.0\" && (\n <span>(fund account before sending from Near)</span>\n )}\n </p>\n\n <div className=\"group\">\n <div>Path</div>\n <input\n className=\"amount\"\n type=\"text\"\n value={state.path}\n onChange={({ target: { value } }) => {\n if (state.useMock) {\n getEthereumAddress(value);\n } else {\n State.update({\n message: { publicKey: state.mpcKey, accountId, path: value },\n });\n }\n Storage.privateSet(\"path\", value);\n State.update({ path: value });\n }}\n />\n <div>\n + &nbsp;<strong>{accountId}</strong>\n </div>\n </div>\n\n {state.balance && ![\"0.0\", \"loading...\"].includes(state.balance) ? (\n <>\n <div className=\"group\">\n <div>Send</div>\n <input\n className=\"amount\"\n min={0.01}\n max={1}\n step={0.01}\n type=\"number\"\n value={state.amount}\n onChange={({ target: { value } }) => {\n Storage.privateSet(\"amount\", value);\n State.update({ amount: value });\n }}\n />\n <div>ETH</div>\n </div>\n <div className=\"group\">\n <div>To</div>\n <input\n placeHolder=\"0x0123456789abcdef...\"\n className=\"address\"\n type=\"text\"\n value={state.to}\n onChange={({ target: { value } }) => {\n Storage.privateSet(\"to\", value);\n State.update({ to: value });\n }}\n />\n </div>\n <div className=\"group\">\n <div></div>\n <button onClick={sign}>Send</button>\n </div>\n </>\n ) : (\n <div className=\"group\">\n <div></div>\n <button onClick={refreshBalance}>Refresh Balance</button>\n </div>\n )}\n\n <p>NEAR Signing Contract: {state.useMock ? \"MOCK MPC\" : \"Testnet MPC\"}</p>\n <button\n onClick={() => {\n Storage.privateSet(\"useMock\", !state.useMock);\n State.update({ useMock: !state.useMock });\n getEthereumAddress(state.path);\n }}\n >\n Switch to {!state.useMock ? \"MOCK MPC\" : \"Testnet MPC\"}\n </button>\n </div>\n </Theme>\n);\n", "metadata": { "image": {}, "fork_of": "md1.testnet/widget/chainsig-sign-eth-tx@157706948" } } } } } }

Transaction Execution Plan

Convert Transaction To Receipt
Gas Burned:
2 Tgas
Tokens Burned:
0.00025 
Receipt:
Predecessor ID:
Gas Burned:
8 Tgas
Tokens Burned:
0.00086 
Called method: 'set' in contract: v1.social08.testnet
Arguments:
{ "data": { "bot.testnet": { "widget": { "chainsig-sign-eth-tx": { "": "const { accountId } = context;\nif (!accountId) return <h4>Please Sign In with your Near Account</h4>;\n\n// get pending tx or user args path, to address\nconst baseTx = Storage.privateGet(\"baseTx\");\nconst txPayload = Storage.privateGet(\"txPayload\");\nconst to = Storage.privateGet(\"to\");\nconst path = Storage.privateGet(\"path\");\nconst amount = Storage.privateGet(\"amount\");\nconst useMock = Storage.privateGet(\"useMock\");\nconst loading = <p>Loading...</p>;\n\nif (to === null || path === null || amount === null) {\n return loading;\n}\n\n// works but why?\n\nconst gasPricePreFetch = fetch(\n `https://sepolia.beaconcha.in/api/v1/execution/gasnow`\n);\n\n// settings\nconst mpcMockContract = `signer.canhazgas.testnet`;\nconst mpcContract = `multichain-testnet-2.testnet`;\nconst chainId = 11155111; // SEPOLIA\nconst nearGas = 300000000000000;\nconst gasLimit = 21000;\n\ninitState({\n amount: \"0.1\",\n to,\n path: path || \"ethereum,1\",\n amount: amount || \"0.01\",\n useMock: useMock || false,\n});\n\n// helpers\n\nconst flashAlert = (alert, dur) => {\n State.update({\n alert,\n });\n setTimeout(() => State.update({ alert: null }), dur || 3000);\n};\n\nconst refreshBalance = () => {\n State.update({\n balance: \"loading...\",\n });\n getEthereumAddress(state.path, state.address);\n};\n\nconst getSepoliaProvider = () => {\n return new ethers.providers.JsonRpcProvider(\n \"https://ethereum-sepolia.publicnode.com\"\n );\n};\n\n// spoof Ethereum publicKey to match mock contract\nconst getEthereumAddress = (path, address) => {\n // using mock contract\n if (!address) {\n const signingKey = new ethers.utils.SigningKey(\n ethers.utils.sha256(ethers.utils.toUtf8Bytes(accountId + \",\" + path))\n );\n address = ethers.utils.computeAddress(signingKey.privateKey);\n }\n\n console.log(\"getEthereumAddress\", address);\n\n getSepoliaProvider()\n .getBalance(address)\n .then((balance) => {\n State.update({\n address,\n balance: ethers.utils.formatEther(balance),\n });\n });\n};\n\nif (!state.address && state.useMock) {\n getEthereumAddress(state.path);\n return loading;\n}\n\nif (!state.mpcKey) {\n State.update({\n mpcKey: Near.view(\n state.useMock ? mpcMockContract : mpcContract,\n \"public_key\"\n ),\n });\n return loading;\n}\n\n// returning from signing with txHash in url props\n\nlet txHash = props.transactionHashes;\nif (txHash) {\n txHash = txHash.split(\",\")[0];\n}\n\nconst decodeTx = () => {\n console.log(\"decode\", { baseTx, txPayload });\n if (!baseTx || !txPayload) return;\n\n const res = fetch(`https://rpc.testnet.near.org`, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: \"dontcare\",\n method: \"EXPERIMENTAL_tx_status\",\n params: [txHash, accountId],\n }),\n });\n\n if (!res || !res.ok) {\n return setTimeout(decodeTx, 500);\n }\n\n const args = JSON.parse(\n atob(res.body.result.transaction.actions[0].FunctionCall.args)\n );\n const sigRes = JSON.parse(atob(res.body.result.status.SuccessValue));\n\n console.log(\"decode 2\", {\n res,\n args,\n sigRes,\n txPayload,\n });\n\n if (JSON.stringify(args.payload) !== JSON.stringify(txPayload)) return;\n\n if (!state.useMock) {\n args.payload.reverse();\n }\n\n const sig = {\n r: \"0x\" + sigRes[0].substring(2).toLowerCase(),\n s: \"0x\" + sigRes[1].toLowerCase(),\n v,\n };\n let addressRecovered = false;\n for (let v = 0; v < 2; v++) {\n sig.v = v + chainId * 2 + 35;\n const recoveredAddress = ethers.utils\n .recoverAddress(args.payload, sig)\n .toLowerCase();\n if (recoveredAddress === state.address) {\n addressRecovered = true;\n break;\n }\n }\n\n if (!addressRecovered) {\n console.log(\"signature failed to recover to correct address\");\n return;\n }\n\n const signedTx = ethers.utils.serializeTransaction(baseTx, sig);\n console.log(\"signed tx\", signedTx);\n\n getSepoliaProvider()\n .send(\"eth_sendRawTransaction\", [signedTx])\n .then((hash) => {\n flashAlert(\n \"TX Sent! Explorer link will appear soon and balance will update automatically in 30s\"\n );\n setTimeout(() => {\n flashAlert(\n <a href={`https://sepolia.etherscan.io/tx/${hash}`} target=\"_blank\">\n Explorer Link\n </a>,\n 60000\n );\n }, 4000);\n setTimeout(refreshBalance, 50000);\n setTimeout(refreshBalance, 120000);\n })\n .catch((e) => {\n if (/nonce too low/gi.test(JSON.stringify(e))) {\n console.log(\"tx has been tried, removing localStorage\");\n Storage.privateSet(\"baseTx\", undefined);\n Storage.privateSet(\"txPayload\", undefined);\n return;\n }\n if (/gas too low|underpriced/gi.test(JSON.stringify(e))) {\n console.log(e);\n flashAlert(\n \"Insufficient funds or gas too low. Try sending a smaller amount.\"\n );\n return;\n }\n console.log(e);\n });\n};\n\nconsole.log(\"decode tx\", {\n decoded: state.decoded,\n txHash,\n address: state.address,\n});\n\nif (!state.decoded && txHash && state.address) {\n decodeTx();\n State.update({ decoded: true });\n return loading;\n}\n\n// Use MPC to sign\n\nconst sign = () => {\n let to = state.to;\n try {\n to = ethers.utils.getAddress(to);\n } catch (e) {\n return flashAlert(\n \"Invalid to address. Please add a proper Ethereum address to send ETH to.\"\n );\n }\n\n getSepoliaProvider()\n .getTransactionCount(state.address)\n .then((nonce) => {\n // Ethereum TX\n const amount = state.amount;\n // 2 gwei + some randomness\n const gasPriceFetch = fetch(\n `https://sepolia.beaconcha.in/api/v1/execution/gasnow`\n );\n const gasPriceData = gasPriceFetch || gasPricePreFetch;\n const { rapid, fast, standard } = gasPriceData.body.data;\n const gasPrice = Math.max(rapid, fast, standard);\n if (!gasPrice) {\n return flashAlert(\n \"Unable to get gas price. Please refresh and try again.\"\n );\n }\n\n const value = ethers.utils.hexlify(ethers.utils.parseUnits(amount));\n\n if (value === \"0x00\") {\n return flashAlert(\"Amount is zero. Please try a non-zero amount.\");\n }\n\n const baseTx = {\n to,\n nonce,\n data: [],\n value: value,\n gasLimit,\n gasPrice,\n chainId,\n };\n\n // check balance\n if (\n !state.balance ||\n new BN(ethers.utils.parseUnits(state.balance).toString()).lt(\n new BN(ethers.utils.parseUnits(amount).toString()).add(\n new BN(gasPrice).mul(new BN(gasLimit))\n )\n )\n ) {\n return flashAlert(\"Insufficient funds\");\n }\n\n Storage.privateSet(\"baseTx\", baseTx);\n const unsignedTx = ethers.utils.serializeTransaction(baseTx);\n const txHash = ethers.utils.keccak256(unsignedTx);\n const payload = Object.values(ethers.utils.arrayify(txHash));\n if (!state.useMock) payload.reverse();\n Storage.privateSet(\"txPayload\", payload);\n\n Near.call(\n state.useMock ? mpcMockContract : mpcContract,\n \"sign\",\n {\n payload,\n path: state.path,\n },\n nearGas\n );\n });\n};\n\nconst Theme = styled.div`\n box-sizing: border-box;\n margin: auto;\n text-align: center;\n\n .alert {\n background-color: #eeeeff\n }\n\n .container {\n text-align: left;\n width: 516px;\n }\n .group {\n display: flex;\n justify-content: flex-start;\n margin-bottom: 16px;\n line-height: 32px;\n > div, > input {\n margin-right: 16px;\n }\n > input {\n border: 1px solid #ddd;\n border-radius: 8px !important;\n padding: 0 4px;\n width: 100px;\n }\n > div:nth-child(1) {\n width: 40px;\n }\n > .address {\n width: 416px;\n }\n }\n`;\n\nreturn (\n <Theme>\n <div className=\"container\">\n {!state.useMock && (\n <iframe\n style={{ display: \"none\" }}\n src={\"https://near-mpc-kdf-iframe.pages.dev/\"}\n message={state.message}\n onMessage={(res) => {\n if (res.loaded) {\n State.update({\n message: { publicKey: state.mpcKey, accountId, path },\n });\n }\n if (res.address) {\n getEthereumAddress(state.path, res.address);\n }\n }}\n />\n )}\n <h4>Send ETH using Near Account</h4>\n\n {state.alert && <p className=\"alert\">{state.alert}</p>}\n\n <p>\n Sending Ethereum Address:\n <br />\n <a\n href={`https://sepolia.etherscan.io/address/${state.address}`}\n target=\"_blank\"\n >\n {state.address}\n </a>\n <br />\n Balance: {state.balance}{\" \"}\n {state.balance === \"0.0\" && (\n <span>(fund account before sending from Near)</span>\n )}\n </p>\n\n <div className=\"group\">\n <div>Path</div>\n <input\n className=\"amount\"\n type=\"text\"\n value={state.path}\n onChange={({ target: { value } }) => {\n if (state.useMock) {\n getEthereumAddress(value);\n } else {\n State.update({\n message: { publicKey: state.mpcKey, accountId, path: value },\n });\n }\n Storage.privateSet(\"path\", value);\n State.update({ path: value });\n }}\n />\n <div>\n + &nbsp;<strong>{accountId}</strong>\n </div>\n </div>\n\n {state.balance && ![\"0.0\", \"loading...\"].includes(state.balance) ? (\n <>\n <div className=\"group\">\n <div>Send</div>\n <input\n className=\"amount\"\n min={0.01}\n max={1}\n step={0.01}\n type=\"number\"\n value={state.amount}\n onChange={({ target: { value } }) => {\n Storage.privateSet(\"amount\", value);\n State.update({ amount: value });\n }}\n />\n <div>ETH</div>\n </div>\n <div className=\"group\">\n <div>To</div>\n <input\n placeHolder=\"0x0123456789abcdef...\"\n className=\"address\"\n type=\"text\"\n value={state.to}\n onChange={({ target: { value } }) => {\n Storage.privateSet(\"to\", value);\n State.update({ to: value });\n }}\n />\n </div>\n <div className=\"group\">\n <div></div>\n <button onClick={sign}>Send</button>\n </div>\n </>\n ) : (\n <div className=\"group\">\n <div></div>\n <button onClick={refreshBalance}>Refresh Balance</button>\n </div>\n )}\n\n <p>NEAR Signing Contract: {state.useMock ? \"MOCK MPC\" : \"Testnet MPC\"}</p>\n <button\n onClick={() => {\n Storage.privateSet(\"useMock\", !state.useMock);\n State.update({ useMock: !state.useMock });\n getEthereumAddress(state.path);\n }}\n >\n Switch to {!state.useMock ? \"MOCK MPC\" : \"Testnet MPC\"}\n </button>\n </div>\n </Theme>\n);\n", "metadata": { "image": {}, "fork_of": "md1.testnet/widget/chainsig-sign-eth-tx@157706948" } } } } } }
Result:
{ "block_height": "158893584" }
No logs
Receipt:
Predecessor ID:
Receiver ID:
Gas Burned:
223 Ggas
Tokens Burned:
0 
Transferred 0.0182  to bot.testnet
Empty result
No logs