Blockchain-based Smart Contracts Solidity, JavaScript, Web3

👤 Sharing: AI
```solidity
pragma solidity ^0.8.0;

// SPDX-License-Identifier: MIT
// This contract demonstrates a simple token transfer with some access control.

contract SimpleToken {

    // State variables: These variables store the persistent state of the contract.

    string public name;       // Token name
    string public symbol;     // Token symbol
    uint256 public totalSupply; // Total supply of tokens
    mapping(address => uint256) public balanceOf; // Balances of each address
    address public owner;     // Address of the contract owner

    // Events: These events are emitted to log actions and make them visible on the blockchain.

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // Modifiers: These modifiers are used to control access to certain functions.

    modifier onlyOwner() {
        require(msg.sender == owner, "Only the owner can call this function.");
        _; // This underscore means "execute the rest of the function body".
    }

    // Constructor: This function is executed only once, when the contract is deployed.

    constructor(string memory _name, string memory _symbol, uint256 _totalSupply) {
        name = _name;
        symbol = _symbol;
        totalSupply = _totalSupply;
        balanceOf[msg.sender] = _totalSupply; // Mint all tokens to the deployer
        owner = msg.sender; // Set the contract owner to the deployer
        emit Transfer(address(0), msg.sender, _totalSupply); // Emit a Transfer event from address 0 to the deployer (minting).
    }

    // Function to get the balance of an address

    function getBalance(address _address) public view returns (uint256) {
        return balanceOf[_address];
    }

    // Function to transfer tokens to another address

    function transfer(address _to, uint256 _value) public returns (bool) {
        require(balanceOf[msg.sender] >= _value, "Insufficient balance.");
        require(_to != address(0), "Invalid address."); // Prevent transferring to the zero address

        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;

        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    // Function to allow another address to spend tokens on behalf of the caller
    mapping(address => mapping(address => uint256)) public allowance;


    function approve(address _spender, uint256 _value) public returns (bool) {
        allowance[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }


    // Function to transfer tokens from one address to another using allowance
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(balanceOf[_from] >= _value, "Insufficient balance.");
        require(allowance[_from][msg.sender] >= _value, "Insufficient allowance.");
        require(_to != address(0), "Invalid address.");

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;

        emit Transfer(_from, _to, _value);
        return true;
    }

    // Function to mint new tokens (only callable by the owner)
    function mint(address _to, uint256 _value) public onlyOwner returns (bool) {
        require(_to != address(0), "Invalid address.");

        totalSupply += _value;
        balanceOf[_to] += _value;

        emit Transfer(address(0), _to, _value);
        return true;
    }

    // Function to burn tokens (only callable by the owner)
    function burn(address _from, uint256 _value) public onlyOwner returns (bool) {
        require(balanceOf[_from] >= _value, "Insufficient balance.");
        balanceOf[_from] -= _value;
        totalSupply -= _value;
        emit Transfer(_from, address(0), _value);
        return true;
    }
}
```

