作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
拉狄克·奥斯特洛夫斯基的头像

Radek奥斯托夫斯基

Radek是一名区块链工程师,对以太坊智能合约感兴趣. 他在机器学习方面也有丰富的经验.

以前在

索尼
分享

区块链及其应用如今比以往任何时候都受欢迎. 特别是以太坊, 提供智能合约功能, 为可以在分布式系统中实现的新想法打开了大门, 不可变的, 不可信的时尚.

开始 以太坊智能合约 由于学习曲线相当陡峭,太空可能会让人有点不知所措. 我们希望这篇文章(以及以太坊系列的后续文章)可以减轻这种痛苦,让您快速上手.

松露,固体,和ĐApps

在本文中,我们假设您对区块链应用程序和以太坊有一些基本的了解. 如果你觉得你需要温习一下你的知识,我们推荐 这是来自松露框架的以太坊概述.

这篇文章涵盖的内容:

  • 时间锁定钱包的应用
  • 开发环境设置
  • 使用松露框架开发智能合约
    • 固体合同的描述
    • 如何编译、迁移和测试智能合约
  • 使用ÐApp与浏览器中的智能合约进行交互
    • 使用MetaMask设置浏览器
    • 主要用例的概览

时间锁定的钱包:用例

有许多不同的应用 Et在这里um 聪明的合同. 目前最流行的是加密货币(作为ERC20代币实现)和众筹代币销售(a.k.a. 首次代币发行(ico).实用程序ERC20令牌的一个很好的例子是 Motoro硬币. 在这篇博文中, 我们将探索一些不同的东西:将资金锁定在加密钱包合约中的想法. 这个想法本身有很多用例.

为ICO投资

有几个例子, 但目前锁定资金最常见的原因可能是“投资”.“想象一下,你刚刚成功地筹集了一个ICO,你的公司仍然持有你的团队成员之间分配的大部分代币.

确保员工持有的代币不能直接交易,对所有相关方都是有益的. 如果没有适当的控制措施, 任何给定的员工都可以通过出售他们所有的代币来采取行动, 兑现了, 然后离开公司. 这将对市场价格产生负面影响,并使项目的所有其他贡献者不高兴.

基于加密的“最后遗嘱”

另一个想法是使用智能合约作为加密遗嘱. 想象一下,我们希望将我们的加密货币储蓄存储在一个家庭成员可以访问的合同中, 但只有在我们出事之后. 假设我们应该通过时不时地调用一些合约调用来“签到”钱包.

如果我们没有按时报到,他们就会把钱取走. 他们各自获得的资金比例可以在合同中明确规定, 也可以由家庭成员协商一致决定.

养老金或信托基金

锁定资金的另一个应用可能是创建一个小型养老基金或定期储蓄账户, i.e.,它可以防止所有者在未来的某个时间之前提取任何资金. (对于沉迷于加密货币的交易者来说,这可能特别有用,可以帮助他们保持以太币的完整性.)

在这篇博文的其余部分,我们将探索的用例是类似的:为其他人保存一些加密货币, 就像未来的生日礼物.

让我们想象一下,我们想在某人18岁生日时送他一个以太币. 我们可以在一张纸上写下账户的私钥和存放资金的钱包的地址,然后装在信封里交给他们. 他们唯一要做的就是在他们18岁时从他们的账户中调用合约上的一个函数,所有的资金都会转移到他们身上. 或者,我们可以使用一个简单的ÐApp. 听起来不错? 让我们开始吧!

以太坊开发设置

在进行智能合约开发之前,您需要 节点.jsGit 安装在您的机器上. 在本博客中,我们将使用 松露 框架. 即使你没有它也可以, 松露大大降低了开发的进入门槛, 测试, 以及以太坊智能合约的部署. 我们完全同意他们的声明:

<块quote>

松露是以太坊最流行的开发框架,其使命是让您的生活变得更加轻松.

安装命令如下:

NPM安装-g松露

现在,得到这个项目的代码:

Git克隆http://github.com/radek1st/time-locked-wallet
cd time-locked-钱包

值得注意的是,该项目遵循标准的松露项目结构,感兴趣的目录如下:

  • 合同:持有所有的可靠性合约
  • 迁移:包含描述迁移步骤的脚本
  • src:包含ÐApp代码
  • 测试:存储所有契约测试

包含的智能合约概述

这个项目包括几份合同. 以下是概要:

  • TimeLockedWallet.索尔 是这个项目的主要合同,下面有详细的描述.
  • TimeLockedWalletFactory.索尔工厂 合约,让任何人都可以轻松地部署自己的 TimeLockedWallet.
  • ERC20.索尔 以太坊代币的ERC20标准接口.
  • ToptalToken.索尔 是定制的ERC20令牌吗.
  • SafeMath.索尔 是一个小库使用的ToptalToken执行安全的算术运算.
  • 迁移.索尔 内部松露合同是否有助于迁移.

关于编写以太坊合约的任何问题,请参阅官方 可靠性智能合约文档.

TimeLockedWallet.索尔

我们的 TimeLockedWallet.索尔 稳定性合约是这样的:

实用可靠度^0.4.18;

上面一行表示此合约所需的可靠性编译器的最低版本.

进口”./ ERC20.索尔”;

这里,我们导入其他契约定义,稍后在代码中使用.

合约时间锁钱包{
    ...
}

以上就是我们的主要目标. 合同 适用于我们合同的代码. 下面描述的代码来自花括号内.

地址公众创作者;
地址公owner;
int 公共 unlockDate;
联合公共创建;

这里我们定义了几个 公共 默认情况下生成相应getter方法的变量. 其中有几个是典型的 使用uint (无符号整数)和一对是 address (16个字符长的以太坊地址.)

修饰符 onlyOwner {
  要求(味精.Sender == owner);
  _;
}

简单来说, 修饰符 是否在开始执行它所附加的函数之前必须满足先决条件.

函数TimeLockedWallet (
    地址_creator,地址_owner, unlockdate
) 公共 {
    Creator = _creator;
    Owner = _owner;
    unlockDate = _unlockDate;
    createdAt = 现在;
}

这是我们的第一个函数. 因为名称与我们的合同名称完全一致, 它是构造函数,在创建契约时只调用一次.

注意,如果您要更改合同的名称, 这将成为任何人都可以调用的正常函数,并在您的合约中形成后门,就像 奇偶Multisig钱包错误. 另外, 请注意,案例也很重要, 所以如果这个函数名是小写的,它也会变成一个普通的函数, 这不是你想要的.

函数()可支付公共{ 
  收到(味精.发送方、味精.值);
}

上述函数是一种特殊类型,称为 回退 函数. 如果有人向这个合约发送任何ETH,我们将很高兴地收到它. 合约的ETH余额将会增加,并将触发 收到了 事件. 要使任何其他函数接受传入的ETH,可以用 应付 关键字.

函数info() 公共 视图返回(address, address, 使用uint, 使用uint, 使用uint) {
    返回(创建者,所有者,unlockDate, createdAt, 这).平衡);
}

