一个针对可出售智能合约的协议

原文作者:Pablo Ruiz

原文地址:https://medium.freecodecamp.org/a-protocol-for-sellable-smart-contracts-829bc2ce02b3

照片由 Jezael Melgoza 上传至 Unsplash

智能合约所有权的概念并没有被内置进以太坊里面。即便智能合约的创建和部署都由一个账户完成,智能合约的创建者(无论是外部持有账户(External Owned Account,EOA)还是另外一个合约)对自己部署的智能合约也没有一点特权。

智能合约的很多使用场景都要有个 "持有者" 的角色。而 “持有者” 应该被赋予对智能合约的一些特权和责任。

在一个众筹合约(crowdsale contract)里,持有者可能要负责管理整个过程,并在出现问题时中止这个众筹。

在一些像 Lottery 和 Ruffle Dapp 这样的抽奖里,持有者可能要负责执行抽签的过程。

在任何需要持有资金的合约里,可能需要将持有人设置为在合约销毁时的受益人。

照片由 Ricardo Resende 上传至 Unsplash

将持有者设定为部署合约的那个账号是很多智能合约都遵循的一个惯例。

就像下面这样:

pragma solidity 0.4.19;

contract MyContract {
    address owner;

    function MyContract(){
        owner = msg.sender;
    }
}

然后加个修饰符:

modifier onlyOwner {
    require(msg.sender == owner);
    _;    
}

最后利用这个修饰符确保只有合约持有者才能进行一些关键的操作。

// 自毁合约并且把资金都转给持有者
// 显然,这个操作只能提供给持有者

function destroyContract() public onlyOwner {
    selfdestruct(owner);
}

合同所有权变更的问题

有些情况需要将合同的所有权交给其他人。这里举几个例子:

  • 合约持有者只是代表别人部署了合约(比如开发人员或顾问为某个公司做承包工作)。
  • 一个公司想清算或者变卖它的资产,而资产包括了持有或者没持有以太币的智能合约。
  • 智能合约的持有者想拿它送人,把它捐掉,或者卖掉换钱。
照片由 rawpixel.com 上传至 Unspixel

有些合约(不过恐怕不多)会内置一个用来把合约所有权交给其他账户的函数。其中一部分还会内置另外一个能让被交予所有权的用户接受这一交接的函数。

function changeOwner(address _newOwner) public onlyOwner {
    ownerCandidate = _newOwner;
}


function acceptOwnership() public {
    require(msg.sender == ownerCandidate);  
    owner = ownerCandidate;
}

目前,changeOwner() 还有 acceptOwnership() 这两个函数还有一些没能解决的常见的问题,使得它们没能被广泛使用。这些问题在上面提到的所有权交接的情形里都会有所涉及:

  • 合约买方怎么知道卖方会在买方付款之后真的会执行相应的 changeOwner() 函数?
  • 换个角度也会有这种情况。如果采用货到付款的方式,卖方怎么知道买方会在卖方先交付所有权之后会真的付款?
  • 买方怎么知道卖方在交接所有权之前不会篡改合约(毕竟它只是数据)?

针对可出售合约的协议

我所提出的解决方案是实现一系列功能,使智能合约的持有者可以通过与特定的某个人交换以太币的形式,或者以宣布出售合约并基于先到先得的规则来让别人购买的形式,来出售一个智能合约。这一协议也可以加以扩展,使得使用其他的销售方法,并在不同的销售方法里进行不同形式的拍卖成为可能。

这一合约的细节可以在相应的EIP(Ethereum Improvement Proposals,以太坊改进提案)里面查阅并且讨论。

接下来我会介绍一个使用这一协议的范例。这一范例的源码也可以在我的 Github 项目上查阅。

处理所有权

处理合约的所有权是非常基础的部分。正如惯例所做的那样,我们会在合约初始化时设置该条约的持有者为msg.sender

function Sellable() public {
    owner = msg.sender;
    Transfer(now,address(0),owner,0);
}

然后我们再加上修饰符 onlyOwner。这一修饰符会用在每一个我们想只让合约持有者能执行的函数:

modifier onlyOwner {
    require(msg.sender == owner);
    _;    
}

我们会希望我们的合约能在特定的条件下允许 owner 对其进行修改。

将合约出售

合同的持有者可以通过调用以下函数来将它出售:

function initiateSale(uint _price, address _to) onlyOwner public {
    require(_to != address(this) && _to != owner);
    require(!selling);
        
    selling = true;
        
    // 如果确定了买方就在这里将买方记录下来
    sellingTo = _to;
        
    askingPrice = _price;
}

initiateSale() 函数有两个参数:

  • uint _price:这是合约持有者对所出售的合约的定价。
  • address_to:这是可选的参数,对应于合约买方。

在出售合约时,持有者有两种选择:在交易已经事先定好的情况下持有者可以指定买家;或者他们也可以只 “宣布” 出售合约,并且让第一个宣称想要(而且按价格支付了)的人得到它。

另外,定价可以设置为 0。这意味着合同的持有者可以赠送,捐赠合约或只是把合约弄走。

这里要注意一个重点:有个修饰符ifNotLocked可以添加到合同的函数里面,以防止合同在被出售的过程中被执行。如果使用得当,这可以防止合约数据在出售完成之前被篡改。

最后,这里还有一个函数cancelPurchase()要介绍。它能让合约持有者在有人买到合约之前取消合约的销售。

