{"id":13523128,"url":"https://github.com/b3hr4d/eth_payment_tutorial","last_synced_at":"2026-01-17T12:24:18.735Z","repository":{"id":202655096,"uuid":"707842586","full_name":"b3hr4d/eth_payment_tutorial","owner":"b3hr4d","description":"Step-by-step tutorial for building a decentralized ETH payment system on the Internet Computer.","archived":false,"fork":false,"pushed_at":"2024-08-03T17:38:45.000Z","size":5316,"stargazers_count":15,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-02T07:33:34.677Z","etag":null,"topics":["dapp","ethereum","icp","internet-computer","web3"],"latest_commit_sha":null,"homepage":"https://uu4vt-kqaaa-aaaap-abmia-cai.icp0.io/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/b3hr4d.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-20T19:35:28.000Z","updated_at":"2024-10-29T18:23:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"24ba7895-23aa-4e5e-9c88-b0f53e946ba1","html_url":"https://github.com/b3hr4d/eth_payment_tutorial","commit_stats":null,"previous_names":["b3hr4d/eth_payment_tutorial"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/b3hr4d/eth_payment_tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b3hr4d%2Feth_payment_tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b3hr4d%2Feth_payment_tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b3hr4d%2Feth_payment_tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b3hr4d%2Feth_payment_tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/b3hr4d","download_url":"https://codeload.github.com/b3hr4d/eth_payment_tutorial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/b3hr4d%2Feth_payment_tutorial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28508464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T11:50:55.898Z","status":"ssl_error","status_checked_at":"2026-01-17T11:50:55.569Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dapp","ethereum","icp","internet-computer","web3"],"created_at":"2024-08-01T06:00:55.848Z","updated_at":"2026-01-17T12:24:18.706Z","avatar_url":"https://github.com/b3hr4d.png","language":"JavaScript","funding_links":[],"categories":["Courses, Tutorials, and Samples"],"sub_categories":["Tutorials and Samples"],"readme":"# Building a Cross-Chain ETH Payment and E-Commerce Platform on the Internet Computer: A Step-by-Step Tutorial\n\n## Introduction\n\nThis comprehensive tutorial guides you through the process of building a decentralized e-commerce platform on the Internet Computer that can accept Ethereum (ETH) payments, handle withdrawals, and manage a digital storefront. Starting from a basic template, we'll incrementally add features to create a robust, cross-chain solution.\n\n## Why I Made This Tutorial?\n\nThis tutorial is a hands-on guide designed for developers new to the Internet Computer blockchain. I've created a set of libraries to make development easier and more enjoyable, and this tutorial walks you through the process of building a decentralized ETH payment system using these libraries.\n\nWe'll explore key features like HTTP outcalls and stable memory, providing a solid foundation for anyone looking to bring their dream project to life on the Internet Computer. Although this tutorial uses my specific set of methods and libraries, there are many other tools and techniques you could use.\n\nFor example, you could integrate the [`ic-eth-rpc`](https://github.com/internet-computer-protocol/ic-eth-rpc) package for Ethereum RPC calls or accept ckETH as a payment method. The main goal here is to demonstrate the Internet Computer's flexibility and ease of use, especially when it comes to confirming transactions on-chain.\n\n## Objective\n\nThe goal of this tutorial is to create a fully functional decentralized e-commerce platform that:\n\n- Accepts ETH as payment for digital items\n- Integrates with Ethereum smart contracts for payment processing.\n- Verifies transactions on-chain for added security\n- Allows for the withdrawal of ETH to an Ethereum address\n- Utilizes stable memory to keep track of transactions and items.\n- Manages a digital storefront with items for sale\n- Implements access control to secure sensitive operations\n\n## Final Product(YouTube Video)\n\n[![Watch the video](https://img.youtube.com/vi/lPNOqKwPRlE/maxresdefault.jpg)](https://youtu.be/lPNOqKwPRlE)\n\n## Prerequisites\n\n- [DFINITY Canister SDK](https://sdk.dfinity.org/docs/quickstart/local-quickstart.html)\n- [Node.js](https://nodejs.org/en/download/)\n- [Rust](https://www.rust-lang.org/tools/install)\n- Basic understanding of Ethereum and smart contracts\n\n## Step 1: Clone the Starter Repository and Run the Project Locally\n\nWe'll begin by cloning the [`ic-rust-nextjs`](https://github.com/b3hr4d/ic-rust-nextjs) repository, which serves as our starter template.\n\n### What's Inside the Starter Repository?\n\n- [`README.md`](https://github.com/b3hr4d/ic-rust-nextjs/blob/main/README.md): Provides an overview and setup instructions.\n- [`Cargo.toml`](https://github.com/b3hr4d/ic-rust-nextjs/blob/main/Cargo.toml): The manifest file for the Rust workspace.\n- [`dfx.json`](https://github.com/b3hr4d/ic-rust-nextjs/blob/main/dfx.json): Configuration file for the DFINITY Canister SDK.\n- [`backend/hello/src/lib.rs`](https://github.com/b3hr4d/ic-rust-nextjs/blob/main/backend/hello/src/lib.rs): The Rust code for the backend logic.\n- [`src/pages/index.tsx`](https://github.com/b3hr4d/ic-rust-nextjs/blob/main/src/pages/index.tsx): The main page of the Next.js app.\n- [`src/service/hello.ts`](https://github.com/b3hr4d/ic-rust-nextjs/blob/main/src/service/hello.ts): Service file to interact with the Rust backend.\n\n### Cloning the Repository\n\nTo clone the repository, open your terminal and run:\n\n```bash\ngit clone https://github.com/b3hr4d/ic-rust-nextjs.git\n```\n\n### Running the Project Locally\n\nAfter cloning the repository, the next step is to run the project locally to ensure everything is set up correctly. Follow the commands below based on your package manager (Yarn or npm).\n\n### Installing Dependencies\n\nFirst, let's install all the required dependencies:\n\n```bash\nyarn install:all\n# or\nnpm run install:all\n```\n\n### Running Local Internet Computer\n\nTo start the local Internet Computer environment, run:\n\n```bash\nyarn dfx:start\n# or\nnpm run dfx:start\n```\n\n### Deploying to the Local Internet Computer\n\nDeploy your the backend canister to the local Internet Computer by running:\n\n```bash\nyarn deploy payment\n# or\nnpm run deploy payment\n```\n\n### Running the Next.js App\n\nFinally, to run the Next.js(frontend) app, execute:\n\n```bash\nyarn dev\n# or\nnpm run dev\n```\n\nOpen your browser and navigate to [http://localhost:3000](http://localhost:3000) to see your app running.\n\n![Alt text](assets/starting.png)\n\n## Step 1.1: Rename the Canister ID\n\nIn this step, we'll rename the canister ID to make it easier to reference in the code.\n\n### Renaming the Canister ID\n\nTo rename the default project name and canister name from \"hello\" to \"payment\", follow these steps:\n\n1. Open the `Cargo.toml` file in the \"backend/hello\" directory.\n\n2. Find the line that says `name = \"hello\"` and change it to `name = \"payment\"`.\n\n3. Save the file.\n\n4. Next, open the `dfx.json` file in the root directory of your project.\n\n5. Find the line that says `\"hello\": {` and change it to `\"payment\": {`.\n\n6. Inside the `\"payment\"` object change \"package\" from `\"hello\"` to `\"payment\"` and candid from `\"backend/hello/hello.did\"` to `\"backend/payment/payment.did\"`.\n\n7. Save the file.\n\n8. Rename the directory `backend/hello` to `backend/payment`.\n\n9. Open the `Cargo.toml` file in the root directory again.\n\n10. Find the line that says `members = [\"backend/hello\"]` and change it to `members = [\"backend/payment\"]`.\n\n11. Save the file.\n\n12. Open the `payment` directory and locate the `hello.did` file.\n\n13. Ensure that the `.did` file is named `payment.did`.\n\n14. Save any changes if necessary.\n\nWith these changes, your project and canister will now be named \"payment\" instead of \"hello\".\n\n## Step 2: Modify the Backend for ETH Deposits\n\nIn this step, we'll modify the backend to include a function that generates a deposit principal from a canister ID. This is essential for converting SepoliaETH into ckSepoliaETH, as per the ckEth documentation.\n\n### Installing the `b3_utils` Rust Crate\n\nFirst, we need to install the [b3_utils](https://docs.rs/b3_utils/latest/b3_utils/) Rust crate. Open your `Cargo.toml` file and add the following line under `[dependencies]`:\n\n```toml\nb3_utils = \"0.11.0\"\n```\n\nor run this command:\n\n```bash\ncargo add b3_utils\n```\n\n### Modifying the `greet` Function\n\nReplace the existing `greet` function with the new `deposit_principal` function:\n\n```rust\nuse b3_utils::{vec_to_hex_string_with_0x, Subaccount};\nuse candid::Principal;\n\n#[ic_cdk::query]\nfn deposit_principal(principal: String) -\u003e String {\n    let principal = Principal::from_text(principal).unwrap();\n    let subaccount = Subaccount::from_principal(principal);\n\n    let bytes32 = subaccount.to_bytes32().unwrap();\n\n    vec_to_hex_string_with_0x(bytes32)\n}\n```\n\n#### Why This Change?\n\n1. **Function Annotation**: We use `#[ic_cdk::query]` to indicate that this is a query method, meaning it's read-only and doesn't modify the state.\n\n2. **Principal Conversion**: We convert the passed string into a `Principal` object, which is essential for generating a subaccount.\n\n3. **Subaccount Generation**: We generate a `Subaccount` from the `Principal`, which is a necessary step for depositing ETH.\n\n4. **Bytes32 Conversion**: We convert the subaccount into a bytes32 array, which is the required format for the smart contract on the Sepolia Ethereum testnet.\n\n5. **Hex String**: Finally, we convert the bytes32 array into a hex string with a \"0x\" prefix, which can be used as an argument for the smart contract.\n\n### Deploy the Modified Backend Canister\n\nAfter making the changes to the backend, open another terminal and deploy the canister to your local Internet Computer environment using the following command:\n\n```bash\nyarn deploy payment\n```\n\nNote: confirm the consent with `yes` to the change on the terminal.\n\nThis will deploy only the `payment` canister, which now includes the `deposit_principal` function.\n\n### Update the Frontend Code\n\nNavigate to the frontend code where the `useQueryCall` hook is used. This is typically found in a component file. Change the method from `\"greet\"` to `\"deposit_principal\"`:\n\n```javascript\nconst { call, data, error, loading } = useQueryCall({\n  functionName: \"deposit_principal\"\n})\n```\n\n#### Testing the Changes\n\n1. **Pass the Canister ID**: Update the frontend to include an input field where you can enter the canister ID.\n\n2. **Check the Output**: The output should be a hexadecimal string that represents the deposit principal, which can be used for depositing ETH.\n\n![Alt text](assets/deposit_principal.png)\n\n## Step 3: Integrate MetaMask and Call the Minter Helper Contract\n\nIn this step, we'll integrate MetaMask using the [wagmi](https://wagmi.sh) library and set up the frontend to call the minter helper contract's deposit function.\n\n### Prerequisites\n\n- Make sure you have the [MetaMask extension](https://metamask.io/download.html) installed in your browser.\n\n### Installing `wagmi` and `viem`\n\nFirst, install the `wagmi` and `viem` packages:\n\n```bash\nyarn add wagmi viem\n```\n\n### Configure `wagmi`\n\nCreate a new file `config.ts` inside the `src/service` directory and add the following code:\n\n```javascript\nimport { createPublicClient, http } from \"viem\"\nimport { createConfig, sepolia } from \"wagmi\"\n\nexport const config = createConfig({\n  chains: [sepolia],\n  connectors: [injected()],\n  client({ chain }) {\n    return createClient({ chain, transport: http() })\n  }\n})\n```\n\n### Create the `Wallet` Component\n\nCreate a new file named `Wallet.tsx` inside the `src/components` folder and add the following code:\n\n```javascript\nimport { useAccount, useConnect, useDisconnect } from \"wagmi\"\nimport { MetaMaskConnector } from \"wagmi/connectors/metaMask\"\n\ninterface WalletProps {}\n\nconst Wallet: React.FC\u003cWalletProps\u003e = ({}) =\u003e {\n  const { address } = useAccount()\n\n  const { connect } = useConnect({\n    connector: new MetaMaskConnector()\n  })\n\n  const { disconnect } = useDisconnect()\n\n  if (address)\n    return (\n      \u003cmain\u003e\n        Connected to: {address}\n        \u003cbr /\u003e\n        \u003cbutton onClick={() =\u003e disconnect()}\u003eDisconnect\u003c/button\u003e\n      \u003c/main\u003e\n    )\n  return \u003cbutton onClick={() =\u003e connect()}\u003eConnect Wallet\u003c/button\u003e\n}\n\nexport default Wallet\n```\n\n### Update `index.tsx`\n\nFinally, update your `src/pages/index.tsx` file and replace `\u003cGreeting /\u003e` with the following code`:\n\n```javascript\n// ...existing imports\nimport Wallet from \"../components/Wallet\"\nimport { config } from \"service/config\"\nimport { WagmiConfig } from \"wagmi\"\n\nfunction HomePage() {\n  return (\n    {/* ...existing components */}\n    {/* \u003cGreeting /\u003e */}\n    \u003cWagmiConfig config={config}\u003e\n      \u003cWallet /\u003e\n    \u003c/WagmiConfig\u003e\n    {/* ...existing components */}\n  )\n}\n```\n\n#### Testing the Changes\n\nYou should see a \"Connect Wallet\" button on your browser, similar to the screenshot below.\n\n![Alt text](assets/connect_wallet.png)\nClicking on the button should open a MetaMask popup asking for permission to connect. After connecting, you should see your wallet address on the screen.\n\n## Step 4: Prepare Minter Helper Contract and Enable Deposits\n\nIn this step, we'll prepare the minter helper contract for calls and enable ETH deposits through the frontend.\n\n### Fetch Contract ABI\n\n1. **Navigate to Etherscan**: Open the [contract page on Sepolia Etherscan](https://sepolia.etherscan.io/address/0xb44B5e756A894775FC32EDdf3314Bb1B1944dC34#code).\n\n2. **Copy Contract ABI**: Copy the Contract ABI from the \"Contract\" tab.\n   ![Alt text](assets/contract_abi.png)\n\n3. **Create `abi.json`**: Inside the `src/service` directory, create a new file named `abi.json` and paste the copied ABI.\n\n### Create the `Deposit` Component\n\nCreate a new file named `Deposit.tsx` inside the `src/components` directory and add the following code:\n\n```javascript\nimport { canisterId } from \"declarations/payment\"\nimport { useEffect, useState } from \"react\"\nimport helperAbi from \"service/abi.json\"\nimport { useQueryCall } from \"service/payment\"\nimport { parseEther } from \"viem\"\nimport { useContractWrite } from \"wagmi\"\n\ninterface DepositProps {}\n\nconst Deposit: React.FC\u003cDepositProps\u003e = ({}) =\u003e {\n  const [amount, setAmount] = useState(0)\n\n  const { data: canisterDepositAddress } = useQueryCall({\n    functionName: \"deposit_principal\"\n  })\n\n  useEffect(() =\u003e {\n    call(canisterId)\n  }, [])\n\n  const { data, isLoading, write } = useContractWrite({\n    address: \"0xb44B5e756A894775FC32EDdf3314Bb1B1944dC34\",\n    abi: helperAbi,\n    functionName: \"deposit\",\n    value: parseEther(amount.toString()),\n    args: [canisterDepositAddress]\n  })\n\n  const changeHandler = (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n    let amount = e.target.valueAsNumber\n    if (Number.isNaN(amount) || amount \u003c 0) amount = 0\n\n    setAmount(amount)\n  }\n\n  if (isLoading) {\n    return \u003cdiv\u003eLoading...\u003c/div\u003e\n  } else if (data?.hash) {\n    return \u003cdiv\u003eTransaction Hash: {data.hash}\u003c/div\u003e\n  } else {\n    return (\n      \u003cdiv\u003e\n        \u003cinput type=\"number\" value={amount} onChange={changeHandler} /\u003e\n        \u003cbutton onClick={() =\u003e write()}\u003eDeposit\u003c/button\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n\nexport default Deposit\n```\n\n#### Understanding `useContractWrite`\n\nThe `useContractWrite` hook is used to interact with Ethereum smart contracts. Here's what each parameter does:\n\n- **`address`**: The Ethereum address of the contract you want to interact with.\n- **`abi`**: The ABI (Application Binary Interface) of the contract, which is a JSON representation of the contract's methods and structures.\n- **`functionName`**: The name of the function in the contract that you want to call.\n- **`value`**: The amount of ETH to send along with the function call, converted to its smallest unit (wei) using `parseEther`.\n- **`args`**: An array of arguments that the function takes. In this case, it's the deposit address generated by the canister.\n\n### Update the `Wallet` Component\n\nAdd the `\u003cDeposit /\u003e` component to the `Wallet.tsx` file, right above the \"Disconnect\" button:\n\n```javascript\n// ...existing code\nreturn (\n  \u003cmain\u003e\n    Connected to: {address}\n    \u003cbr /\u003e\n    \u003cDeposit /\u003e\n    \u003cbr /\u003e\n    \u003cbutton onClick={() =\u003e disconnect()}\u003eDisconnect\u003c/button\u003e\n  \u003c/main\u003e\n)\n```\n\n#### Testing the Changes\n\nYou should have small amount of Sepolia ETH in your wallet. you can get some using this [faucet](https://sepoliafaucet.com/).\n\n1. **Call the Deposit Function**: Please make sure you are on the Sepolia network on the metamask then Use the new deposit input and button to initiate a deposit.\n   ![Alt text](assets/deposit_input.png)\n2. **Confirm with MetaMask**: A MetaMask popup should appear asking for confirmation to proceed with the transaction.\n   ![Alt text](assets/deposit_metamask.png)\n3. **Check the Output**: After confirming, you should see a transaction hash.\n   ![Alt text](assets/deposit_result.png)\n\n## Step 5: Wait for Transaction Confirmation\n\nIn this step, we'll implement a mechanism to wait for transaction confirmations before verifying the payment inside the canister.\n\n### Create the `Confirmation` Component\n\nCreate a new file named `Confirmation.tsx` inside the `src/components` directory and add the following code:\n\n```javascript\nimport { Hash } from \"viem\"\nimport { useWaitForTransaction } from \"wagmi\"\n\ninterface ConfirmationProps {\n  hash: Hash;\n}\n\nconst Confirmation: React.FC\u003cConfirmationProps\u003e = ({ hash }) =\u003e {\n  const { data, isError, error, isLoading } = useWaitForTransaction({\n    hash,\n    confirmations: 6 // 6 confirmations for be sure that the transaction is confirmed\n  })\n\n  if (isError \u0026\u0026 error) {\n    return \u003cdiv\u003eTransaction error {error.toString()}\u003c/div\u003e\n  } else if (isLoading) {\n    return \u003cdiv\u003eWaiting for confirmation…\u003c/div\u003e\n  } else if (data) {\n    return \u003cdiv\u003eTransaction Status: {data.status}\u003c/div\u003e\n  } else {\n    return null\n  }\n}\n\nexport default Confirmation\n```\n\n#### Understanding `useWaitForTransaction`\n\nThe `useWaitForTransaction` hook is used to wait for a specified number of confirmations for a given Ethereum transaction hash. Here's what each parameter does:\n\n- **`hash`**: The transaction hash for which you are waiting for confirmations.\n\n- **`confirmations`**: The number of confirmations to wait for before considering the transaction as confirmed. The default is 1, but in this example, we set it to 6 for added security.\n\n### Update the `Deposit` Component\n\nReplace the line that shows the transaction hash with the `Confirmation` component:\n\nChange this:\n\n```javascript\nreturn \u003cdiv\u003eTransaction Hash: {data.hash}\u003c/div\u003e\n```\n\nTo this:\n\n```javascript\nreturn \u003cConfirmation hash={data.hash} /\u003e\n```\n\n#### Testing the Changes\n\n1. **Send Another Transaction**: Initiate another deposit transaction.\n\n2. **Check the Output**: You should see the confirmation process in action. Once the specified number of confirmations is reached, the transaction status will be displayed.\n   ![Alt text](assets/confirmation.png)\n\n## Step 6: Fetching Transaction On-Chain\n\nIn this step, we'll verify the Ethereum transaction on-chain by calling the Ethereum JSON-RPC API from within the canister.\n\n### Add Dependencies\n\nAdd the following dependencies to your `Cargo.toml`:\n\n```toml\nserde = { version = \"1.0\", features = [\"derive\"] }\n```\n\n### Create the `eth_get_transaction_receipt` Function\n\n#### Understanding the Function\n\nThe function `eth_get_transaction_receipt` performs the following tasks:\n\n- **Call to EVM RPC Canister**: It initiates a call to the EVM RPC canister, utilizing the `eth_get_transaction_receipt` method to retrieve the transaction receipt for a given transaction hash. The function prepares the necessary parameters, including a list of Ethereum Sepolia network services (e.g., PublicNode, BlockPi, Ankr) to ensure reliable data retrieval.\n\n- **Handle the RPC Response**: The function processes the response from the EVM RPC canister. If the response is consistent across the selected network services, it returns the transaction `receipt` wrapped in an `Ok` result. If the results are inconsistent or if an error occurs during the RPC call, the function returns an error message wrapped in an Err result.\n\n- **Error Handling:**: It captures and returns any errors that occur during the process, such as network issues, inconsistencies in the RPC responses, or communication failures, providing detailed error messages for troubleshooting.\n\n### Add Dependency\n\nAdd the following dependency to your `Cargo.toml`:\n\n```toml\nevm-rpc-canister-types = \"1.0.0\"\n```\n\n### Modify dfx.json file\n\nAdd the follwing code snippet to your `dfx.json` file:\n\n```json\n\"evm_rpc\": {\n  \"type\": \"custom\",\n  \"candid\": \"https://github.com/internet-computer-protocol/evm-rpc-canister/releases/latest/download/evm_rpc.did\",\n  \"wasm\": \"https://github.com/internet-computer-protocol/evm-rpc-canister/releases/latest/download/evm_rpc.wasm.gz\",\n  \"remote\": {\n    \"id\": {\n      \"ic\": \"7hfb6-caaaa-aaaar-qadga-cai\"\n    }\n  },\n  \"specified_id\": \"7hfb6-caaaa-aaaar-qadga-cai\",\n  \"init_arg\": \"(record { nodesInSubnet = 28 })\"\n}\n```\n\n## Initiate the EVM RPC Canister with your canister ID\n\nUse the following types to import the structs from the `evm_rpc_canister_types` crate:\n\n```rust\n// Import the structs from the crate\nuse evm_rpc_canister_types::{\n    EthSepoliaService, GetTransactionReceiptResult, MultiGetTransactionReceiptResult, RpcServices,\n    EVM_RPC,\n};\n```\n\n## Implement the code logic\n\nHere's the code snippet for the function:\n\n```rust\n// Implementing the eth_get_transaction function\nasync fn eth_get_transaction_receipt(hash: String) -\u003e Result\u003cGetTransactionReceiptResult, String\u003e {\n    // Make the call to the EVM_RPC canister\n    let result: Result\u003c(MultiGetTransactionReceiptResult,), String\u003e = EVM_RPC\n        .eth_get_transaction_receipt(\n            RpcServices::EthSepolia(Some(vec![\n                EthSepoliaService::PublicNode,\n                EthSepoliaService::BlockPi,\n                EthSepoliaService::Ankr,\n            ])),\n            None,\n            hash,\n            10_000_000_000,\n        )\n        .await\n        .map_err(|e| format!(\"Failed to call eth_getTransactionReceipt: {:?}\", e));\n\n    match result {\n        Ok((MultiGetTransactionReceiptResult::Consistent(receipt),)) =\u003e Ok(receipt),\n        Ok((MultiGetTransactionReceiptResult::Inconsistent(error),)) =\u003e Err(format!(\n            \"EVM_RPC returned inconsistent results: {:?}\",\n            error\n        )),\n        Err(e) =\u003e Err(format!(\"Error calling EVM_RPC: {}\", e)),\n    }\n}\n```\n\nNote: Please always keep `ic_cdk::export_candid!();` at the very end of the `lib.rs` file.\n\n### Test the Function Using Candid UI\n\nFor testing the function, we'll use the Candid UI, which is a web-based interface for interacting with canisters. It's automatically generated when you deploy a canister using the DFINITY Canister SDK.\n\nAdd this function to your `lib.rs` file:\n\n```rust\n// Testing get receipt function\n#[ic_cdk::update]\nasync fn get_receipt(hash: String) -\u003e GetTransactionReceiptResult {\n    eth_get_transaction_receipt(hash).await.unwrap()\n}\n```\n\n1. **Deploy the Canister**: Deploy the updated canister using the command `yarn deploy evm_rpc \u0026\u0026 yarn deploy payment`.\n\n2. **Navigate to Candid UI**: After successful deployment, navigate to the Candid UI using the link provided in the terminal.\n   Somthing like this `http://127.0.0.1:4943/?canisterId=bd3sg-teaaa-aaaaa-qaaba-cai\u0026id=bkyz2-fmaaa-aaaaa-qaaaq-cai`\n\n3. **Test the Function**: Inside the Candid UI, you should see the `get_receipt` function. Test it by passing a transaction hash and observing the response.\n   ![Alt text](assets/receipt.png)\n\n## Step 7: On-Chain Verification of Transactions\n\nIn this step, we'll create a function to verify Ethereum transactions on-chain. This function will use the logs emitted by the smart contract to verify the transaction details.\n\n#### Event Topics\n\nThe logs' topics are based on the `ReceivedEth` event, which has the following signature:\n![Alt text](assets/logs.png)\n\n```solidity\nReceivedEth (index_topic_1 address from, uint256 value, index_topic_2 bytes32 principal)\n```\n\n- `log.topics[0]`: Event signature hash\n- `log.topics[1]`: `from` address (index_topic_1)\n- `log.topics[2]`: `principal` (index_topic_2)\n\n#### Event Data\n\n- `log.data`: `value` (uint256)\n\n### Create the `verify_transaction` Function\n\nHere's the code snippet for the function:\n\n```rust\nconst MINTER_ADDRESS: \u0026str = \"0xb44b5e756a894775fc32eddf3314bb1b1944dc34\";\n\nuse candid::Nat;\nuse b3_utils::hex_string_with_0x_to_nat;\n\n#[derive(CandidType, Deserialize)]\npub struct VerifiedTransactionDetails {\n    pub amount: Nat,\n    pub from: String,\n}\n\n#[ic_cdk::update]\nasync fn verify_transaction(hash: String) -\u003e VerifiedTransactionDetails {\n    // Get the transaction receipt\n    let receipt_result = match eth_get_transaction_receipt(hash).await {\n        Ok(receipt) =\u003e receipt,\n        Err(e) =\u003e panic!(\"Failed to get receipt: {}\", e),\n    };\n\n    // Ensure the transaction was successful\n    let receipt = match receipt_result {\n        GetTransactionReceiptResult::Ok(Some(receipt)) =\u003e receipt,\n        GetTransactionReceiptResult::Ok(None) =\u003e panic!(\"Receipt is None\"),\n        GetTransactionReceiptResult::Err(e) =\u003e {\n            panic!(\"Error on Get transaction receipt result: {:?}\", e)\n        }\n    };\n\n    // Check if the status indicates success (Nat 1)\n    let success_status = Nat::from(1u8);\n    if receipt.status != success_status {\n        panic!(\"Transaction failed\");\n    }\n\n    // Verify the 'to' address matches the minter address\n    if receipt.to != MINTER_ADDRESS {\n        panic!(\"Minter address does not match\");\n    }\n\n    let deposit_principal = canister_deposit_principal();\n\n    // Verify the principal in the logs matches the deposit principal\n    let log_principal = receipt\n        .logs\n        .iter()\n        .find(|log| log.topics.get(2).map(|topic| topic.as_str()) == Some(\u0026deposit_principal))\n        .unwrap_or_else(|| panic!(\"Principal not found in logs\"));\n\n    // Extract relevant transaction details\n    let amount = hex_string_with_0x_to_nat(\u0026log_principal.data)\n        .unwrap_or_else(|e| panic!(\"Failed to parse amount: {}\", e));\n    let from_address = receipt.from.clone();\n\n    VerifiedTransactionDetails {\n        amount,\n        from: from_address,\n    }\n}\n```\n\n#### Understanding the Function\n\nThe function `verify_transaction` performs the following tasks:\n\n- **Check Transaction Status**: It checks if the transaction was successful by comparing the `status` field to \"1\".\n\n- **Verify Address**: It verifies that the `to` address in the transaction and the `address` in the logs match the minter address.\n\n- **Verify Principal**: It verifies that the principal in the logs matches the canister's deposit principal. The principal is found in `log.topics[2]`.\n\n- **Return Transaction Details**: It returns the amount and the sender's address.\n\n### Create a Function to Return Canister Deposit Principal\n\nFor a more robust and secure way, create a new function that returns the canister's deposit principal:\n\n```rust\n#[ic_cdk::query]\nfn canister_deposit_principal() -\u003e String {\n    let subaccount = Subaccount::from(ic_cdk::id());\n\n    let bytes32 = subaccount.to_bytes32().unwrap();\n\n    vec_to_hex_string_with_0x(bytes32)\n}\n```\n\n#### Testing the Functions\n\n1. **Deploy the Canister**: Deploy the updated canister using `yarn deploy payment`.\n\n2. **Navigate to Candid UI**: After successful deployment, navigate to the Candid UI using the link provided in the terminal.\n\n3. **Test the Functions**: Inside the Candid UI, you should see the `verify_transaction` and `canister_deposit_principal` functions. Test them by passing a transaction hash and observing the response.\n   ![Alt text](assets/candid_verify.png)\n\n## Step 8: Frontend Update for On-Chain Verification\n\nIn this step, we'll update the frontend to call the `verify_transaction` function after the transaction has been confirmed on-chain.\n\n### Create the `VerifyTransaction` Component\n\n#### Understanding the Component\n\nThe `VerifyTransaction` component performs the following tasks:\n\n- **Call `verify_transaction`**: It calls the `verify_transaction` function from the canister when the `hash` prop is provided.\n\n- **Display Status**: It displays the transaction details, including the amount and the sender's address, once the transaction is confirmed on-chain.\n\nHere's the code snippet for the component:\n\n```javascript\nimport { useEffect } from \"react\"\nimport { useQueryCall } from \"service/payment\"\nimport { formatEther } from \"viem\"\n\ninterface VerifyTransactionProps {\n  hash: string;\n}\n\nconst VerifyTransaction: React.FC\u003cVerifyTransactionProps\u003e = ({ hash }) =\u003e {\n  const { loading, error, data, call } = useQueryCall({\n    functionName: \"verify_transaction\"\n  })\n\n  useEffect(() =\u003e {\n    call([hash])\n  }, [hash])\n\n  if (loading) {\n    return \u003cdiv\u003eProcessing…\u003c/div\u003e\n  } else if (error) {\n    return \u003cdiv\u003e{error.toString()}\u003c/div\u003e\n  } else if (data) {\n    return (\n      \u003cdiv\u003e\n        Transaction(\u003cb\u003e{hash}\u003c/b\u003e) with \u003cb\u003e{formatEther(data[0])}\u003c/b\u003eETH from{\" \"}\n        \u003cb\u003e{data[1]}\u003c/b\u003e is confirmed on-chain.\n      \u003c/div\u003e\n    )\n  } else {\n    return null\n  }\n}\n\nexport default VerifyTransaction\n```\n\n### Update the `Confirmation` Component\n\nReplace the line that shows the transaction status with the `VerifyTransaction` component:\n\nChange this:\n\n```javascript\nreturn \u003cdiv\u003eTransaction Status: {data.status}\u003c/div\u003e\n```\n\nTo this:\n\n```javascript\nreturn \u003cVerifyTransaction hash={data.transactionHash} /\u003e\n```\n\n#### Testing the Changes\n\n1. **Initiate a Transaction**: Initiate a deposit transaction and confirm it.\n\n2. **Check the Output**: You should see the transaction details displayed once the transaction is confirmed and procceed on-chain.\n   ![Alt text](assets/verified_onchain.png)\n\n## Step 9: Deploying to the Internet Computer Mainnet\n\nIn this step, we'll deploy our project to the Internet Computer mainnet. This involves a few key steps:\n\n### Topping Up Your Wallet with Cycles\n\nBefore deploying to the mainnet, you'll need to ensure that your wallet has enough cycles.\n\n1. **Quickstart**: Run `dfx quickstart` in your terminal and follow the process to top up your wallet.\n\n2. **Faucet Cycles**: Alternatively, you can get some free cycles from the DFINITY [cycles faucet](https://forum.dfinity.org/t/cycles-faucet-is-now-live).\n\n### Deploying the Canister\n\nRun the following command to deploy your canister to the mainnet:\n\n```bash\nyarn deploy --network=ic\n```\n\nAlternatively, you can choose to deploy only the backend to the mainnet and run the frontend locally. To deploy just the backend, use:\n\n```bash\nyarn deploy payment --network=ic\n```\n\nTo run the frontend locally, execute:\n\n```bash\nyarn dev\n```\n\nUpon successful deployment of the backend, you should see output similar to this in your terminal:\n\n![alt text](./assets/terminal.png)\n\n### Testing on Mainnet\n\n1. **Open the Frontend**: If you've deployed the frontend to the mainnet, navigate to the frontend URL provided in the terminal. If you're running the frontend locally, you can access it via `http://localhost:3000` or the URL provided in your local development server.\n\n2. **Initiate a Transaction**: Initiate a deposit transaction and confirm it.\n   ![Alt text](assets/mainnet_deposit.png)\n\n## Step 10: Integrating with ICRC Standard\n\nIn this step, we'll integrate our canister with the ckETH ICRC standard to show the balance and enable ETH withdrawal.\n\n### Adding Ledger Feature to `b3_utils`\n\nFirst, add the \"ledger\" feature to the `b3_utils` crate in your `Cargo.toml`:\n\n```toml\nb3_utils = { version = \"0.11.0\", features = [\"ledger\"] }\n```\n\n### Setting Up Ledger and Minter Constants\n\nAdd the following lines at the top of your Rust code to specify the ledger and minter canister IDs:\n\n```rust\nconst LEDGER: \u0026str = \"apia6-jaaaa-aaaar-qabma-cai\";\nconst MINTER: \u0026str = \"jzenf-aiaaa-aaaar-qaa7q-cai\";\n```\n\n### Creating the Balance Function\n\n#### Understanding the Function\n\nThe `balance` function uses the `ICRC1` trait from `b3_utils` to fetch the balance of the canister in ckETH.\n\nHere's the code snippet for the function:\n\n```rust\nuse b3_utils::ledger::{ICRCAccount, ICRC1};\nuse candid::Principal;\n\n#[ic_cdk::update]\nasync fn balance() -\u003e Nat {\n    let account = ICRCAccount::new(ic_cdk::id(), None);\n\n    ICRC1::from(LEDGER).balance_of(account).await.unwrap()\n}\n```\n\n#### Testing the ckETH Balance Function\n\n1. **Deploy to Mainnet**: Run `yarn deploy payment --network=ic` to upgrade canister.\n\n2. **Open Candid UI**: Navigate to the Candid UI and test the `balance` function. Note that the minting process might take some time.\n   ![Alt text](assets/balance.png)\n\n### Creating the Transfer Function\n\n#### Understanding the Function\n\nThe `transfer` function allows the canister to transfer a specified amount of ckETH to another account.\nThe function uses the `ICRC1` trait from `b3_utils` to transfer the specified amount of ckETH to the recipient.\n\nHere's the code snippet for the function:\n\n```rust\nuse b3_utils::ledger::{ICRC1TransferArgs, ICRC1TransferResult};\nuse std::str::FromStr;\n\n#[ic_cdk::update]\nasync fn transfer(to: String, amount: Nat) -\u003e ICRC1TransferResult {\n    let to = ICRCAccount::from_str(\u0026to).unwrap();\n\n    let transfer_args = ICRC1TransferArgs {\n        to,\n        amount,\n        from_subaccount: None,\n        fee: None,\n        memo: None,\n        created_at_time: None,\n    };\n\n    ICRC1::from(LEDGER).transfer(transfer_args).await.unwrap()\n}\n```\n\n#### Testing the Transfer Function\n\n1. **Deploy to Mainnet**: Run `yarn deploy payment --network=ic` to upgrade canister.\n\n2. **Open Candid UI**: Navigate to the Candid UI and test the `transfer` function by passing the recipient's [ICRCAccount](https://forum.dfinity.org/t/icrc-1-account-human-readable-format/14682/56) comptible format string and the amount of ckETH to transfer.\n   ![Alt text](assets/transfer.png)\n\n### Approving the Minter to Spend ckETH\n\n#### Understanding the Function\n\nThe `approve` function uses the `ICRC2` trait from `b3_utils` to approve the minter to spend your ckETH. This is a one-time action if you approve a large amount.\n\nHere's the code snippet for the function:\n\n```rust\nuse b3_utils::ledger::{ICRC2ApproveArgs, ICRC2ApproveResult, ICRC2};\n\n#[ic_cdk::update]\nasync fn approve(amount: Nat) -\u003e ICRC2ApproveResult {\n    let minter = Principal::from_text(\u0026MINTER).unwrap();\n\n    let spender = ICRCAccount::new(minter, None);\n\n    let args = ICRC2ApproveArgs {\n        amount,\n        spender,\n        created_at_time: None,\n        expected_allowance: None,\n        expires_at: None,\n        fee: None,\n        memo: None,\n        from_subaccount: None,\n    };\n\n    ICRC2::from(LEDGER).approve(args).await.unwrap()\n}\n```\n\n#### Testing the Approve Function\n\n1. **Deploy to Mainnet**: Again upgrade the canister using `yarn deploy payment --network=ic`.\n\n2. **Open Candid UI**: Navigate to the Candid UI and test the `approve` function.\n   ![Alt text](assets/approve.png)\n\n## Step 11: Creating the Withdraw Function\n\nIn this step, we'll create a `withdraw` function that allows users to withdraw ETH from the canister.\n\n### Defining Types from the Minter Canister\n\nFirst, define some types that will be used for the withdrawal operation. These types are derived from the minter canister.\n\n```rust\nuse candid::{CandidType, Deserialize};\n\n#[derive(CandidType, Deserialize)]\npub struct WithdrawalArg {\n    pub amount: Nat,\n    pub recipient: String,\n}\n\n#[derive(CandidType, Deserialize, Clone, Debug)]\npub struct RetrieveEthRequest {\n    pub block_index: Nat,\n}\n\n#[derive(CandidType, Deserialize, Debug)]\npub enum WithdrawalError {\n    AmountTooLow { min_withdrawal_amount: Nat },\n    InsufficientFunds { balance: Nat },\n    InsufficientAllowance { allowance: Nat },\n    TemporarilyUnavailable(String),\n}\n\ntype WithdrawalResult = Result\u003cRetrieveEthRequest, WithdrawalError\u003e;\n```\n\n### Creating the Withdraw Function\n\n#### Understanding the Function\n\nThe `withdraw` function uses the `InterCall` trait from `b3_utils` to make an internal canister call to the minter canister. The function takes an `amount` and a `recipient` as arguments and initiates the withdrawal process.\n\nHere's the code snippet for the function:\n\n```rust\nuse b3_utils::InterCall;\n\n#[ic_cdk::update]\nasync fn withdraw(amount: Nat, recipient: String) -\u003e WithdrawalResult {\n    let withraw = WithdrawalArg { amount, recipient };\n\n    InterCall::from(MINTER)\n        .call(\"withdraw_eth\", withraw)\n        .await\n        .unwrap()\n}\n```\n\n#### Testing the Withdraw Function\n\n1. **Deploy to Mainnet**: Run `yarn deploy payment --network=ic`.\n\n2. **Open Candid UI**: Navigate to the Candid UI and test the `withdraw` function. Make sure to enter the amount in wei.\n   ![Alt text](assets/withdraw.png)\n\n## Step 12: Adding Security and Functionalities\n\nIn this step, we'll add some security measures and functionalities to our canister.\n\n### Adding Security Measures\n\n#### Guards\n\nWe'll add guards to the `withdraw` and `approve` functions to ensure that only the controller can call them. Add the following line at the top of your Rust code:\n\n```rust\nuse b3_utils::caller_is_controller;\n```\n\nThen, add the `guard` attribute to the `withdraw` and `approve` functions:\n\n```rust\n#[ic_cdk::update(guard = \"caller_is_controller\")]\n```\n\n#### Transaction List\n\nTo prevent a transaction from being processed more than once, we'll use stable memory. Add the \"stable_memory\" feature to `b3_utils` in your `Cargo.toml`:\n\n```toml\nb3_utils = { version = \"0.11.0\", features = [\"ledger\", \"stable_memory\"] }\n```\n\nThen, add the following code to initialize stable memory:\n\n```rust\nuse b3_utils::memory::init_stable_mem_refcell;\nuse b3_utils::memory::types::DefaultStableBTreeMap;\nuse std::cell::RefCell;\n\nthread_local! {\n    static TRANSACTIONS: RefCell\u003cDefaultStableBTreeMap\u003cString, String\u003e\u003e = init_stable_mem_refcell(\"trasnactions\", 1).unwrap();\n    static ITEMS: RefCell\u003cDefaultStableBTreeMap\u003cString, u128\u003e\u003e = init_stable_mem_refcell(\"items\", 2).unwrap();\n}\n```\n\n### Adding Functionalities\n\n#### Item Management\n\nWe'll add functionalities to set items and their prices, and to get the list of items. Here are the functions:\n\n```rust\n#[ic_cdk::query]\nfn get_transaction_list() -\u003e Vec\u003c(String, String)\u003e {\n    TRANSACTIONS.with(|t| {\n        t.borrow()\n            .iter()\n            .map(|(k, v)| (k.clone(), v.clone()))\n            .collect()\n    })\n}\n\n#[ic_cdk::update(guard = \"caller_is_controller\")]\nfn set_item(item: String, price: u128) {\n    ITEMS.with(|p| p.borrow_mut().insert(item, price));\n}\n\n#[ic_cdk::query]\nfn get_items() -\u003e Vec\u003c(String, u128)\u003e {\n    ITEMS.with(|p| {\n        p.borrow()\n            .iter()\n            .map(|(k, v)| (k.clone(), v.clone()))\n            .collect()\n    })\n}\n```\n\n#### Buying Items\n\nWe'll add a function to buy items. This function will check the transaction list to ensure that the transaction has not been processed before. and check the amount to ensure that it's not too low.\n\nHere's the function:\n\n```rust\n#[ic_cdk::update]\nasync fn buy_item(item: String, hash: String) -\u003e u64 {\n    if TRANSACTIONS.with(|t| t.borrow().contains_key(\u0026hash)) {\n        panic!(\"Transaction already processed\");\n    }\n\n    let price = ITEMS.with(|p| {\n        p.borrow()\n            .get(\u0026item)\n            .unwrap_or_else(|| panic!(\"Item not found\"))\n            .clone()\n    });\n\n    let verified_details = match verify_transaction(hash.clone()).await {\n        Ok(details) =\u003e details,\n        Err(e) =\u003e panic!(\"Transaction verification failed: {}\", e),\n    };\n\n    let VerifiedTransactionDetails { amount, from } = verified_details;\n\n    if amount.parse::\u003cu128\u003e().unwrap_or(0) \u003c price {\n        panic!(\"Amount too low\");\n    }\n\n    TRANSACTIONS.with(|t| {\n        let mut t = t.borrow_mut();\n        t.insert(hash, from);\n\n        t.len() as u64\n    })\n}\n```\n\n### Testing\n\n1. **Deploy to Mainnet**: Run `yarn deploy payment --network=ic`.\n\n2. **Testing Guards**: Use the terminal to execute functions with guards. For example:\n\n   ```bash\n   dfx canister call payment withdraw '(10000000000000000, \"0xB51f94aEEebE55A3760E8169A22e536eBD3a6DCB\")' --network ic\n   ```\n\n   To add a new controller, run:\n\n   ```bash\n   dfx canister update-settings payment --add-controller 'YOUR_PRINCIPAL' --network=ic\n   ```\n\n3. **Adding Items**: Add items using the terminal:\n\n   ```bash\n   dfx canister call payment set_item '(\"Pizza\", 1000000000000)' --network ic\n   ```\n\n   Check the items inside the Candid UI using `get_items`.\n   ![Alt text](assets/get_items.png)\n\n## Step 13: Frontend Integration for Shop and Item Purchase\n\nIn this step, we'll integrate the frontend to display a shop and handle item purchases.\n\n### Shop Component\n\nCreate a new file `Shop.tsx` inside the `src/components` directory and add the following code:\n\n```jsx\nimport { useEffect } from \"react\"\nimport { useQueryCall } from \"service/payment\"\nimport Item from \"./Item\"\n\ninterface ShopProps {}\n\nconst Shop: React.FC\u003cShopProps\u003e = ({}) =\u003e {\n  const {\n    data: items,\n    loading,\n    call\n  } = useQueryCall({\n    functionName: \"get_items\"\n  })\n\n  return (\n    \u003cdiv\n      style={{\n        marginTop: 10,\n        display: \"grid\",\n        gridTemplateColumns: \"repeat(2, 1fr)\",\n        gridGap: 20\n      }}\n    \u003e\n      {loading ? (\n        \u003cdiv\u003eLoading...\u003c/div\u003e\n      ) : (\n        items?.map(([name, price]) =\u003e {\n          return \u003cItem name={name} price={price} key={name} /\u003e\n        })\n      )}\n    \u003c/div\u003e\n  )\n}\n\nexport default Shop\n```\n\nThis component fetches the list of items from the backend and displays them in a grid layout.\n\n### Item Component\n\nCreate a new file `Item.tsx` inside the `src/components` directory and add the following code:\n\n```jsx\nimport { useEffect } from \"react\"\nimport helperAbi from \"service/abi.json\"\nimport { useQueryCall } from \"service/payment\"\nimport { formatEther } from \"viem\"\nimport { useContractWrite } from \"wagmi\"\nimport Confirmation from \"./Confirmation\"\n\ninterface ItemProps {\n  name: string\n  price: bigint\n}\n\nconst Item: React.FC\u003cItemProps\u003e = ({ name, price }) =\u003e {\n  const { data: canisterDepositAddress, call } = useQueryCall(\n    functionName: \"canister_deposit_principal\"\n  )\n\n  const { data, isLoading, write } = useContractWrite({\n    address: \"0xb44B5e756A894775FC32EDdf3314Bb1B1944dC34\",\n    abi: helperAbi,\n    functionName: \"deposit\",\n    value: price,\n    args: [canisterDepositAddress]\n  })\n\n  if (isLoading) {\n    return \u003cdiv\u003eBuying {name}…\u003c/div\u003e\n  } else if (data?.hash) {\n    return \u003cConfirmation hash={data.hash} item={name} /\u003e\n  } else {\n    return (\n      \u003cdiv\u003e\n        \u003ch3\u003e{name}\u003c/h3\u003e\n        \u003cdiv\u003e{formatEther(price).toString()} ETH\u003c/div\u003e\n        \u003cbutton onClick={() =\u003e write()}\u003eBuy {name}\u003c/button\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n\nexport default Item\n```\n\nThis component handles the purchase of individual items. It uses the `canister_deposit_principal` and `deposit` methods to handle the transaction.\n\n### Confirmation Component\n\nEdit the existing `Confirmation.tsx` file to add the `item` prop:\n\n```jsx\nimport { Hash } from \"viem\"\nimport { useWaitForTransaction } from \"wagmi\"\nimport VerifyTransaction from \"./VerifyTransaction\"\n\ninterface ConfirmationProps {\n  item: string\n  hash: Hash\n}\n\nconst Confirmation: React.FC\u003cConfirmationProps\u003e = ({ item, hash }) =\u003e {\n  const { data, isError, error, isLoading } = useWaitForTransaction({\n    hash,\n    confirmations: 6\n  })\n\n  if (isError \u0026\u0026 error) {\n    return \u003cdiv\u003eTransaction error {error.toString()}\u003c/div\u003e\n  } else if (isLoading) {\n    return \u003cdiv\u003eWaiting for confirmation on Ethereum…\u003c/div\u003e\n  } else if (data) {\n    return \u003cVerifyTransaction hash={data.transactionHash} item={item} /\u003e\n  } else {\n    return null\n  }\n}\n\nexport default Confirmation\n```\n\nThis component waits for the Ethereum transaction to be confirmed and then triggers the on-chain verification on the Internet Computer.\n\n### Verify Transaction Component\n\nEdit the existing `VerifyTransaction.tsx` file to add the `item` prop and work with the new `buy_item` method:\n\n```jsx\nimport { useEffect } from \"react\"\nimport { useQueryCall } from \"service/payment\"\n\ninterface VerifyTransactionProps {\n  item: string\n  hash: string\n}\n\nconst VerifyTransaction: React.FC\u003cVerifyTransactionProps\u003e = ({\n  item,\n  hash\n}) =\u003e {\n  const { loading, error, data, call } = useUpdateCall({\n    functionName: \"buy_item\"\n  })\n\n  useEffect(() =\u003e {\n    if (hash) {\n      call([item, hash])\n    }\n  }, [hash])\n\n  if (loading) {\n    return \u003cdiv\u003eProcessing Purchase on ICP...\u003c/div\u003e\n  } else if (error) {\n    return \u003cdiv\u003e{error.toString()}\u003c/div\u003e\n  } else if (data) {\n    return (\n      \u003cdiv\u003e\n        \u003ch3\u003e{item} bought!\u003c/h3\u003e\n        \u003cdiv\u003ePurchase ID: {data.toString()}\u003c/div\u003e\n      \u003c/div\u003e\n    )\n  } else {\n    return null\n  }\n}\n\nexport default VerifyTransaction\n```\n\nThis component calls the `buy_item` method on the backend to finalize the purchase and display a purchase ID.\n\n### Update Wallet Component\n\nIn your `Wallet.tsx`, replace `\u003cDeposit /\u003e` with `\u003cShop /\u003e`.\n\n### Testing\n\n1. **Local Testing**: Run `yarn dev` to test the application locally.\n\n2. **Deploy to Mainnet**: Run `yarn deploy --network=ic` to deploy the application to the Internet Computer mainnet.\n\n3. **Live Example**: The live example can be accessed at [https://uu4vt-kqaaa-aaaap-abmia-cai.icp0.io/](https://uu4vt-kqaaa-aaaap-abmia-cai.icp0.io/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb3hr4d%2Feth_payment_tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fb3hr4d%2Feth_payment_tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fb3hr4d%2Feth_payment_tutorial/lists"}