这是我们的第一个正则函数. 它没有函数参数,并定义了要返回的输出元组. 请注意, 这.平衡 返回此合约的当前以太币余额.

函数 withdraw() onlyOwner 公共 {
   需要(现在 >= unlockDate);
   味精.发送方.转移(这.平衡);
   退出(味精.发送者,这.平衡);
}

上面的函数只能在以下情况下执行 onlyOwner 满足前面定义的修饰符. 如果 需要 语句不为真,则合同退出并出现错误. 这就是我们检查是否 unlockDate 已经过去了. 味精.发送方 是这个函数的调用者,它被转移了整个合约的以太币余额. 在最后一行中,我们也触发了a 收回了 事件. 稍后将描述事件.

有趣的是, 现在等于 块.时间戳——可能不像人们想象的那么准确. 要由矿工来采摘, 因此,它可能长达15分钟(900秒),如下所述 公式:

<块quote>

家长。.时间戳 >= 块.时间戳 <= 现在 + 900 seconds

因此, 现在 不应该用来测量小的时间单位.

回退令牌 (addres_令牌合同)
   需要(现在 >= unlockDate);
   ERC20 令牌 = ERC20 (_令牌Contract);
   int 令牌Balance = 令牌.平衡Of(这个);
   令牌.转移(所有者、令牌Balance);
   收回了Tokens (_令牌Contract味精.发送方,令牌Balance);
}