function cancelSale() onlyOwner public {
    require(selling);
        
    // 重置与出售有关的变量
    resetSale();
}

购买合约

当合约开始出售时,卖方完成出售所需的全部工作就是为买方(如果指定了的话)或任何人(如果没有指定买方)调用以下函数:

function completeSale() public payable {
    require(selling);
    require(msg.sender != owner);
    require(msg.sender == sellingTo || sellingTo == address(0));
    require(msg.value == askingPrice);
        
    // 交换所有权
    address prevOwner = owner;
    address newOwner = msg.sender;
    uint salePrice = askingPrice;
        
    owner = newOwner;
        
    // 重置和出售合约有关的变量
    resetSale();
        
    prevOwner.transfer(salePrice);
        
    Transfer(now,prevOwner,newOwner,salePrice);
}

这里completeSale()函数带有修饰符payable,这意味着该函数是一个需要发送以太币才能执行的函数。而发送的金额必须与持有者设置的定价完全相同。

当 completeSale() 被执行时,一部分以太币会从买家转移给卖家,然后所有权会从卖家转移给买家。然后在完成这一流程后给新的持有者重置合约的交易状态。这样新的持有者就能正常使用或者出售这一合约了。

一个示例

下面演示一个出售简单合约的例子:

contract Kitty is Sellable {
    
    string public name;
    uint public kittyValue = 0;
    
    function Kitty(string _name) public {
        name = _name;
    }
    
    function findNewOwner() public onlyOwner {
        kittyValue = kittyValue + 1 ether;   
        super.initiateSale(kittyValue,address(0));
    }
    
    function renameKitty(string newName) ifNotLocked public onlyOwner {
        name = newName;
    }
    
    function buyKitty() public payable {
        require(msg.value == kittyValue);
        super.completeSale();
    }
}

现在我们有一个用来代表 CryptoKitty😺(链养猫)的合约。持有者可以通过findNewOwner()把它卖掉。每当小猫被出售时,他的价值都会增加 1 个以太币。小猫的主人可以通过函数renameKitty()改变它的名字。不过当小猫正在出售的时候,由于renameKitty()函数加了ifNotLocked修饰符,它的名字就不能改了。

就是这样!

如果您有进一步改进 Sellable 协议的建议,请在我创建的EIP中发表您的意见,错误或建议

照片由 Jonas Vincent 上传至 Unsplash

本文的版权归 Tnecesoc 所有,如需转载请联系作者。

发表于

我来说两句

9 条评论
登录 后参与评论

相关文章

来自专栏申龙斌的程序人生

三角套利分析

搬砖是币圈中一种常见的套利方式,主要利用两个交易所之间的币币交易对的价格差,低买高卖来获利,随着参与人数的增多,现在市面上的手工搬砖基本上没有机会了,全是搬砖机...

1644
来自专栏区块链大本营

区块链构架就是造房子!10分钟带你走遍构建全程

2008年,一个叫做中本聪(Satoshi Nakamoto)的人(或团体)定义了第一个区块链。

702
来自专栏企鹅号快讯

谈谈我对区块链的理解

通过之前研究的十大前景行业(带来高收入的 10 大开源技术,可以涨工资了!)提到人工智能,区块链,大数据都是今年以及2018年的技术热门趋势,本文专门介绍下什么...

2247
来自专栏区块链深度

智能合约是怎样运作的?三分钟读懂智能合约

当今社会,执行合约需要耗费大量社会资源。比方说,A、B两家公司签订合同,后来A违反合同条约,导致B损失重大。B想要拿回属于自己的东西,于是向法院起诉。就算B打官...

1644
来自专栏企鹅号快讯

虾说区块链-57-《精通比特币》笔记十二

一直在说区块链是一系列技术结合后的新的技术架构,那么这里分别介绍下这些相关技术,也涉及到一些扩展开去的相关内容。 ? 区块链-《精通比特币》笔记十二: 2018...

19810
来自专栏欧阳大哥的轮子

用脑残的方式来理解虚拟货币和区块链技术

最近比特币等虚拟货币价格大涨,造就了N多的富翁和负翁。现在到处都在谈论区块链技术,貌似不知道一点或者不谈论这些都不好意思说自己是IT人员一样。其实一直以来我也就...

792
来自专栏企鹅号快讯

漫画玩转区块链,让区块链不再难懂

来源及作者:程序员小灰 ? ? ? ? ? ? ? ? 什么是区块链? 区块链,英文 Blockchain,本质上是一种去中心化的分布式数据库。任何人只要架设自...

18910
来自专栏BestSDK

从生产到交易,一文读懂比钻石还贵的“比特币”

以物易物的比特村 话说在这个世界上,有一个叫比特村的小村庄,村庄共有几百户人家。这个村庄几乎与世隔绝,过着自给自足的生活。由于没有大规模贸易,比特村村民一直过着...

3148
来自专栏奇点大数据

一个故事告诉你比特币的原理及运作机制

花时间看了一些比特币原理相关的资料,虽然不敢说把每个细节都完全搞懂了,不过整体思路和关键部分的主要原理还是比较明白。写一篇文章分享给大家。这篇文章的定位会比较科...

3555
来自专栏大数据文摘

以太坊是什么鬼?!媲美比特币的加密币大揭秘

1213

扫码关注云+社区