Blockchain-Based Auto-Stake Splitter Solidity, Web3

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

// SPDX-License-Identifier: MIT

// Contract for an Auto-Stake Splitter
contract AutoStakeSplitter {

    // Owner of the contract
    address public owner;

    // Percentage allocated to staking (e.g., 80 = 80%)
    uint256 public stakingPercentage;

    // Percentage allocated to marketing (e.g., 10 = 10%)
    uint256 public marketingPercentage;

    // Percentage allocated to development (e.g., 10 = 10%)
    uint256 public developmentPercentage;

    // Contract addresses for receiving the split funds
    address payable public stakingAddress;
    address payable public marketingAddress;
    address payable public developmentAddress;

    // Event emitted when funds are split
    event FundsSplit(uint256 amount, uint256 stakingAmount, uint256 marketingAmount, uint256 developmentAmount);

    // Event emitted when the owner is changed
    event OwnerChanged(address indexed oldOwner, address indexed newOwner);

    // Event emitted when the staking address is changed
    event StakingAddressChanged(address indexed oldAddress, address indexed newAddress);

    // Event emitted when the marketing address is changed
    event MarketingAddressChanged(address indexed oldAddress, address indexed newAddress);

    // Event emitted when the development address is changed
    event DevelopmentAddressChanged(address indexed oldAddress, address indexed newAddress);


    // Modifier to restrict access to the owner
    modifier onlyOwner() {
        require(msg.sender == owner, "Only the owner can call this function.");
        _;
    }

    // Constructor: sets the owner and initial percentage splits and addresses
    constructor(
        uint256 _stakingPercentage,
        uint256 _marketingPercentage,
        uint256 _developmentPercentage,
        address payable _stakingAddress,
        address payable _marketingAddress,
        address payable _developmentAddress
    ) {
        require(_stakingPercentage + _marketingPercentage + _developmentPercentage == 100, "Percentages must add up to 100");
        require(_stakingAddress != address(0), "Staking address cannot be the zero address");
        require(_marketingAddress != address(0), "Marketing address cannot be the zero address");
        require(_developmentAddress != address(0), "Development address cannot be the zero address");

        owner = msg.sender;
        stakingPercentage = _stakingPercentage;
        marketingPercentage = _marketingPercentage;
        developmentPercentage = _developmentPercentage;
        stakingAddress = _stakingAddress;
        marketingAddress = _marketingAddress;
        developmentAddress = _developmentAddress;
    }

    // Function to receive ETH and split it according to the defined percentages
    receive() external payable {
        splitFunds();
    }

    // Fallback function to receive ETH
    fallback() external payable {
        splitFunds();
    }


    // Internal function to handle the fund splitting logic
    function splitFunds() internal {
        uint256 amount = msg.value;

        uint256 stakingAmount = (amount * stakingPercentage) / 100;
        uint256 marketingAmount = (amount * marketingPercentage) / 100;
        uint256 developmentAmount = (amount * developmentPercentage) / 100;

        // Send funds to the respective addresses
        (bool stakingSuccess, ) = stakingAddress.call{value: stakingAmount}("");
        require(stakingSuccess, "Staking transfer failed.");

        (bool marketingSuccess, ) = marketingAddress.call{value: marketingAmount}("");
        require(marketingSuccess, "Marketing transfer failed.");

        (bool developmentSuccess, ) = developmentAddress.call{value: developmentAmount}("");
        require(developmentSuccess, "Development transfer failed.");

        emit FundsSplit(amount, stakingAmount, marketingAmount, developmentAmount);
    }

    // Function to update the staking percentage.  Can only be called by the owner.
    function setStakingPercentage(uint256 _stakingPercentage) external onlyOwner {
        require(_stakingPercentage + marketingPercentage + developmentPercentage == 100, "Percentages must add up to 100");
        stakingPercentage = _stakingPercentage;
    }

    // Function to update the marketing percentage.  Can only be called by the owner.
    function setMarketingPercentage(uint256 _marketingPercentage) external onlyOwner {
        require(stakingPercentage + _marketingPercentage + developmentPercentage == 100, "Percentages must add up to 100");
        marketingPercentage = _marketingPercentage;
    }

    // Function to update the development percentage.  Can only be called by the owner.
    function setDevelopmentPercentage(uint256 _developmentPercentage) external onlyOwner {
        require(stakingPercentage + marketingPercentage + _developmentPercentage == 100, "Percentages must add up to 100");
        developmentPercentage = _developmentPercentage;
    }

    // Function to update the staking address. Can only be called by the owner.
    function setStakingAddress(address payable _stakingAddress) external onlyOwner {
        require(_stakingAddress != address(0), "Staking address cannot be the zero address");
        emit StakingAddressChanged(stakingAddress, _stakingAddress);
        stakingAddress = _stakingAddress;
    }

    // Function to update the marketing address. Can only be called by the owner.
    function setMarketingAddress(address payable _marketingAddress) external onlyOwner {
        require(_marketingAddress != address(0), "Marketing address cannot be the zero address");
        emit MarketingAddressChanged(marketingAddress, _marketingAddress);
        marketingAddress = _marketingAddress;
    }

    // Function to update the development address. Can only be called by the owner.
    function setDevelopmentAddress(address payable _developmentAddress) external onlyOwner {
        require(_developmentAddress != address(0), "Development address cannot be the zero address");
        emit DevelopmentAddressChanged(developmentAddress, _developmentAddress);
        developmentAddress = _developmentAddress;
    }


    // Function to change the owner of the contract.  Can only be called by the current owner.
    function changeOwner(address _newOwner) external onlyOwner {
        require(_newOwner != address(0), "New owner cannot be the zero address");
        emit OwnerChanged(owner, _newOwner);
        owner = _newOwner;
    }

    // Function to withdraw any accidentally sent tokens
    function withdrawEther(address payable _to, uint256 _amount) external onlyOwner {
        require(_to != address(0), "Cannot send to the zero address");
        require(address(this).balance >= _amount, "Insufficient balance");
        (bool success, ) = _to.call{value: _amount}("");
        require(success, "Withdrawal failed");
    }


    //Function to see the contracts balance
    function getContractBalance() public view returns (uint256) {
        return address(this).balance;
    }
}
```

**Explanation:**

1.  **`pragma solidity ^0.8.0;`**:  Specifies the Solidity compiler version to be used.  `^0.8.0` means version 0.8.0 or higher, but less than 0.9.0.
2.  **`// SPDX-License-Identifier: MIT`**:  Specifies the license under which the code is released.  This is a best practice.
3.  **`contract AutoStakeSplitter { ... }`**:  Defines the Solidity contract named `AutoStakeSplitter`.