下面是提取ERC20令牌的函数. 因为合约本身不知道分配给这个地址的任何令牌, 我们必须传入要撤销的已部署ERC20令牌的地址. 实例化它 ERC20 (_令牌Contract) 然后找到并将整个令牌余额转移给接收者. 我们还解雇了一名 收回了Tokens 事件.

事件 收到了(addres_from, int _amount);
事件撤回(地址_to, int _amount);
事件 withw令牌s (addres_令牌合同, addres_to, 使用uint _amount);

在这个代码片段中,我们定义了几个事件. 触发事件基本上是附加在区块链上的交易收据上的日志条目. 每个事务可以附加零个或多个日志条目. 事件的主要用途是 调试和监控.

这就是我们对以太币和ERC20令牌进行时间锁定所需的全部内容——只需几行代码. 还不错吧? 现在让我们看看我们的另一份合同, TimeLockedWalletFactory.索尔.

TimeLockedWalletFactory.索尔

创建高级工厂合同的背后有两个主要原因. 首先是安全问题. 把钱放在不同的钱包里, 我们最终不会只有一个拥有大量以太币和代币的合约. 这将给钱包所有者100%的控制权,并希望阻止黑客试图利用它.

其次, 工厂合约允许轻松创建TimeLockedWallet合约, 不需要有任何开发设置. 您需要做的就是从另一个钱包或ĐApp调用一个函数.

实用可靠度^0.4.18;

进口”./ TimeLockedWallet.索尔”;

合同时间锁定钱包工厂 {
    ...
}

上述内容很简单,与之前的合同非常相似.

映射(address => 地址[]) 钱包;

这里,我们定义a 映射 类型, 哪个像字典或地图, 但是所有可能的键都是预设的,并指向默认值. 在这个例子中 address 类型,默认为零地址 0x00. 我们还有一个数组类型, 地址[],这是 addresses.

在可靠性语言中,数组总是包含一种类型,可以有固定或可变的长度. 在本例中,数组是无界的.

为了总结这里的业务逻辑,我们定义了一个映射,称为 钱包 它由用户地址(合约创建者和所有者)组成,每个地址都指向一组相关的钱包合约地址.

函数gett钱包 (addres_user) 
    公共
    视图
    返回(地址[])
{
    返回钱包(_user);
}

在这里,我们使用上述函数返回所有合约钱包a _user 创建的或有权创建的. 请注意, 视图 (在较旧的编译器版本中称为 常数)表示这是一个不会改变区块链状态的函数,因此可以免费调用, 不消耗任何汽油.

函数newTimeLockedWallet(地址_owner, unlockdate)
    应付
    公共
    返回钱包(地址)
{
    钱包= 新TimeLockedWallet(味精 ..发送方, _owner, _unlockDate);
    钱包(味精.发送者).推动(钱包);
    如果(味精.发送方 != _owner) {
        钱包(_owner).推动(钱包);
    }
    钱包.传输(味精.值);
    创建(钱包,味精.发送方, _owner, 现在, _unlockDate, 味精.值);
}

这是合同中最重要的部分:工厂方法. 通过调用它的构造函数,我们可以动态地创建一个新的时间锁定钱包: 新TimeLockedWallet(味精.发件人,_owner, _unlockDate). 然后存储创建者和接收者的地址. 稍后,我们将在此函数执行中传递的所有可选以太币转移到新创建的钱包地址. 最后,我们发出信号 创建 事件,定义为:

