返回介绍

开发智能合约

发布于 2024-06-02 01:00:06 字数 4970 浏览 0 评论 0 收藏 0

网页打开 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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文