4.  **State Variables:**
    *   `address public owner;`: Stores the address of the contract owner. Only the owner can execute specific administrative functions. The `public` keyword automatically generates a getter function to retrieve the owner's address.
    *   `uint256 public stakingPercentage;`: Stores the percentage of funds to be allocated to staking.
    *   `uint256 public marketingPercentage;`: Stores the percentage of funds to be allocated to marketing.
    *   `uint256 public developmentPercentage;`: Stores the percentage of funds to be allocated to development.
    *   `address payable public stakingAddress;`: Stores the Ethereum address where staking funds will be sent. `payable` means this address can receive Ether.
    *   `address payable public marketingAddress;`: Stores the Ethereum address where marketing funds will be sent.
    *   `address payable public developmentAddress;`: Stores the Ethereum address where development funds will be sent.

5.  **Events:**
    *   `event FundsSplit(uint256 amount, uint256 stakingAmount, uint256 marketingAmount, uint256 developmentAmount);`: An event that is emitted whenever funds are split and sent to the respective addresses.  Events are a way for external applications (like web3 frontends) to monitor the contract's activity.
    *   `event OwnerChanged(address indexed oldOwner, address indexed newOwner);`: An event emitted when the contract owner is changed.
    *   `event StakingAddressChanged(address indexed oldAddress, address indexed newAddress);`: An event emitted when the staking address is changed.
    *   `event MarketingAddressChanged(address indexed oldAddress, address indexed newAddress);`: An event emitted when the marketing address is changed.
    *   `event DevelopmentAddressChanged(address indexed oldAddress, address indexed newAddress);`: An event emitted when the development address is changed.