事件创建(地址钱包), 地址从, 地址, 使用使用uint createdAt, 使用使用uint unlockDate, 单位金额);

ToptalToken.索尔

如果我们没有创建自己的以太坊令牌,本教程就不会那么有趣, 为了完整性, 我们正在赋予生命 ToptalToken. ToptalToken 是一个标准的ERC20令牌,实现如下接口:

合同ERC20 {
  使用uint256 公共 totalSupply;

  函数平衡Of(地址谁)的公共视图返回(使用uint256);
  函数transfer(地址, 使用uint256 value) 公共 returns (bool);
  函数允许(地址所有者,地址支出者)公共视图返回(使用uint256);
  函数transferFrom(地址from,地址to, 使用uint256值)公共返回(bool);
  批准(地址支出者,使用uint256值)公共返回(bool);

  事件审批(地址索引的所有者,地址索引的支出者,使用uint256值);
  事件转移(地址索引从,地址索引到,使用uint256值);
}

它与其他令牌的区别如下:

string 公共 常数 name = "Toptal Token";
字符串公共常量符号= "TTT";
Uint256 公共 常数 decimals = 6;

totalSupply = 1000000 *(10 **位小数);

我们给了它一个名字,一个符号,总供应量是一百万,并且使它可以被整除到六位小数.

要了解令牌合约的不同变体,请随意浏览 OpenZeppelin回购.

松露控制台:编译、迁移和测试智能合约

要快速入门,请使用内置区块链运行松露:

松露开发

你应该看到这样的内容:

松露 Develop始于http://localhost:9545/

账户:
(0) 0 x627306090abab3a6e1400e9345bc60c78a8bef57
(1) 0 xf17f52151ebef6c7334fad080c5704d77216b732
(2) 0 xc5fdf4076b8f3a5357c5e395ab970b5b54098fef
(3) 0 x821aea9a577a9b44299b9c15c88cf3087f3b5544
(4) 0 x0d1d4e623d10f9fba5db95830f7d3839406c6af2
(5) 0 x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e
(6) 0 x2191ef87e392377ec08e7c08eb105ef5448eced5
(7) 0 x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5
(8) 0 x6330a553fc93768f612722bb8c2ec78ac90b3bbc
(9) 0 x5aeda56215b167893e80b4fe645ba6d5bab767de

助记:糖枫蛋糕糖布丁奶油蜂蜜丰富光滑的碎甜点

助记符种子允许您重新创建私钥和公钥. 例如,将其导入到MetaMask中,如下所示:

RESTORE VAULT:使用助记符钱包种子将私钥和公钥导入MetaMask.

要编译合同,运行:

> compile

你应该看到:

编译 ./合同/ ERC20.索尔...
编译 ./合同/迁移.索尔...
编译 ./合同/ SafeMath.索尔...
编译 ./合同/ TimeLockedWallet.索尔...
编译 ./合同/ TimeLockedWalletFactory.索尔...
编译 ./合同/ ToptalToken.索尔...
将工件写入 ./构建/合同

现在,我们需要定义要部署哪些契约. 这是在 迁移/ 2 _deploy_合同.js:

var TimeLockedWalletFactory =工件.要求(“TimeLockedWalletFactory”);
var ToptalToken =工件.要求(“ToptalToken”);

模块.导出=函数(部署器){
  部署人员.部署(TimeLockedWalletFactory);
  部署人员.部署(ToptalToken);
};

我们首先导入两个契约构件 TimeLockedWalletFactoryToptalToken. 然后我们简单地部署它们. 我们错过了 TimeLockedWallet 因为这个契约是动态部署的. 有关迁移的更多信息,请参阅 松露迁移文档.

要迁移契约,运行:

> migrate

这应该会导致类似以下的结果:

运行migration: 1_initial_migration.js
     部署迁移...
     ... 0 x1c55ae0eb870ac1baae86eeb15f3aba3f521df46d9816e04400e9b5951ecc099
     迁移:0 x8cdaf0cd259887258bc13a92c0a6da92698644c0
   保存成功迁移到网络...
     ... 0 xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
   节约工件...
   正在运行的迁移:2_deploy_合同.js
     部署TimeLockedWalletFactory...
     ... 0 xe9d9c37508bb58a1591d0f052d6870810118a0a19f728bf0cea4f4e5c17acd7a
     TimeLockedWalletFactory: 0 x345ca3e014aaf5dca488057592ee47305d9b3e10
     部署ToptalToken...
     ... 0 x0469ce110735f27bbb1a85c85a77ba4b0ba0d5aa52c3d67164045b849d8b2ed6
     ToptalToken: 0 xf25186b5081ff5ce73482ad761db0eb0d25abfbf
   保存成功迁移到网络...
     ... 0 x059cf1bbc372b9348ce487de910358801bbbd1c89182853439bec0afaee6c7db
   节约工件...

你可以看出来 TimeLockedWalletFactoryToptalToken 成功部署.

最后,为了确保一切正常,让我们运行一些测试. 测试位于 测试 目录和对应的主要合同 TimeLockedWalletTest.jsTimeLockedWalletFactoryTest.js. 为简便起见, 我们将不深入编写测试的细节, 把它留给读者作为练习. 要执行测试,只需运行:

> 测试

希望你能看到所有的测试都像这样通过:

合同:TimeLockedWalletFactory
  工厂创建的合同是否有效(365ms)

合同:TimeLockedWallet
  在解锁日期(668ms)后,所有者可以提取资金。
  在解锁日期(765ms)之前,任何人都不能提取资金。
  在解锁日期(756毫秒)之后,除了所有者以外,任何人都不能提取资金。
  拥有者可以在解锁日期(671ms)后提取ToptalToken。
  ✓允许获取钱包信息(362ms)

6次传球(4s)

限时钱包ÐApp

是时候看看它的实际应用了. 与任何区块链交互的最简单方法是使用带有web UI的分布式应用程序, 也就是ÐApps(有时也叫dapps).”)

分布式应用程序设置

为了运行这个ÐApp,您需要有一个支持以太坊的浏览器. 实现此目标的最简单方法是安装 MetaMask Chrome插件. 还有一个 安装和配置MetaMask与松露的可视化指南.

智能合约场景

回到我们的场景,我们为什么不首先介绍演员呢? 让我们假设Alice将是时间锁定钱包的创建者,Bob将是资金的接收者/最终所有者.

元面具上爱丽丝和鲍勃的账户.

场景概述:

  • Alice为Bob创建了一个时间锁定的钱包,并发送了一些ETH
  • Alice还发送了一些ERC20 total令牌
  • Bob可以看到他可以访问的钱包和他创建的钱包
  • 在钱包的时间锁到期之前,Bob不能提取任何资金
  • Bob在解锁ETH时提取ETH
  • Bob提取了所有ERC20 total令牌

首先,Alice为Bob创建一个时间锁定的钱包,并发送一个初始的以太币. 我们可以看到Bob已经创建了一个新的合约钱包:

使用时间锁定钱包ĐApp为Alice创建一个钱包.

在合约创建后的任何时候,钱包都可以充值. 充值可以来自任何人,并以以太币或ERC20代币的形式进行. 让Alice发送100个total 令牌到Bob的新钱包,如下所示:

Alice使用时间锁定钱包ĐApp向Bob发送了100个总代币.

从Alice的角度来看,充值后的钱包是这样的:

爱丽丝对装满钱的钱包的看法.

现在让我们切换角色,以Bob的身份登录. Bob应该能够看到他创建或接收的所有钱包. 由于Alice创建的合约仍然是时间锁定的,他不能提取任何资金:

Bob对充值后的钱包的视图,此时钱包仍处于时间锁定状态.

耐心地等待,直到锁过期……

一个现在解锁的钱包在时间锁定钱包ĐApp.

