以太坊彩票合約案例


一.合約案例簡介

  • 此案例為入門案例,從 合約 -> 編譯 -> web3部署和調用

二.彩票合約源碼

pragma solidity ^0.4.25;
// 彩票合約
contract LotteryShop{

    //購買彩票事件,在購買彩票方法中調用
    event BuyLottery(address indexed buyer,uint money,uint16 luckNum);
    //開獎事件,在開獎方法中調用
    event DrawLottery(address winner,uint money,uint16 luckNum);

    //購買記錄(購買者的address, 彩票號碼)
    mapping(address=>uint) buyMapping;
    //購買用戶的地址
    address[] usrAdrList;

    //管理員地址
    address manageAdr;
    //合約地址
    address contractAdr;

    constructor() public payable{
        //將合約部署人的地址保存起來作為管理員地址
        manageAdr=msg.sender;
        //將當前合約對象的地址保存
        contractAdr= address(this);
    }

    //0.1 顯示管理員地址
    function ShowManageAdr() constant returns(address){
        return manageAdr;
    }

    //0.2 顯示調用者的彩票數據
    function ShowInvokerCaiPiao() constant returns(uint){
        return buyMapping[msg.sender];
    }

    //0.3 顯示管理員余額
    function ShowManageBalance() constant returns(uint){
        return manageAdr.balance;
    }
    //0.4 顯示合約余額
    function ShowContractMoney() constant returns(uint){
        return contractAdr.balance;
    }
    //0.5 獲取買家地址列表
    function getAllUsrAddress() view returns(address[]){
        return usrAdrList;
    }

    //0.5 買彩票方法
    function BuyCaiPiao(uint16 haoMa) payable{
        //0. 判斷用戶賬戶是否有1 eth
        require(msg.value == 1 ether);
        //1. 判斷彩票購買列表里是否已經存在當前用戶
        require(buyMapping[msg.sender]==0);

        //2. 將用戶的錢轉到合約賬戶
        contractAdr.send(msg.value);

        //3.1 調用事件日志
        emit BuyLottery(msg.sender,msg.value,haoMa);

        //3.2 添加到mapping
        buyMapping[msg.sender] = haoMa;
        //3.3 將地址存入 買家數組
        usrAdrList.push(msg.sender);
    }

    //1. 開獎 - 必須是管理員才能操作
    function KaiJiang() adminOnly payable returns(uint){
        //1.生成一個隨機的開獎號碼
        uint256 luckNum = uint256(keccak256(abi.encodePacked(block.difficulty,now)));
        //1.1 取模10,保證獎號在10以內
        luckNum = luckNum % 3;

        address tempAdr;
        //2.循環用戶地址數組
        for(uint32 i=0; i< usrAdrList.length;i++){
            tempAdr = usrAdrList[i];
            //2.1 判斷用戶地址 在 mapping中 對應的 CaiPiao.hao 的數字是否一樣
            if(buyMapping[tempAdr] == luckNum){
                //2.2 記錄日志
                emit DrawLottery(tempAdr,contractAdr.balance,uint16(luckNum));
                //2.3 將合約里所有的錢轉給 中獎賬戶地址
                tempAdr.send(contractAdr.balance);
                break;
            }
        }
        //3.返回 中獎號碼
        return luckNum;
    }

    //2. 重置數據
    function resetData() adminOnly{
        //2.1 循環 買家數組,刪除 購買記錄mapping中對應的記錄
        for(uint16 i = 0;i<usrAdrList.length;i++){
            delete buyMapping[usrAdrList[i]];
        }
        //2.2 刪除 買家數組
        delete usrAdrList;
    }

    //3. 銷毀合約
    function kill() adminOnly{
        //3.1 調用合約自毀函數,把合約賬戶余額轉給當前調用者(管理員)
        selfdestruct(msg.sender);
    }

    //4. 管理員修飾符,只允許管理員操作
    modifier adminOnly() {
        require(msg.sender == manageAdr);
        //代碼修飾器所修飾函數的代碼
        _;
    }
}

三.nodeJs 編譯合約並保存為文件

const solc = require('solc');
const path = require('path');
const fs = require('fs');

//1.1 合約代碼文件路徑
const sourceFilePath = path.resolve(__dirname,'./LotteryShop.sol');
//1.2 合約編譯成字節碼后的保存文件路徑
const bytecodeFilePath = path.resolve(__dirname,'./LotteryShop.bytecode');
//2.讀取文件
const source = fs.readFileSync(sourceFilePath,'utf-8');
//3.編譯合約原文件
const result = solc.compile(source,1);
//console.log(result);

console.log('1.編譯完成:'+sourceFilePath);
//3.1 將編譯后的代碼寫入到文件中,這樣部署文件 就不需要每次都重新編譯,而是直接讀取編譯好的合約字節碼和abi就行了
fs.writeFileSync(bytecodeFilePath,JSON.stringify(result.contracts[':LotteryShop']),'utf-8');
console.log('2.字節文件寫入完成:'+bytecodeFilePath);

//4.暴露給外部訪問
//module.exports=result.contracts[':CaiPiaoShop'];

四.web3調用代碼

//1.1 導入 編譯好的 合約的 字節代碼 和 abi
const path = require('path');
const fs = require('fs');
//1.2 合約編譯后的文件路徑
const bytecodeFilePath = path.resolve(__dirname,'./LotteryShop.bytecode');
const bytecodeJsonStr = fs.readFileSync(bytecodeFilePath,'utf-8');
const byteCodeJsonObj = JSON.parse(bytecodeJsonStr);
//const {bytecode,interface} = require('./compilCaiPiao');
const bytecode = byteCodeJsonObj.bytecode;
const interface = byteCodeJsonObj.interface;