6.  **`modifier onlyOwner() { ... }`**: Defines a modifier. Modifiers are used to restrict access to certain functions.  In this case, the `onlyOwner` modifier ensures that only the contract owner can call functions using this modifier.  `require(msg.sender == owner, "Only the owner can call this function.");` checks if the caller (`msg.sender`) is the owner.  If not, it throws an error. `_;` is a special symbol that tells Solidity to execute the rest of the function where the modifier is used.

7.  **`constructor(...) { ... }`**: The constructor is a special function that is executed only once, when the contract is deployed.
    *   It takes the staking, marketing, and development percentages and addresses as arguments.
    *   `require(_stakingPercentage + _marketingPercentage + _developmentPercentage == 100, "Percentages must add up to 100");`:  Ensures that the percentages add up to exactly 100.
    *   `require(_stakingAddress != address(0), "Staking address cannot be the zero address");`: Makes sure that the staking address isn't the zero address (0x0), which would likely lead to lost funds.  Similar checks are done for the marketing and development addresses.
    *   It initializes the state variables (owner, percentages, and addresses).

8.  **`receive() external payable { ... }` and `fallback() external payable { ... }`**: These are special functions that are called when the contract receives Ether.
    *   `receive()` is called when Ether is sent to the contract without any data (a plain Ether transfer).
    *   `fallback()` is called when Ether is sent to the contract with data, but no function matching that data is found.
    *   Both of them call the `splitFunds()` function to handle the fund splitting logic. `payable` means the function can receive Ether. `external` means it can only be called from outside the contract.

9.  **`function splitFunds() internal { ... }`**:  This is the core logic of the contract.
    *   `uint256 amount = msg.value;`: Gets the amount of Ether sent to the contract. `msg.value` contains the Ether sent with the transaction.
    *   `uint256 stakingAmount = (amount * stakingPercentage) / 100;`: Calculates the amount to be sent to the staking address based on the defined percentage.
    *   Similar calculations are done for the marketing and development amounts.
    *   `(bool stakingSuccess, ) = stakingAddress.call{value: stakingAmount}("");`: Sends the calculated amount to the staking address using the `call` function. `call` is a low-level function for sending Ether and data to another address.  `{value: stakingAmount}` specifies the amount of Ether to send. The `""` is for data, which is empty in this case.
    *   `require(stakingSuccess, "Staking transfer failed.");`: Checks if the transfer to the staking address was successful. If not, it reverts the transaction to prevent inconsistent state. Similar checks are performed for marketing and development transfers.
    *   `emit FundsSplit(amount, stakingAmount, marketingAmount, developmentAmount);`: Emits the `FundsSplit` event to notify listeners about the fund distribution.

10. **`setStakingPercentage(uint256 _stakingPercentage) external onlyOwner { ... }`**: A function to update the staking percentage.  Can only be called by the owner.  It checks that the new percentages add up to 100. Similar `set` functions are provided for marketing and development percentages and the recipient addresses.

11. **`changeOwner(address _newOwner) external onlyOwner { ... }`**: A function to change the contract owner.  Only the current owner can call this function.  It emits the `OwnerChanged` event.

12. **`withdrawEther(address payable _to, uint256 _amount) external onlyOwner { ... }`**:  A function to withdraw Ether that might have been accidentally sent to the contract.  Only the owner can call this. It checks there is enough balance in the contract and the transfer succeeded.

13. **`getContractBalance() public view returns (uint256) { ... }`**:  A function to get the current balance of the contract.  `public view` means anyone can call it and it doesn't modify the contract's state. `returns (uint256)` specifies the return type.

**How to Use (with Web3.js):**

1.  **Deploy the Contract:**
    *   Compile the Solidity code using a Solidity compiler (e.g., Remix, Hardhat, or Truffle).
    *   Deploy the compiled contract to a blockchain network (e.g., Ethereum mainnet, Goerli, Sepolia, or a local development network like Ganache).  You will need a wallet with some Ether to pay for the gas costs.
    *   Note the contract address after deployment.