Bob现在已经准备好提取以太币和Toptal代币:

Bob从未解锁的钱包中提取以太币.

Bob从未解锁的钱包中取出total 令牌.

清空时间锁钱包后,他的地址余额增加了,这让他非常高兴,也很感激爱丽丝:

没锁的钱包,现在空了.

Et在这里um网络

如果您想与所描述的契约交互, 你不必在本地运行它们:我们已经将它们部署到以太坊的Rinkeby测试网络上. 部署ToptalToken 在这里 部署了TimeLockedWalletFactory 在这里.

你可以使用我们的 Ð部署应用程序,链接到GitHub页面提供的上述合同. 注意,您需要安装MetaMask并将其连接到Rinkeby.

故障排除

我们在开发这个项目的过程中遇到了一些问题. 第一个是Chrome中MetaMask的脆弱(就像抱怨无效) 现时标志). 我们发现最简单的修复方法就是重新安装插件.

此外,在编辑智能合约时,松露有时会不同步,并抱怨 固体参数数无效 错误. 我们发现一个简单的 Rm -r build 再次进行编译/迁移将清除它.

以太坊开发:值得陡峭的学习曲线

我们希望这篇文章能激起你的兴趣,让你踏上以太坊的开发者之旅. 通往网络荣耀的道路将是陡峭而耗时的, 但是有很多资源可以帮助你 这一个 这对我们很有帮助). 欢迎通过下面的评论与我们联系.

这个项目的源代码 在GitHub上可用.

如果你想知道如何使用 uPort 手机应用程序,而不是MetaMask,看看 演示源代码 这个项目的另一个赢得黑客马拉松的版本.

我也欢迎你阅读 我的后续教程,专注于ĐApp的创建.

学分

非常感谢 Maciek Zielinski 感谢他对这个项目的贡献.

了解基本知识

  • 什么是智能合约?

    智能合约是在以太坊虚拟机上执行的计算机代码. 智能合约可以发送和接受以太和数据. 契约本质上是不可变的,除非程序另有规定.

  • 什么是以太坊虚拟机?

    以太坊虚拟机(EVM)是一个沙盒运行时环境,用于作为执行字节码的堆栈机器实现的智能合约. 它的重点是为全世界的计算机提供安全性和执行不受信任的代码.

  • 什么是Mist和以太坊钱包?

    Mist是分布式应用程序的官方浏览器(ĐApps), 有时是dapps),它们是以太坊网络的用户友好的前端/ ui. 以太坊钱包就是其中之一ĐApps. 两者都是由构建以太坊的同一个人开发的.

  • 以太坊是开源的吗?

    引用Vitalik Buterin的话, 以太坊的发明者:“以太坊的一切, 包括网站, 的工具, 白皮书,当然还有所有的软件和编译器都是100%的, 墙到墙的开源和GPL协议.”

  • 以太坊是去中心化的吗??

    是的,它本质上是完全去中心化的. 读写操作是完全去中心化的,目前由工作量证明机制保护. 以太坊的设计方式是没有一个人或一个团体控制区块链.

  • 以太坊使用什么编程语言?

    以太坊目前选择的语言是可靠性. 可靠性是一种面向契约的编程语言, 主要受JavaScript启发, C++, 和Python, 用于编写智能合约. 还有其他语言也即将出现,比如Vyper.

  • ERC20令牌是什么?

    令牌是实现ERC20标准的智能合约. 它们包括获取总供应量和余额以及转移代币的方法等操作. 代币从未真正离开合约, 但它们只是在内部映射中被重新分配给不同的持有者的钱包地址.

聘请Toptal这方面的专家.
现在雇佣
拉狄克·奥斯特洛夫斯基的头像
Radek奥斯托夫斯基

位于 普吉岛,泰国

成员自 2014年9月24日

作者简介

Radek是一名区块链工程师,对以太坊智能合约感兴趣. 他在机器学习方面也有丰富的经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前在

索尼

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.