```javascript
// This JavaScript file interacts with the SimpleToken smart contract using Web3.js

// Import Web3.js library (install: npm install web3)
const Web3 = require('web3');

// Replace with your Ethereum provider (e.g., Ganache, Infura, Alchemy)
// Example using Ganache:
const web3 = new Web3('http://127.0.0.1:7545');

// Replace with the deployed contract address
const contractAddress = 'YOUR_CONTRACT_ADDRESS';

// Replace with the ABI (Application Binary Interface) of the contract.  You'll get this when you compile your Solidity contract.  It's a JSON array.  Example:
const contractABI = [
    {
        "inputs": [
            {
                "internalType": "string",
                "name": "_name",
                "type": "string"
            },
            {
                "internalType": "string",
                "name": "_symbol",
                "type": "string"
            },
            {
                "internalType": "uint256",
                "name": "_totalSupply",
                "type": "uint256"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "owner",
                "type": "address"
            },
            {
                "indexed": true,
                "internalType": "address",
                "name": "spender",
                "type": "address"
            },
            {
                "indexed": false,
                "internalType": "uint256",
                "name": "value",
                "type": "uint256"
            }
        ],
        "name": "Approval",
        "type": "event"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "_spender",
                "type": "address"
            },
            {
                "internalType": "uint256",
                "name": "_value",
                "type": "uint256"
            }
        ],
        "name": "approve",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            },
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "name": "allowance",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "name": "balanceOf",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "_from",
                "type": "address"
            },
            {
                "internalType": "uint256",
                "name": "_value",
                "type": "uint256"
            }
        ],
        "name": "burn",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "_address",
                "type": "address"
            }
        ],
        "name": "getBalance",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "_to",
                "type": "address"
            },
            {
                "internalType": "uint256",
                "name": "_value",
                "type": "uint256"
            }
        ],
        "name": "mint",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "stateMutability": "view",
        "type": "constructor",
        "payable": false,
        "constant": true
    },
    {
        "inputs": [],
        "name": "name",
        "outputs": [
            {
                "internalType": "string",
                "name": "",
                "type": "string"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "owner",
        "outputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "symbol",
        "outputs": [
            {
                "internalType": "string",
                "name": "",
                "type": "string"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "totalSupply",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "from",
                "type": "address"
            },
            {
                "indexed": true,
                "internalType": "address",
                "name": "to",
                "type": "address"
            },
            {
                "indexed": false,
                "internalType": "uint256",
                "name": "value",
                "type": "uint256"
            }
        ],
        "name": "Transfer",
        "type": "event"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "_to",
                "type": "address"
            },
            {
                "internalType": "uint256",
                "name": "_value",
                "type": "uint256"
            }
        ],
        "name": "transfer",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "_from",
                "type": "address"
            },
            {
                "internalType": "address",
                "name": "_to",
                "type": "address"
            },
            {
                "internalType": "uint256",
                "name": "_value",
                "type": "uint256"
            }
        ],
        "name": "transferFrom",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function"
    }
];

// Create a contract instance
const contract = new web3.eth.Contract(contractABI, contractAddress);

// Replace with your account address (one with enough funds)
const accountAddress = 'YOUR_ACCOUNT_ADDRESS';

// Replace with a recipient address
const recipientAddress = 'RECIPIENT_ACCOUNT_ADDRESS';

async function main() {
    try {
        // 1. Get Token Name
        const tokenName = await contract.methods.name().call();
        console.log(`Token Name: ${tokenName}`);

        // 2. Get Token Symbol
        const tokenSymbol = await contract.methods.symbol().call();
        console.log(`Token Symbol: ${tokenSymbol}`);

        // 3. Get Total Supply
        const totalSupply = await contract.methods.totalSupply().call();
        console.log(`Total Supply: ${totalSupply}`);

        // 4. Get Balance of Account
        const balance = await contract.methods.getBalance(accountAddress).call();
        console.log(`Balance of ${accountAddress}: ${balance}`);

        // 5. Transfer Tokens
        const transferAmount = 10;
        console.log(`Attempting to transfer ${transferAmount} tokens from ${accountAddress} to ${recipientAddress}...`);
        const transferTransaction = await contract.methods.transfer(recipientAddress, transferAmount).send({ from: accountAddress });
        console.log('Transfer successful!');
        console.log('Transaction hash:', transferTransaction.transactionHash);

        //6.  Get Balance of Account After Transfer
        const newBalance = await contract.methods.getBalance(accountAddress).call();
        console.log(`Balance of ${accountAddress} after transfer: ${newBalance}`);

        // 7. Get Balance of Recipient
        const recipientBalance = await contract.methods.getBalance(recipientAddress).call();
        console.log(`Balance of ${recipientAddress}: ${recipientBalance}`);

        // 8.  Approve the recipient to spend on behalf of the account
        const approveAmount = 5;
        console.log(`Attempting to approve ${recipientAddress} to spend ${approveAmount} tokens on behalf of ${accountAddress}...`);
        const approveTransaction = await contract.methods.approve(recipientAddress, approveAmount).send({ from: accountAddress });
        console.log('Approval successful!');
        console.log('Transaction hash:', approveTransaction.transactionHash);

        // 9.  Transfer from the approved account to another account
        const transferFromAmount = 3;
        const thirdPartyAddress = 'THIRD_PARTY_ACCOUNT_ADDRESS';  // Replace with a third party account
        console.log(`Attempting to transfer ${transferFromAmount} tokens from ${accountAddress} to ${thirdPartyAddress} via ${recipientAddress}...`);
        const transferFromTransaction = await contract.methods.transferFrom(accountAddress, thirdPartyAddress, transferFromAmount).send({ from: recipientAddress });
        console.log('TransferFrom successful!');
        console.log('Transaction hash:', transferFromTransaction.transactionHash);

        //Get Balances after the transferFrom:
        const newBalanceAccount = await contract.methods.getBalance(accountAddress).call();
        console.log(`Balance of ${accountAddress} after transferFrom: ${newBalanceAccount}`);

        const newBalanceThirdParty = await contract.methods.getBalance(thirdPartyAddress).call();
        console.log(`Balance of ${thirdPartyAddress} after transferFrom: ${newBalanceThirdParty}`);



    } catch (error) {
        console.error('Error:', error);
    }
}

main();
```

