Solidity 智能合约实例分析——网购

2018-12-20  本文已影响0人  FinJmy

1 场景

在网购的应用场景中,我们定义如下几个关键要素:

purchase.jpg

2 逻辑

  1. 所有参与者持有一个区块链账户
  2. 卖方创建合约,指定商品价格
  3. 买方使用代币购买,钱款锁定
  4. 卖方发货
  5. 买方确认收货,钱款打给卖方
buy_flow.jpg

3 完整代码

源代码地址 https://solidity.readthedocs.io/en/v0.5.1/solidity-by-example.html

pragma solidity >=0.4.22 <0.6.0;

contract Purchase {
    uint public value;
    address payable public seller;
    address payable public buyer;
    enum State { Created, Locked, Inactive }
    State public state;

    constructor() public payable {
        seller = msg.sender;
        value = msg.value / 2;
        require((2 * value) == msg.value, "Value has to be even.");
    }

    modifier condition(bool _condition) {
        require(_condition);
        _;
    }

    modifier onlyBuyer() {
        require(
            msg.sender == buyer,
            "Only buyer can call this."
        );
        _;
    }

    modifier onlySeller() {
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _;
    }

    modifier inState(State _state) {
        require(
            state == _state,
            "Invalid state."
        );
        _;
    }

    event Aborted();
    event PurchaseConfirmed();
    event ItemReceived();

    function abort()
        public
        onlySeller
        inState(State.Created)
    {
        emit Aborted();
        state = State.Inactive;
        seller.transfer(address(this).balance);
    }

    function confirmPurchase()
        public
        inState(State.Created)
        condition(msg.value == (2 * value))
        payable
    {
        emit PurchaseConfirmed();
        buyer = msg.sender;
        state = State.Locked;
    }

    function confirmReceived()
        public
        onlyBuyer
        inState(State.Locked)
    {
        emit ItemReceived();
        state = State.Inactive;
        buyer.transfer(value);
        seller.transfer(address(this).balance);
    }
}

4 解析

4.1 数据结构

买家卖家都需要进行代币的交易活动,因此账户定义为 address payable。这个合约出现了 enum 枚举类型。

uint public value;  // 公开的价格数据
address payable public seller;
address payable public buyer;
enum State { Created, Locked, Inactive }
State public state;

4.2 构造函数

此处要求商家打入的资金为商品售价的2倍作为保证金,因此 msg.value 必须为偶数,才能确保 ÷2 算出的商品价格是正常值。

constructor() public payable {
    seller = msg.sender;
    value = msg.value / 2;
    require((2 * value) == msg.value, "Value has to be even.");
}

4.3 修改器

modifier 指示函数修改器。本示例中,这种修改器以嵌入的方式加到被作用函数上。运行时,_; 部分会用被作用函数的原有代码替代。

modifier condition(bool _condition) { require(_condition); _; }

modifier onlyBuyer() {
    require(msg.sender == buyer, "Only buyer can call this.");
    _;
}

modifier onlySeller() {
    require(msg.sender == seller, "Only seller can call this.");
    _;
}

modifier inState(State _state) {
    require(state == _state, "Invalid state.");
    _;
}

4.4 购买下单函数

该函数使用了多个修饰器来进行条件检查。要求 msg.value 的值要恰好等于设置的商品价格的2倍作为保证金。调用时会触发 PurchaseConfirmed 事件。购买确认后,买家的对应代币被锁定。

event PurchaseConfirmed();

function confirmPurchase()
    public inState(State.Created)
    condition(msg.value == (2 * value)) payable
{
    emit PurchaseConfirmed();
    buyer = msg.sender;
    state = State.Locked;
}
}

4.5 收货确认函数

买家在收到货物后,调用该函数确认打款。将触发 ItemReceived 事件。这里的函数设计流程再一次体现了先改状态再做实际操作的思路,避免同时多次调用,重复转账。address(this).balance 是指当前合约中的余额,在实际中是卖家的保证金 + 商品钱款。由于合约是卖家创建的,所以应该退回给卖家。

this 的定义如下:

The current contract, explicitly convertible to address

event ItemReceived();

function confirmReceived() public onlyBuyer inState(State.Locked)
{
    emit ItemReceived();
    state = State.Inactive;
    // 退回买家等同于商品价格的保证金
    buyer.transfer(value);
    // 退回卖家保证金及货款
    seller.transfer(address(this).balance);
}

4.6 下架函数

卖家在商品被购买前可以进行商品下架操作,触发 Aborted 事件,并退回相关资金。

event Aborted();

function abort() public onlySeller inState(State.Created)
{
    emit Aborted();
    state = State.Inactive;
    // 退回卖家保证金
    seller.transfer(address(this).balance);
}

(完)

上一篇下一篇

猜你喜欢

热点阅读