2.  **Interact with the Contract using Web3.js:**

```javascript
// Assuming you have web3.js initialized and connected to a provider
// (e.g., MetaMask, Infura, or a local node)

// Replace with your contract address
const contractAddress = "0xYourContractAddress";

// Replace with the ABI of your contract (copy from the Solidity compiler output)
const contractABI = [
  // Your contract ABI here (a JSON array)
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "_stakingPercentage",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "_marketingPercentage",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "_developmentPercentage",
        "type": "uint256"
      },
      {
        "internalType": "address payable",
        "name": "_stakingAddress",
        "type": "address"
      },
      {
        "internalType": "address payable",
        "name": "_marketingAddress",
        "type": "address"
      },
      {
        "internalType": "address payable",
        "name": "_developmentAddress",
        "type": "address"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "stakingAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "marketingAmount",
        "type": "uint256"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "developmentAmount",
        "type": "uint256"
      }
    ],
    "name": "FundsSplit",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "oldOwner",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "newOwner",
        "type": "address"
      }
    ],
    "name": "OwnerChanged",
    "type": "event"
  },
  {
    "inputs": [],
    "name": "getContractBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "owner",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "stateMutability": "payable",
    "type": "receive"
  }
]; // Get this from your compiler

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

// Example 1: Sending Ether to the contract
async function sendEther(amountInEther) {
  try {
    const accounts = await web3.eth.getAccounts();
    const amountInWei = web3.utils.toWei(amountInEther, "ether"); // Convert Ether to Wei
    await web3.eth.sendTransaction({
      from: accounts[0], // Use the first account in your wallet
      to: contractAddress,
      value: amountInWei,
    });
    console.log("Ether sent successfully!");
  } catch (error) {
    console.error("Error sending Ether:", error);
  }
}

// Example usage:
// sendEther("0.1"); // Send 0.1 Ether to the contract

// Example 2: Getting the contract balance
async function getBalance() {
  try {
    const balance = await autoStakeSplitter.methods.getContractBalance().call();
    const balanceInEther = web3.utils.fromWei(balance, "ether");
    console.log("Contract balance:", balanceInEther, "Ether");
  } catch (error) {
    console.error("Error getting balance:", error);
  }
}

// Example usage:
// getBalance();

// Example 3:  Listening for the FundsSplit event
autoStakeSplitter.events.FundsSplit({
  fromBlock: "latest" // Only listen for events from the latest block onwards
}).on('data', event => {
  console.log("FundsSplit Event:", event.returnValues);
}).on('error', console.error);
```

**Important Considerations:**

*   **Security:**  This is a basic example.  In a real-world scenario, thorough security audits are crucial to prevent vulnerabilities such as reentrancy attacks, integer overflows, and front-running. Consider using security best practices like Checks-Effects-Interactions pattern.
*   **Gas Optimization:**  Solidity code can be optimized to reduce gas costs.  Techniques include minimizing state variable reads and writes, using efficient data structures, and avoiding unnecessary loops.
*   **Error Handling:**  Implement robust error handling to gracefully manage unexpected situations and provide informative error messages.  Use `try...catch` blocks in your JavaScript code when interacting with the contract.
*   **Testing:**  Write comprehensive unit and integration tests to ensure the contract functions correctly under various conditions.  Tools like Hardhat and Truffle make testing easier.
*   **Frontend:**  Build a user-friendly frontend using libraries like React, Vue.js, or Angular to allow users to easily interact with the contract. Use libraries like Web3.js or Ethers.js.
*   **Immutability:** Once the contract is deployed, it cannot be easily changed. Consider building an upgradable contract using proxy patterns if there is a likelyhood that the contract might need to be upgraded in the future.

This comprehensive example provides a foundation for building a blockchain-based auto-stake splitter. Remember to adapt and enhance the code based on your specific requirements and security considerations.
👁️ Viewed: 10

Comments