开发智能合约

网页打开Remix,http://remix.ethereum.org/。点击左上角加号,新建智能合约,取名 market.sol。

新建 struct :

  • 商品Commodity
pragma solidity ^0.4.14;

contract Market{

    struct Commodity{
        string name;
        string url;
        uint price;
        address seller;
        address buyer;
        uint time;
        bool isFinish;
    }
}

主角有了,下面该定义他的行为了:

  • Seller 发布物品,需要有个容器来保存 谁发布了什么,是不是mapping比较好?
  • 还有发布物品的方法。
  • Buyer 决定购买要往合约里打钱,还应该定义 收钱方法,正如前文所述,是带payable关键字的方法。
mapping(address => Commodity) warehouse;

function addCommodity(string name, string url, uint price){
    require(bytes(name).length > 0);
    require(bytes(url).length > 0);

    warehouse[msg.sender] = Commodity(name, url, price/(1 ether), msg.sender, 0x0, now, false);
}

我们增加了warehouse(仓库)这个mapping容器,以seller地址作为索引。addCommodity方法中,检验输入参数string长度大于0,Solidity中必须先把string转换为bytes形式才能计算长度。require 检验条件通常放在方法一开头。

price 这里除以 1 ether的意思是 把单位1换算成ether,不然后面的transfer转账操作会出现问题。

msg.sender为调用该合约的地址,在这个方法中就是发布物品的人,因此这样完成mapping 地址与新物品的映射。新Commodity的构造参数顺序必须按照struct定义时顺序,最后的0x0表示地址为空,因为现在刚刚发布还不知道买家是谁。

time参数是 发布时间的意思,简简单单一个now就代表了当前时间戳。

isFinish 表示这件物品是否卖出去了,判断交易是否结束。

等一等。如果卖家以后想要修改之前发布的信息呢?比如根据市场动态,决定提价,或者过了一段时间之后无人问津,决定降价,或者后来卖给其他朋友了呢。因此还需要 删改方法。

function removeCommodity(){
    var commodity = warehouse[msg.sender];
    require(commodity.seller != 0x0);

    delete warehouse[msg.sender];
}

function updateCommodity(string name, uint price){
    var commodity = warehouse[msg.sender];
    require(commodity.seller != 0x0);

    warehouse[msg.sender].name = name;
    warehouse[msg.sender].price = price;
    warehouse[msg.sender].time = now;
}

delete关键字出现了。正如前文所述,要删除一个元素必须通过它实现。

或许会有疑问:

var commodity = warehouse[msg.sender];

这句已经获取到了commodity对象,直接删改它不就好了么?

之前讲过memory和storage的区别,这里commodity实际上是memory变量,warehouse[msg.sender]获取的是storage变量,当storage变量赋值给memory变量时,实际上是拷贝了一份数据给memory使用,因此修改commodity实际上改的并不是warehouse[msg.sender]这个storage,因此我们这样删改。

还要注意:这里warehouse的索引都是msg.sender,意思是必须是卖家自己才能操作。如不做检查,谁都能改数据岂不乱套了?

下面是 打钱function:

function buy(address seller) payable returns (uint) {
    var commodity = warehouse[seller];
    require(commodity.seller != 0x0);

    warehouse[seller].buyer = msg.sender;
    return this.balance;
}

此次实例,我们以卖家address作为索引,来获取对应的物品。在方法一开始还是保留一个良好的习惯:上来先检查有无这个卖家对应的物品。然后记录下买家地址。

最后,就是激动人心的买家确认收货,放款环节了。

function confirmFinish(address seller){
    var commodity = warehouse[seller];
    require(commodity.seller != 0x0);
    require(commodity.buyer == msg.sender);

    warehouse[seller].isFinish = true;
    commodity.seller.transfer(commodity.price);
}

最重要的是要确定当前调用该方法的地址是买家地址。然后再修改状态,transfer执行向seller地址打钱操作。

转钱操作有两个:

transfer:

  • 发生异常throws。

  • 默认2300gas,不可调整,这样防止可重入攻击(因为2300gas太少了以至于除了转账不能做多余的操作)

send:

  • 发送异常返回false。
  • 默认2300gas,不可调整,这样防止可重入攻击。

  • 如果你想处理合约中的失败情况,应该少用。

目前完整代码:

pragma solidity ^0.4.14;

contract Market{

    struct Commodity{
        string name;
        string url;
        uint price;
        address seller;
        address buyer;
        uint time;
        bool isFinish;
    }

    mapping(address => Commodity) warehouse;


    function buy(address seller) payable returns (uint) {
        var commodity = warehouse[seller];
        require(commodity.seller != 0x0);

        warehouse[seller].buyer = msg.sender;
        return this.balance;
    }

    function addCommodity(string name, string url, uint price){
        require(bytes(name).length > 0);
        require(bytes(url).length > 0);

        warehouse[msg.sender] = Commodity(name, url, price/(1 ether), msg.sender, 0x0, now, false);
    }

    function removeCommodity(){
        var commodity = warehouse[msg.sender];
        require(commodity.seller != 0x0);

        delete warehouse[msg.sender];
    }

    function updateCommodity(string name, uint price){
        var commodity = warehouse[msg.sender];
        require(commodity.seller != 0x0);

        warehouse[msg.sender].name = name;
        warehouse[msg.sender].price = price/(1 ether);
        warehouse[msg.sender].time = now;
    }

    function confirmFinish(address seller){
        var commodity = warehouse[seller];
        require(commodity.seller != 0x0);
        require(commodity.buyer == msg.sender);

        warehouse[seller].isFinish = true;
        commodity.seller.transfer(commodity.price);
    }
}

results matching ""

    No results matching ""