快速实现一个解决分配不均等问题的智能合约

  • 木西
  • 发布于 2天前
  • 阅读 37

前言本文编写的分账合约,主要为了解决现实生活中的分配不均等相关的信任问题。分账合约定义:一种智能合约,用于将收到的款项自动分配给多个预设的地址。这种合约在多种场景下非常有用,例如在多个投资者、团队成员或合作伙伴之间公平分配收益。分账合约可以确保资金的透明分配,减少信任问题,并自动化资金管理

前言

本文编写的分账合约,主要为了解决现实生活中的分配不均等相关的信任问题。

分账合约

定义:一种智能合约,用于将收到的款项自动分配给多个预设的地址。这种合约在多种场景下非常有用,例如在多个投资者、团队成员或合作伙伴之间公平分配收益。分账合约可以确保资金的透明分配,减少信任问题,并自动化资金管理。

功能

  1. 自动分配:当合约收到资金时,自动按照预设的比例将资金分配给多个地址。
  2. 透明性:所有分配记录都在区块链上公开,确保透明度。
  3. 灵活性:可以设置不同的分配比例,满足不同的需求。
  4. 安全性:通过智能合约的代码确保资金分配的正确性和安全性。

    场景

  5. 多签名钱包:多个团队成员共同管理资金,每次收到资金时自动分配。
  6. 投资组合:多个投资者共同投资一个项目,收益按比例分配。
  7. 合作伙伴关系:多个合作伙伴共同运营一个项目,收入按协议分配。
  8. 众筹项目:将众筹资金按比例分配给多个受益者。

合约开发

合约说明:本合约主要包含一下功能:查看受益人地址、份额、应得受益、已领取的受益、合约总份额、合约支出金额、账号当下应该领取的金额,领取收益方法;

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import "hardhat/console.sol";
/**
 * 分账合约 
 * @dev 这个合约会把收到的ETH按事先定好的份额分给几个账户。收到ETH会存在分账合约中,需要每个受益人调用release()函数来领取。
 */
contract PaymentSplit{
    // 事件
    event PayeeAdded(address account, uint256 shares); // 增加受益人事件
    event PaymentReleased(address to, uint256 amount); // 受益人提款事件
    event PaymentReceived(address from, uint256 amount); // 合约收款事件

    uint256 public totalShares; // 总份额
    uint256 public totalReleased; // 总支付

    mapping(address => uint256) public shares; // 每个受益人的份额
    mapping(address => uint256) public released; // 支付给每个受益人的金额
    address[] public payees; // 受益人数组

    /**
     * @dev 初始化受益人数组_payees和分账份额数组_shares
     * 数组长度不能为0,两个数组长度要相等。_shares中元素要大于0,_payees中地址不能为0地址且不能有重复地址
     */
    constructor(address[] memory _payees, uint256[] memory _shares) payable {
        // 检查_payees和_shares数组长度相同,且不为0
        require(_payees.length == _shares.length, "PaymentSplitter: payees and shares length mismatch");
        require(_payees.length > 0, "PaymentSplitter: no payees");
        // 调用_addPayee,更新受益人地址payees、受益人份额shares和总份额totalShares
        for (uint256 i = 0; i < _payees.length; i++) {
            _addPayee(_payees[i], _shares[i]);
        }
    }

    /**
     * @dev 回调函数,收到ETH释放PaymentReceived事件
     */
    receive() external payable virtual {
        emit PaymentReceived(msg.sender, msg.value);
    }

    /**
     * @dev 为有效受益人地址_account分帐,相应的ETH直接发送到受益人地址。任何人都可以触发这个函数,但钱会打给account地址。
     * 调用了releasable()函数。
     */
    function release(address payable _account) public virtual {
        // account必须是有效受益人
        require(shares[_account] > 0, "PaymentSplitter: account has no shares");
        // 计算account应得的eth
        uint256 payment = releasable(_account);
        // 应得的eth不能为0
        require(payment != 0, "PaymentSplitter: account is not due payment");
        // 更新总支付totalReleased和支付给每个受益人的金额released
        totalReleased += payment;
        released[_account] += payment;
        // 转账
        _account.transfer(payment);
        emit PaymentReleased(_account, payment);
    }

    /**
     * @dev 计算一个账户能够领取的eth。
     * 调用了pendingPayment()函数。
     */
    function releasable(address _account) public view returns (uint256) {
        // 计算分账合约总收入totalReceived
        uint256 totalReceived = address(this).balance + totalReleased;
        // 调用_pendingPayment计算account应得的ETH
        return pendingPayment(_account, totalReceived, released[_account]);
    }

    /**
     * @dev 根据受益人地址`_account`, 分账合约总收入`_totalReceived`和该地址已领取的钱`_alreadyReleased`,计算该受益人现在应分的`ETH`。
     */
    function pendingPayment(
        address _account,
        uint256 _totalReceived,
        uint256 _alreadyReleased
    ) public view returns (uint256) {
        console.log(_account, _totalReceived, _alreadyReleased);
        // account应得的ETH = 总应得ETH - 已领到的ETH
        return (_totalReceived * shares[_account]) / totalShares - _alreadyReleased;
    }

    /**
     * @dev 新增受益人_account以及对应的份额_accountShares。只能在构造器中被调用,不能修改。
     */
    function _addPayee(address _account, uint256 _accountShares) private {
        // 检查_account不为0地址
        require(_account != address(0), "PaymentSplitter: account is the zero address");
        // 检查_accountShares不为0
        require(_accountShares > 0, "PaymentSplitter: shares are 0");
        // 检查_account不重复
        require(shares[_account] == 0, "PaymentSplitter: account already has shares");
        // 更新payees,shares和totalShares
        payees.push(_account);
        shares[_account] = _accountShares;
        totalShares += _accountShares;
        // 释放增加受益人事件
        emit PayeeAdded(_account, _accountShares);
    }
}
# 编译指令
# npx hardhat compile

合约测试

测试说明

  • 首先向合约转入收益总金额(采用了ethers.getContract对合约进行转账操作,需要下载对应的插件hardhat-deploy-ethers);
  • 进行合约验证测试
    const {ethers,getNamedAccounts,deployments} = require("hardhat");
    const { assert,expect } = require("chai");
    describe("分账合约",function(){
    let paymentSplitContract;
    let deployer;
    let PaymentSplit;//分账合约
    let firstAccount//第一个账户
    let secondAccount//第二个账户
    beforeEach(async function(){
        await deployments.fixture(["Paymentsplit"]);
        //转账
        const { deployer: deployerAddress } = await getNamedAccounts();
        deployer = await ethers.provider.getSigner(deployerAddress);
        paymentSplitContract = await ethers.getContract("PaymentSplit", deployer);
        //获取账户
        firstAccount=(await getNamedAccounts()).firstAccount;
        secondAccount=(await getNamedAccounts()).secondAccount;
        const PaymentsplitDeployment = await deployments.get("PaymentSplit");
        PaymentSplit = await ethers.getContractAt("PaymentSplit",PaymentsplitDeployment.address);//已经部署的合约交
    });
    describe("分账合约测试",function(){
        it("分账合约",async function(){
            let income="100.0";//合约转的账
            const tx = {
                to: paymentSplitContract.address,
                value: ethers.utils.parseEther(income), // 转账 100 ETH
              };
              // 发送交易
              const txResponse = await deployer.sendTransaction(tx);
              console.log("Transaction hash:", txResponse.hash);
              // 等待交易确认
              await txResponse.wait();
              // 检查合约余额
              const balance = await ethers.provider.getBalance(paymentSplitContract.address);
              console.log("Contract balance:", ethers.utils.formatEther(balance));
            //查看受益人地址 参数是说明 账号下标 例如0,1
            console.log("查看受益人地址",await PaymentSplit.payees(0))
            //查看firstAccount账号应给领取多少代币
            let firstAccountBalance=await PaymentSplit.releasable(firstAccount)
            console.log("查看firstAccount账号可以领取多少代币",`${ethers.utils.formatEther(firstAccountBalance)} ETH`)
            //查看受益人已经领取代币
            console.log("受益人已经领取到的代币",await PaymentSplit.released(firstAccount))
            //查看受益人的份额 参数是账号地址
            console.log("受益人的份额",await PaymentSplit.shares(firstAccount))
            //查看合余额总支出
            console.log("合余额总支出",await PaymentSplit.totalReleased())
            //查看合约总份额
            console.log("合约总份额",await PaymentSplit.totalShares())
            //领取受益代币方法 
            await PaymentSplit.release(firstAccount)
            let firstAccountreceive=await PaymentSplit.released(firstAccount)
            let firstAccountreceiveValue=ethers.utils.formatEther(firstAccountreceive)
            console.log("firstAccount已经领取代币",`${firstAccountreceiveValue} ETH`)
            let expenditureBalance=await PaymentSplit.totalReleased()
            let expenditureBalanceValue=ethers.utils.formatEther(expenditureBalance)
            console.log("合余额总支出",`${expenditureBalanceValue} ETH`)
            //账号2的测试验证通理
            //根据受益人地址_account, 分账合约总收入_totalReceived和该地址已领取的钱_alreadyReleased,计算该受益人现在应分的ETH。
            console.log("查看受益账号当下领取多少钱",await PaymentSplit.pendingPayment(firstAccount,Number(income),Number(firstAccountreceiveValue)))
        })
    })
    })
    # 测试指令
    # npx hardhat test ./test/xxx.js

    合约部署

    module.exports = async function ({getNamedAccounts,deployments}) {
    const  firstAccount= (await getNamedAccounts()).firstAccount;
    const  secondAccount= (await getNamedAccounts()).secondAccount;
    //   const [addr1,addr2]=(await ethers.getSigners())
    let TokenAddress= [firstAccount,secondAccount];
    //   console.log(TokenAddress)
    const ShareArray=[8,2]
    const {deploy,log} = deployments;
    const Paymentsplit=await deploy("PaymentSplit",{
    from:firstAccount,
    args: [TokenAddress,ShareArray],//参数 受益人地址数组,受益人份额数组
    log: true,
    })
    console.log('Paymentsplit合约地址',Paymentsplit.address)
    }
    module.exports.tags = ["all", "Paymentsplit"];
    # 部署指令
    # npx hardhat deploy

    总结

    以上就是分账合约开发、测试、部署全部过程,注意:在测试的中需要通过ethers.getContract的方法对合约进行资金的转入,解决ethers.getContract报错问题需要下载相应的插件(hardhat-deploy-ethers),还用要区分ethers.getContractethers.getContractAt的区别后者最主要的场景用来进行合约交互。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
木西
木西
江湖只有他的大名,没有他的介绍。