Future

Arturas-Alfredas Lapinskas
Arturas-Alfredas Lapinskas

Posted on

A Database‑Free Web3 Store: MetaMask Authentication and On‑Chain Product Management

I wrote and deployed a demo Web3 store with Solidity and ethers.js.
Users can log in using MetaMask and make purchases.
The store runs without a database; all transaction data is stored on‑chain.

metamask transaction

metamask transaction

The store was created for learning about solidity and working with smart contracts.

Unfortunately, the book I bought few years ago and the tools it recommends (web3.js, Truffle, Ganache) are now obsolete and no longer maintained, so I had to figure everything out on my own.

First, we needed a tool for building and deploying smart contracts. Modern options are Forge and Hardhat; I chose Forge.

A testnet is required for development. Both Hardhat and Forge provide built‑in local testnets; I used Anvil, Forge’s fast, in‑memory Ethereum node.

Anvil is started from the console. It automatically creates funded accounts and prints the RPC endpoint (e.g., http://127.0.0.1:8545) that MetaMask can connect to.
Anvil output
Connect the MetaMask extension to the RPC URL shown by Anvil, then deploy the compiled contract to that network.

For the contract, we need two functions:

1.Purchase an item. The purchase will transfer funds to the store wallet and record the ID of the purchased item for the buyer.

function purchaseItem(uint8 _itemId) external payable {
    uint256 itemCost = 2 gwei; // 0.0012 USD / 0.18 RUB;

    // Forward the funds to the seller
    (bool success, ) = STORE_ADDRESS.call{value: itemCost}("");
    require(success, "Transfer failed");

    boughtItems[msg.sender].push(_itemId);
}
Enter fullscreen mode Exit fullscreen mode

2.View purchased items.

function getBoughtItems() external view returns (uint8[] memory) {
    return boughtItems[msg.sender];
}
Enter fullscreen mode Exit fullscreen mode

I won't provide tests here. After successful testing and compilation, publish the contract:

forge create ./src/Store.sol:Store \
    --private-key <PRIVATE_KEY> \
    --rpc-url <RPC_URL> \
    --broadcast \
    --constructor-args <STORE_PUBLIC_KEY>
Enter fullscreen mode Exit fullscreen mode

After successful publication, we are given the smart contract address.

smart contract address

Next, we'll write the store application. We'll need a library to interact with MetaMask and the blockchain. I used Ethers v6, as I found it to be the most well-documented.
First, connect to the Ethereum wallet through the MetaMask extension.

const provider = new ethers.BrowserProvider(window.ethereum); 
Enter fullscreen mode Exit fullscreen mode

Query the balance to ensure it's working correctly.

const signerObj = await provider.getSigner();
const balance = await provider.getBalance(signerObj.address);        
const balanceFormatted = ethers.formatEther(balance);
Enter fullscreen mode Exit fullscreen mode

Next, interact with the smart contract.
1.Import abi – a json from the solidity build result.

import ContractArtifact from "***/Store.json";
const abi = ContractArtifact.abi;
Enter fullscreen mode Exit fullscreen mode

2.Create an Contract instance and pass it the smart contract address we received after publish.

const contract = new ethers.Contract(CONTRACT_ADDRESS, abi, signerObj);
Enter fullscreen mode Exit fullscreen mode

3.That's it, you can call the smart contract's functions.

contract.purchaseItem(itemId, {
    value: ethers.parseUnits("2", "gwei")   // <-- attach the wei
}).then((tx) => {
    return tx.wait();
}).then(() => {
    const newItems = items.add(itemId);
    setItems(newItems);
});

const userItems = await contract.getBoughtItems();
Enter fullscreen mode Exit fullscreen mode

Once the work is complete, you can deploy the store and contract to the live network.

Thanks for reading, any feedback or suggestions are appreciated.

Project link

Top comments (0)