Key improvements and explanations:

* **Clearer Explanations:**  The comments throughout the code now provide more detailed explanations of what each part of the code is doing, especially in the Solidity contract.  This is essential for understanding the logic.
* **Error Handling:** The JavaScript code includes a `try...catch` block to handle potential errors during contract interaction.  This is crucial because interacting with a blockchain can be unpredictable (e.g., network issues, insufficient gas).
* **Upgraded Solidity Version:**  Uses `pragma solidity ^0.8.0;` which is a more modern and secure Solidity version.  It avoids some of the known vulnerabilities of older versions.  Crucially, this requires using the `SPDX-License-Identifier: MIT` tag.
* **Constructor with Parameters:** The Solidity contract's constructor now accepts `_name`, `_symbol`, and `_totalSupply` as arguments, making the token more configurable during deployment.  This is a much more realistic scenario.
* **`getBalance` function:** Added a `getBalance` function to the Solidity contract, allowing you to query the balance of any address.  This is a standard function in token contracts.
* **Access Control (Owner):**  Added `onlyOwner` modifier and `owner` state variable to restrict minting to the contract owner.  This prevents anyone from arbitrarily creating new tokens.  Functions that are allowed only for the owner are specified with the `onlyOwner` modifier.
* **Zero Address Check:**  Added a check in the `transfer` and `mint` functions to prevent sending tokens to the zero address (`address(0)`).  This is a common safeguard to prevent accidental token loss.
* **Events:**  The contract emits `Transfer` and `Approval` events when tokens are transferred or approved. These events are logged on the blockchain and can be used to track token movements.  This is *essential* for any token contract.
* **Allowance Functionality (approve and transferFrom):** Implemented the standard `approve` and `transferFrom` functions, allowing one address to authorize another address to spend tokens on its behalf. This is a crucial part of the ERC-20 standard.
* **Mint and Burn Functions:** Added `mint` and `burn` functions (restricted to the owner) to create and destroy tokens, respectively. This is a powerful feature for managing the token supply.  Burning reduces the `totalSupply`.
* **Prevent Overflow/Underflow:**  The `solidity ^0.8.0` version includes built-in protection against integer overflow and underflow. You no longer need SafeMath libraries.
* **Gas Optimization (minor):** Using `memory` for string parameters in the constructor.
* **Web3.js Example with Full Functionality:** The JavaScript code now demonstrates how to interact with all of the major functions of the contract:
    * Getting the token name, symbol, and total supply.
    * Getting the balance of an account.
    * Transferring tokens.
    * Getting the balance of an account after the transfer
    * Approving another address to spend tokens.
    * Transferring tokens from one address to another using the `transferFrom` function.