//2.導入 hd錢包provider
const HDWalletProvider = require("truffle-hdwallet-provider");
//3.助記詞(相當於是我們的私鑰)
const mnemonic = "jar trumpet ..... cat beef"; // 12 word mnemonic
//4.創建 provider,可以用來訪問 以太坊真實網絡節點
const provider = new HDWalletProvider(mnemonic, "https://rinkeby.infura.io/v3/3a60f2b16......",0);//最后的0 是獲取 助記詞 的第1個地址

//5.創建web對象
const Web3 = require('web3');
const web3= new Web3(provider);

// “主函數”作為啟動函數
async function main(){
    console.log('開始與以太網交互......');
    const usrAdr = await web3.eth.getAccounts();
    web3.eth.defaultAccount = usrAdr[0];
    console.log('當前調用者的地址:' + web3.eth.defaultAccount);

    //6.部署合約到 以太網節點
    //let contractObj =await deployContract();

    //7.調用合約
    //7.0 創建 遠程智能合約(傳入第6步已經部署好的合約地址)
    const contractObj = await new web3.eth.Contract(JSON.parse(interface),'0x8f27Ad244cFeabcCc45B81B7406a7EDEA53c5f97');
    console.log('獲取【合約】對象成功!');

    // 7.1 調用 買彩票 的方法(合約對象,購買的號碼)
    //await buyLottery(contractObj,2);

    // 7.2 顯示合約賬戶余額
    //await showContracMoney(contractObj);
    // 查看調用者購買的號碼
    //await showInvokerMoney(contractObj);

    // 7.3 開獎
    await withdrawLottery(contractObj);

    // 7.4 顯示買家賬戶列表
    await showUsrList(contractObj);
    await showContracMoney(contractObj);
    await showInvokerMoney(contractObj);
    // 7.5 重置數據
    //await resetContract(contractObj);
    //await killContract(contractObj);
    //await showUsrList(contractObj);
    console.log('結束!');
}

// 啟動
main();

// 1.部署合約
async function deployContract() {
    console.log('開始部署合約......');
    let contractObj = await new web3.eth.Contract(JSON.parse(interface))
        .deploy({
            data: bytecode,
            arguments: []
        }).send({
           from:web3.eth.defaultAccount,
            gas:'1000000'
        });

    console.log('部署成功,合約地址:【'+contractObj.options.address+'】'); // 0x8f27Ad244cFeabcCc45B81B7406a7EDEA53c5f97
    return contractObj;
}

// 2.買彩票
async function buyLottery(contractObj,luckNum) {
    console.log('開始購買彩票,稍等15秒~~');

    await contractObj.methods.BuyCaiPiao(luckNum).send(
        {
            from:web3.eth.defaultAccount,
            value:web3.utils.toWei('1','ether'),
            gas:'1000000'
        }
    );
    console.log('購買彩票完畢~~');
}

// 3.開獎
async function withdrawLottery(contractObj) {
    console.log('開獎進行中請等待15秒左右...');
    let luckNum;
    await contractObj.methods.KaiJiang().send({
        from:web3.eth.defaultAccount,
        gas:'1000000'
    },function (err,result) {
        //將返回的 開獎數字 賦值給 局部變量 luckNum
        luckNum = result;
        console.log('開獎完成, luckNum:');
        console.log(result);
    });
    console.log('開獎完成:'+luckNum);
}

// -------------------------------------------
// 4.顯示合約余額
async function showContracMoney(contractObj) {
    let msg = await contractObj.methods.ShowContractMoney().call();
    console.log('合約賬號里的余額:'+msg);
}
// 5.顯示管理員地址
async function showManageAddress(contractObj) {
    let msg = await contractObj.methods.ShowManageAdr().call();
    console.log('合約管理員地址:'+msg);
}

// 6.查看調用者購買的號碼
async function showInvokerMoney(contractObj) {
    //獲取當前調用合約的賬戶
    const usrAdr = await web3.eth.getAccounts();

    // 1.因為 被調用合約里的ShowInverkCaiPiao()方法 需要使用 msg.sender
    // 所以在 call({from:當前調用者賬號地址})的時候要指定from為當前調用者
    // let msg = await contractObj.methods.ShowInverkCaiPiao().call({
    //     from:usrAdr[0]
    // });

    // 2.或者,可以設置 web3.eth.defaultAccount = usrAdr[0],再調用 call()時,會自動設置 from
    //設置給 默認賬戶
    web3.eth.defaultAccount = usrAdr[0];
    let msg1 = await contractObj.methods.ShowInvokerCaiPiao().call();
    console.log('調用者購買的號碼:' + msg1);
}

// 7.重置合約數據
async function resetContract(contractObj) {
    // 因為 合約的 resetData 方法里 修改了 全局變量,所以需要使用 send 方法
    let msg = await contractObj.methods.resetData().send({
        from:web3.eth.defaultAccount,
        gas:'1000000'
    });

    console.log('交易記錄:');
    console.log(msg);//打印從交易記錄
    console.log('彩票購買記錄 和 買家地址列表 已清零~~');
}
// 8.銷毀合約
async function killContract(contractObj) {
    console.log('合約銷毀開始~~');
    await contractObj.methods.kill().send({
        from:web3.eth.defaultAccount,
        gas:'1000000'
    });
    console.log('合約已作廢~~');
}
// 9. 顯示買家列表
async function showUsrList(contractObj) {
    let msg = await contractObj.methods.getAllUsrAddress().call();
    console.log('買家地址列表:');
    console.log(msg);

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM