0
点赞
收藏
分享

微信扫一扫

(六)以太坊(truffle、vue、remix)——dapp众筹系统

乱世小白 2022-02-07 阅读 69

一、项目所用技术

1. 前端采用:vue+typescript
2. 合约采用 以太坊开发语言 : solidity
3. 合约部署测试环境:ganache
4.项目源码地址:https://github.com/Janson404/crowdfundingDapp
5.前端项目启动: npm run serve 启动

二、ts源码

import Web3 from 'web3'
//@ts-ignore
import CrowdFunding from './CrowdFunding.json'

//@ts-ignore
const web3 = new Web3(window.ethereum);
const contract = new web3.eth.Contract(CrowdFunding.abi, '0xF70E48B312439B3CB1c0b0fa2B5851d51c81AFeA');

function addListener(fn: Function) {
    //@ts-ignore
    ethereum.on('accountsChanged', fn)
}
// 导入外部接口
export declare interface Funding {
    index: number,

    title: string,
    info: string,
    goal: number,
    endTime: number,
    initiator: string,
    over: boolean,

    success: boolean,
    amount: number,
    numFunders: number,
    numUses: number,
    myAmount?: number
}

export declare interface Use {
    index: number,
    info: string,
    goal: string,
    agreeAmount: string,
    disagree: string,
    over: boolean,
    agree: number // 0: 没决定,1同意,2不同意
}

async function authenticate() {
    //@ts-ignore
    await window.ethereum.enable();
}

async function getAccount() {
    return (await web3.eth.getAccounts())[0];
}
// 获取合约上的所有众筹信息
async function getAllFundings() : Promise<Funding[]> {
    const length = await contract.methods.numFundings().call();
    const result = []
    for(let i=1; i<=length; i++)
        result.push(await getOneFunding(i));
    return result;
}
// 获取合约上的一条众筹信息
async function getOneFunding(index:number) : Promise<Funding> {
    const data = await contract.methods.fundings(index).call();
    // 将数据从wei形式转换为eth
    data.goal = Web3.utils.fromWei(data.goal, 'ether')
    data.amount = Web3.utils.fromWei(data.amount, 'ether')

    return {index, ...data}
}

// 当前账户投资的总的投资金额
async function getMyFundingAmount(index:number) : Promise<number> {
    const account = await getAccount();
    return parseInt(Web3.utils.fromWei(await contract.methods.getMyFundings(account, index).call(), 'ether'));
}

async function getMyFundings() : Promise<{init: Funding[], contr: Funding[]}> {
    const account = await getAccount();
    const all = await getAllFundings();
    const result : {
        init: Funding[],
        contr: Funding[]
    } = {
        init: [],
        contr: []
    };
    
    for(let funding of all) {
        const myAmount = await getMyFundingAmount(funding.index);
        if(funding.initiator == account) {
            result.init.push({
                myAmount,
                ...funding
            })
        }
        // 两个条件句
        if(myAmount != 0) {
            result.contr.push({
                myAmount,
                ...funding
            })
        }
    }
    return result;
}
// 注意一下此处的id与合约中的id,没有理清楚
async function contribute(id:number, value:number) {
    return await contract.methods.contribute(id).send({from: await getAccount(), value: Web3.utils.toWei(value.toString(10), 'ether')});
}
//感觉有bug
async function newFunding(account:string, title:string, info:string, amount:number, seconds:number) {
    return await contract.methods.newFunding(account, title, info, Web3.utils.toWei(amount.toString(10), 'ether'), seconds).send({
        from: account,
        gas: 1000000
    });
}

async function getAllUse(id:number) : Promise<Use[]> {
    const length = await contract.methods.getUseLength(id).call();
    const account = await getAccount();
    const rusult : Use[] = []
    for(let i=1; i<=length; i++) {
        const use = await contract.methods.getUse(id, i, account).call();
        rusult.push({
            index: i,
            info: use[0],
            goal: Web3.utils.fromWei(use[1], 'ether'),
            agreeAmount: Web3.utils.fromWei(use[2], 'ether'),
            disagree: Web3.utils.fromWei(use[3], 'ether'),
            over: use[4],
            agree: use[5]
        });
    }
    return rusult;
}