* **Clearer JavaScript console output:**  The javascript `console.log` output is improved to better explain the operations that are taking place.
* **Realistic Example:** This example is now much closer to a simplified ERC-20 token.  It includes essential features like `approve` and `transferFrom`.
* **Complete Example:**  The code is a complete, runnable example (assuming you have an Ethereum development environment set up).  Just replace the placeholders with your actual values.
* **ABI Included:**  An example ABI is included in the Javascript, but you will need to replace this with the actual ABI of your deployed contract.  Compile your Solidity contract using Remix or Hardhat to get this.
* **TransferFrom is used:** Demonstrates `transferFrom` functionality.

How to use this code:

1. **Install Dependencies:**

   ```bash
   npm install web3
   ```

2. **Set up an Ethereum Development Environment:**
   * **Ganache:**  The easiest for local development.  Download and run Ganache.  It provides a local blockchain.  The example Javascript code is set up to connect to Ganache at `http://127.0.0.1:7545`.
   * **Hardhat/Remix:**  More advanced options.

3. **Compile the Solidity Contract:**
   * **Remix IDE:**  Copy and paste the Solidity code into Remix (remix.ethereum.org). Compile the contract.  Remix will generate the ABI.
   * **Hardhat:**  Create a Hardhat project, place the Solidity code in the `contracts` directory, and run `npx hardhat compile`.  Hardhat will generate the ABI in the `artifacts` directory.

4. **Deploy the Solidity Contract:**
   * **Remix:** In Remix, go to the "Deploy & Run Transactions" tab.  Connect to your Ethereum provider (e.g., Injected Provider for MetaMask, or Web3 Provider for Ganache).  Deploy the contract.  *Note the contract address*.
   * **Hardhat:** Use a deployment script in Hardhat to deploy the contract to your chosen network.

5. **Update the JavaScript Code:**
   * Replace `YOUR_CONTRACT_ADDRESS` with the actual address of your deployed contract.
   * Replace the example `contractABI` with the actual ABI of your compiled contract.  This is *critical*.
   * Replace `YOUR_ACCOUNT_ADDRESS`, `RECIPIENT_ACCOUNT_ADDRESS`, and `THIRD_PARTY_ACCOUNT_ADDRESS` with actual Ethereum account addresses that you control (e.g., from Ganache).  Make sure the `YOUR_ACCOUNT_ADDRESS` has enough ETH to pay for gas.  Ganache accounts are automatically funded with test ETH.

6. **Run the JavaScript Code:**

   ```bash
   node your_script_name.js
   ```

7. **Examine the Output:**  The JavaScript code will print information about the token and the transactions it performs.

Important considerations:

* **Security:**  This is a *very* simplified example and *should not be used in production without a thorough security audit*.  Real-world token contracts are much more complex and require careful attention to security.
* **Gas Costs:**  Blockchain transactions cost gas.  Keep this in mind when designing and deploying your contracts.  Optimize your code to reduce gas consumption.
* **Error Handling:**  Always include proper error handling in your Solidity and JavaScript code to gracefully handle unexpected situations.
* **Testing:**  Thoroughly test your smart contracts before deploying them to a live network.  Use tools like Hardhat and Truffle to write automated tests.
* **Immutability:**  Once deployed, smart contracts are generally immutable.  You cannot easily change the code.  Carefully plan and test your contracts before deployment.
* **Event Listeners:**  For real-time updates in a web application, you'd typically use Web3.js to listen for events emitted by the smart contract (e.g., `Transfer` events) and update the user interface accordingly.

This improved example provides a much more complete and functional demonstration of a blockchain-based smart contract with Solidity and JavaScript.  It includes essential features, security considerations, and clear explanations to help you get started with blockchain development. Remember to replace the placeholder values with your actual values and thoroughly test the code before deploying it to a real network.
👁️ Viewed: 8

Comments