async function agreeUse(id:number, useID: number, agree:boolean) {
    const accont = await getAccount();
    return await contract.methods.agreeUse(id, useID, agree).send({
        from: accont,
        gas: 1000000
    })
}

async function newUse(id:number, goal:number, info:string) {
    const account = await getAccount();
    const eth = Web3.utils.toWei(goal.toString(10), 'ether')
    return await contract.methods.newUse(id, eth, info).send({
        from: account,
        gas: 1000000
    })
}

async function returnMoney(id: number) {
    const account = await getAccount();
    return await contract.methods.returnMoney(id).send({
        from: account,
        gas: 1000000
    })
}

export {
    getAccount,
    authenticate,
    contract,
    getAllFundings,
    getOneFunding,
    getMyFundingAmount,
    contribute,
    newFunding,
    getAllUse,
    agreeUse,
    newUse,
    getMyFundings,
    returnMoney,
    addListener
}

三、solidity合约源码

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;

contract CrowdFunding {
  
  // 投资人结构定义
  struct Funder {
    address payable addr;   // 投资人的地址
    uint amount;            // 出资数额
  }

  // 资金使用请求结构定义
  struct Use {
    string info;                     // 使用请求的说明
    uint goal;                       // 使用请求的数额
    uint agreeAmount;                // 目前的同意数额
    uint disagree;                   // 目前的不同意数额
    bool over;                       // 请求是否结束
    mapping(uint => uint) agree;     // 出资人是否同意 0: 还没决定,1:同意,2:不同意
  }

  // 众筹项目的结构定义
  struct Funding {
    address payable initiator;       // 发起人
    string title;                    // 项目标题
    string info;                     // 项目简介
    uint goal;                       // 目标金额
    uint endTime;                    // 众筹结束时间

    bool success;                    // 众筹是否成功,成功则 amount 含义为项目剩余的钱

    uint amount;                     // 当前已经筹集到的金额
    uint numFunders;                 // 投资记录数量
    uint numUses;                    // 使用请求数量
    mapping(uint => Funder) funders; // 投资记录具体信息
    mapping(uint => Use) uses;       // 所有的使用请求
  }

  uint public numFundings;                  // 众筹项目数量
  mapping(uint => Funding) public fundings; // 所有的众筹项目

  /**
   * 发起众筹项目
   * @param initiator 发起人
   * @param title 项目标题
   * @param info 项目简介
   * @param goal 目标金额
   * @param endTime 结束时间
   */
  function newFunding(address payable initiator, string memory title, string memory info, uint goal, uint endTime) public returns(uint) {
    require(endTime > block.timestamp);

    numFundings = numFundings + 1;
    Funding storage f = fundings[numFundings];
    f.initiator = initiator;
    f.title = title;
    f.info = info;
    f.goal = goal;
    f.endTime = endTime;
    f.success = false;
    f.amount = 0;
    f.numFunders = 0;
    f.numUses = 0;
    
    return numFundings;
  }

  function contribute(uint ID) public payable {
    // 贡献的钱必须大于0,不能超过差额
    require(msg.value > 0 && msg.value <= fundings[ID].goal - fundings[ID].amount);
    // 时间上必须还没结束
    require(fundings[ID].endTime > block.timestamp);
    // 必须是未完成的众筹
    require(fundings[ID].success == false);

    Funding storage f = fundings[ID];
    f.amount += msg.value;
    f.numFunders = f.numFunders + 1;
    f.funders[f.numFunders].addr = msg.sender;
    f.funders[f.numFunders].amount = msg.value;
    // 考虑本项目是否达成目标
    f.success = f.amount >= f.goal;
  }

  // 退钱
  function returnMoney(uint ID) public {
    require(ID <= numFundings && ID >= 1);
    require(fundings[ID].success == false);

    Funding storage f = fundings[ID];
    for(uint i=1; i<=f.numFunders; i++)
      if(f.funders[i].addr == msg.sender) {
        f.funders[i].addr.transfer(f.funders[i].amount);
      //  此处存在bug
        // f.funders[i].amount = 0;
        // f.amount -= f.funders[i].amount;
        f.amount -= f.funders[i].amount;
        f.funders[i].amount = 0;
      }
  }

  function newUse(uint ID, uint goal, string memory info) public {
    require(ID <= numFundings && ID >= 1);
    require(fundings[ID].success == true);
    require(goal <= fundings[ID].amount);
    require(msg.sender == fundings[ID].initiator);

    Funding storage f = fundings[ID];
    f.numUses = f.numUses + 1;
    f.uses[f.numUses].info = info;
    f.uses[f.numUses].goal = goal;
    f.uses[f.numUses].agreeAmount = 0;
    f.uses[f.numUses].disagree = 0;
    f.uses[f.numUses].over = false;
    f.amount = f.amount - goal;
  }

  function agreeUse(uint ID, uint useID, bool agree) public {
    require(ID <= numFundings && ID >= 1);
    require(useID <= fundings[ID].numUses && useID >= 1);
    require(fundings[ID].uses[useID].over == false);

    for(uint i=1; i<=fundings[ID].numFunders; i++)
      if(fundings[ID].funders[i].addr == msg.sender) {
        if(fundings[ID].uses[useID].agree[i] == 1) {
          // 没怎么懂
          fundings[ID].uses[useID].agreeAmount -= fundings[ID].funders[i].amount;
        } else if(fundings[ID].uses[useID].agree[i] == 2) {
          fundings[ID].uses[useID].disagree -= fundings[ID].funders[i].amount;
        }
        if(agree) {
          fundings[ID].uses[useID].agreeAmount += fundings[ID].funders[i].amount;
          fundings[ID].uses[useID].agree[i] = 1;
        }
        else {
          fundings[ID].uses[useID].disagree += fundings[ID].funders[i].amount;
          fundings[ID].uses[useID].agree[i] = 2;
        }
      }
    checkUse(ID, useID);
  }

  function checkUse(uint ID, uint useID) public {
    require(ID <= numFundings && ID >= 1);
    require(fundings[ID].uses[useID].over == false);

    if(fundings[ID].uses[useID].agreeAmount >= fundings[ID].goal / 2) {
      fundings[ID].uses[useID].over = true;
      fundings[ID].initiator.transfer(fundings[ID].uses[useID].goal);
    }
    if(fundings[ID].uses[useID].disagree > fundings[ID].goal / 2) {
      fundings[ID].amount = fundings[ID].amount + fundings[ID].uses[useID].goal;
      fundings[ID].uses[useID].over = true;
    }
  }

  function getUseLength(uint ID) public view returns (uint) {
    require(ID <= numFundings && ID >= 1);

    return fundings[ID].numUses;
  }

  function getUse(uint ID, uint useID, address addr) public view returns (string memory, uint, uint, uint, bool, uint) {
    require(ID <= numFundings && ID >= 1);
    require(useID <= fundings[ID].numUses && useID >= 1);

    Use storage u = fundings[ID].uses[useID];
    uint agree = 0;
    for(uint i=1; i<=fundings[ID].numFunders; i++)
      if(fundings[ID].funders[i].addr == addr) {
        agree = fundings[ID].uses[useID].agree[i];
        break;
      }
    return (u.info, u.goal, u.agreeAmount, u.disagree, u.over, agree);
  }

  function getBalance() public view returns (uint) {
    return address(this).balance;
  }
  
  function getMyFundings(address addr, uint ID) public view returns (uint) {
      uint res = 0;
      for(uint i=1; i<=fundings[ID].numFunders; i++) {
          if(fundings[ID].funders[i].addr == addr)
            res += fundings[ID].funders[i].amount;
      }
      return res;
  } 
}
举报

相关推荐

0 条评论