ethereum初探五:接入开发

以太坊下载后我们就可以启用geth进行启动节点,然后使用接口接入以太坊区块进行应用层的开发了。

首先安装 看我的文章:ethereum初探一:以太坊安装

在正式开始前,我们要先安装node,到node官网上下载linux64位的二进制包。

$cd /usr/local/src

$wget https://nodejs.org/dist/v8.9.4/node-v8.9.4-linux-x64.tar.xz

下载完了解压:

$xz -d node-v8.9.4-linux-x64.tar.xz

$tar -xvf node-v8.9.4-linux-x64.tar.xz

然后进行链接:

$ln -s /usr/local/src/node-v8.9.4-linux-x64/bin/node /usr/local/bin/node

$ln -s /usr/local/src/node-v8.9.4-linux-x64/bin/npm /usr/local/bin/npm

检查下:

$node -v

$npm -v

启动以太坊:

geth –datadir=/chaindata/.eth –nodiscover –rpc –rpcapi  –rpccorsdomain=’*’ –rpcaddr=”localhost”  –ws –wsorigins=’*’  console

这里先为了测试可以用*号,后续为了安全一定要把各项参数都改下。变内网做安全防火墙策略。具体细节不表。

安装web3,这里有个坑。网上目前资料很少,直接去官网才发现web3已经变成1.0,很多不兼容。而且1.0还是测试阶段。所以我还是选用了0.20.X. 我们先换下npm安装源,国外那源国内能2个小时不带动的。

$npm config set registry http://registry.npm.taobao.org/

查看下:$npm get registry.

$npm install web3@^0.20.1

没有git 先安装下git。截止我发稿最新的是0.20.3

$vi base.js

var Web3 = require(‘web3’);
var web3 = new Web3();
console.log(web3);

web3.setProvider(new web3.providers.HttpProvider(‘http://localhost:8545’));
console.log(web3.version);
var coinbase = web3.eth.accounts[0];
console.log(coinbase);

$node base.js

ethereum初探四:geth命令详解

命令用法

geth [选项] 命令 [命令选项] [参数…]

版本:

1.7.3-stable

命令:

account    管理账户
attach     启动交互式JavaScript环境(连接到节点)
bug        上报bug Issues
console    启动交互式JavaScript环境
copydb     从文件夹创建本地链
dump       Dump(分析)一个特定的块存储
dumpconfig 显示配置值
export     导出区块链到文件
import     导入一个区块链文件
init       启动并初始化一个新的创世纪块
js         执行指定的JavaScript文件(多个)
license    显示许可信息
makecache  生成ethash验证缓存(用于测试)
makedag    生成ethash 挖矿DAG(用于测试)
monitor    监控和可视化节点指标
removedb   删除区块链和状态数据库
version    打印版本号
wallet     管理Ethereum预售钱包
help,h     显示一个命令或帮助一个命令列表

ETHEREUM选项:

--config value          TOML 配置文件
--datadir “xxx”         数据库和keystore密钥的数据目录
--keystore              keystore存放目录(默认在datadir内)
--nousb                 禁用监控和管理USB硬件钱包
--networkid value       网络标识符(整型, 1=Frontier, 2=Morden (弃用), 3=Ropsten, 4=Rinkeby) (默认: 1)
--testnet               Ropsten网络:预先配置的POW(proof-of-work)测试网络
--rinkeby               Rinkeby网络: 预先配置的POA(proof-of-authority)测试网络
--syncmode "fast"       同步模式 ("fast", "full", or "light")
--ethstats value        上报ethstats service  URL (nodename:secret@host:port)
--identity value        自定义节点名
--lightserv value       允许LES请求时间最大百分比(0 – 90)(默认值:0) 
--lightpeers value      最大LES client peers数量(默认值:20)
--lightkdf              在KDF强度消费时降低key-derivation RAM&CPU使用

开发者(模式)选项:

--dev               使用POA共识网络,默认预分配一个开发者账户并且会自动开启挖矿。
--dev.period value  开发者模式下挖矿周期 (0 = 仅在交易时) (默认: 0)

ETHASH 选项:

--ethash.cachedir                        ethash验证缓存目录(默认 = datadir目录内)
--ethash.cachesinmem value               在内存保存的最近的ethash缓存个数  (每个缓存16MB ) (默认: 2)
--ethash.cachesondisk value              在磁盘保存的最近的ethash缓存个数 (每个缓存16MB) (默认: 3)
--ethash.dagdir ""                       存ethash DAGs目录 (默认 = 用户hom目录)
--ethash.dagsinmem value                 在内存保存的最近的ethash DAGs 个数 (每个1GB以上) (默认: 1)
--ethash.dagsondisk value                在磁盘保存的最近的ethash DAGs 个数 (每个1GB以上) (默认: 2)

 

交易池选项:

--txpool.nolocals            为本地提交交易禁用价格豁免
--txpool.journal value       本地交易的磁盘日志:用于节点重启 (默认: "transactions.rlp")
--txpool.rejournal value     重新生成本地交易日志的时间间隔 (默认: 1小时)
--txpool.pricelimit value    加入交易池的最小的gas价格限制(默认: 1)
--txpool.pricebump value     价格波动百分比(相对之前已有交易) (默认: 10)
--txpool.accountslots value  每个帐户保证可执行的最少交易槽数量  (默认: 16)
--txpool.globalslots value   所有帐户可执行的最大交易槽数量 (默认: 4096)
--txpool.accountqueue value  每个帐户允许的最多非可执行交易槽数量 (默认: 64)
--txpool.globalqueue value   所有帐户非可执行交易最大槽数量  (默认: 1024)
--txpool.lifetime value      非可执行交易最大入队时间(默认: 3小时)

 

性能调优的选项:

--cache value                分配给内部缓存的内存MB数量,缓存值(最低16 mb /数据库强制要求)(默认:128)
--trie-cache-gens value      保持在内存中产生的trie node数量(默认:120)

 

帐户选项:

--unlock value              需解锁账户用逗号分隔
--password value            用于非交互式密码输入的密码文件

 

API和控制台选项:

--rpc                       启用HTTP-RPC服务器
--rpcaddr value             HTTP-RPC服务器接口地址(默认值:“localhost”)
--rpcport value             HTTP-RPC服务器监听端口(默认值:8545)
--rpcapi value              基于HTTP-RPC接口提供的API
--ws                        启用WS-RPC服务器
--wsaddr value              WS-RPC服务器监听接口地址(默认值:“localhost”)
--wsport value              WS-RPC服务器监听端口(默认值:8546)
--wsapi  value              基于WS-RPC的接口提供的API
--wsorigins value           websockets请求允许的源
--ipcdisable                禁用IPC-RPC服务器
--ipcpath                   包含在datadir里的IPC socket/pipe文件名(转义过的显式路径)
--rpccorsdomain value       允许跨域请求的域名列表(逗号分隔)(浏览器强制)
--jspath loadScript         JavaScript加载脚本的根路径(默认值:“.”)
--exec value                执行JavaScript语句(只能结合console/attach使用)
--preload value             预加载到控制台的JavaScript文件列表(逗号分隔)

 

网络选项:

--bootnodes value    用于P2P发现引导的enode urls(逗号分隔)(对于light servers用v4+v5代替)
--bootnodesv4 value  用于P2P v4发现引导的enode urls(逗号分隔) (light server, 全节点)
--bootnodesv5 value  用于P2P v5发现引导的enode urls(逗号分隔) (light server, 轻节点)
--port value         网卡监听端口(默认值:30303)
--maxpeers value     最大的网络节点数量(如果设置为0,网络将被禁用)(默认值:25)
--maxpendpeers value    最大尝试连接的数量(如果设置为0,则将使用默认值)(默认值:0)
--nat value             NAT端口映射机制 (any|none|upnp|pmp|extip:<IP>) (默认: “any”)
--nodiscover            禁用节点发现机制(手动添加节点)
--v5disc                启用实验性的RLPx V5(Topic发现)机制
--nodekey value         P2P节点密钥文件
--nodekeyhex value      十六进制的P2P节点密钥(用于测试)

 

矿工选项:

--mine                  打开挖矿
--minerthreads value    挖矿使用的CPU线程数量(默认值:8)
--etherbase value       挖矿奖励地址(默认=第一个创建的帐户)(默认值:“0”)
--targetgaslimit value  目标gas限制:设置最低gas限制(低于这个不会被挖?) (默认值:“4712388”)
--gasprice value        挖矿接受交易的最低gas价格
--extradata value       矿工设置的额外块数据(默认=client version)

 

GAS价格选项:

--gpoblocks value      用于检查gas价格的最近块的个数  (默认: 10)
--gpopercentile value  建议gas价参考最近交易的gas价的百分位数,(默认: 50)

 

虚拟机的选项:

--vmdebug        记录VM及合约调试信息

 

日志和调试选项:

--metrics            启用metrics收集和报告
--fakepow            禁用proof-of-work验证
--verbosity value    日志详细度:0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail (default: 3)
--vmodule value      每个模块详细度:以 <pattern>=<level>的逗号分隔列表 (比如 eth/*=6,p2p=5)
--backtrace value    请求特定日志记录堆栈跟踪 (比如 “block.go:271”)
--debug                     突出显示调用位置日志(文件名及行号)
--pprof                     启用pprof HTTP服务器
--pprofaddr value           pprof HTTP服务器监听接口(默认值:127.0.0.1)
--pprofport value           pprof HTTP服务器监听端口(默认值:6060)
--memprofilerate value      按指定频率打开memory profiling    (默认:524288)
--blockprofilerate value    按指定频率打开block profiling    (默认值:0)
--cpuprofile value          将CPU profile写入指定文件
--trace value               将execution trace写入指定文件

 

WHISPER实验选项:

--shh                        启用Whisper
--shh.maxmessagesize value   可接受的最大的消息大小 (默认值: 1048576)
--shh.pow value              可接受的最小的POW (默认值: 0.2)

 

弃用选项:

--fast     开启快速同步
--light    启用轻客户端模式

 

其他选项:

–help, -h    显示帮助

 

版权:

Copyright 2013-2017 The go-ethereum Authors

ECS挂载数据盘

运行 fdisk -l 命令查看实例是否有数据盘。

创建一个单分区数据盘,依次执行以下命令:

  1. 运行 fdisk /dev/vdb:对数据盘进行分区。
  2. 输入 n 并按回车键:创建一个新分区。
  3. 输入 p 并按回车键:选择主分区。因为创建的是一个单分区数据盘,所以只需要创建主分区。

    说明:如果要创建 4 个以上的分区,您应该创建至少一个扩展分区,即选择 e

  4. 输入分区编号并按回车键。因为这里仅创建一个分区,可以输入 1。
  5. 输入第一个可用的扇区编号:按回车键采用默认值 1(ECS是2048)。
  6. 输入最后一个扇区编号:因为这里仅创建一个分区,所以按回车键采用默认值。
  7. 输入 wq 并按回车键,开始分区。

查看新的分区:运行命令 fdisk -l。如果出现以下信息,说明已经成功创建了新分区 /dev/vdb1

在新分区上创建一个文件系统:运行命令 mkfs.ext3 /dev/vdb1

/etc/fstab 写入新分区信息:运行命令 echo /dev/vdb1 /mnt ext3 defaults 0 0 >> /etc/fstab

注意:Ubuntu 12.04 不支持 barrier,所以对该系统正确的命令是:echo '/dev/vdb1 /mnt ext3 barrier=0 0 0' >> /etc/fstab

如果需要把数据盘单独挂载到某个文件夹,比如单独用来存放网页,请将以上命令 /mnt 替换成所需的挂载点路径。

查看 /etc/fstab 中的新分区信息:运行命令 cat /etc/fstab

挂载文件系统:运行命令 mount /dev/vdb1 /mnt

查看目前磁盘空间和使用情况:运行命令 df -h。如果出现新建文件系统的信息,说明挂载成功,可以使用新的文件系统了。

ethereum初探三:在没有第三方下筹集资金

众筹你的创意

有时一个好创意需要大量的资金和集体努力。你可以要求捐赠,但捐赠者更愿意捐赠给他们更确定的项目,这样就能得到足够的支持和资金。这是一个众筹将会很理想的例子:你设定了一个目标,并在最后期限前完成。如果你错过了你的目标,这些捐赠会被返还,从而减少捐赠者的风险。由于代码是开放的和可审计的,所以不需要一个集中的、可信任的平台,因此每个人只需要支付的费用就是燃气费。

TOKENS AND DAOS

在这个例子中,我们将通过解决两个重要的问题来写一个更好的众筹:如何管理和保存奖励,以及在筹集资金后如何使用这些资金。

众筹的回报通常是由一个中心不变的数据库来处理的,该数据库可以跟踪所有捐赠者:任何错过了活动截止日期的人都无法进入,任何改变主意的捐赠者都无法离开。相反,我们将采用分散的方式,并创建一个代币来跟踪奖励,任何贡献的人都会得到一个令他们可以交易、出售或保留的代币。当给出实物奖励的时候,生产者只需要交换真实产品的代币。捐赠者可以保留他们的代币,即使这个项目没有实现它的目标,也可以作为一个纪念品。

而且,一般来说,那些在资金筹集和管理不善的情况下,资金如何使用的人也不能有任何发言权,这往往导致项目根本无法提供任何东西。在这个项目中,我们将使用一个民主的组织,它必须批准任何来自系统的资金。这通常被称为“众包”或“股东权益”,这是非常重要的,在某些情况下,代币可以是奖励本身,尤其是在一些项目中,一群人聚集在一起,建立共同的公共利益。

Get the necessary contracts

  • If you are just testing, switch the wallet to the testnet and start mining.
  • First of all, create a fixed supply token. For this example, we are going to create a supply of 100, use the name gadgets, the box emoji (📦) as a symbol and 0 decimal places. Deploy it and save the address.
  • Now create a shareholder association. In this example we are going to use the address of the token we just created as the Shares Address, a minimum quorum of 10, and 1500 minutes (25 hours) as the voting time. Deploy this contract and save the address.

THE CODE

Now copy this code and let’s create the crowdsale:


pragma solidity ^0.4.16;

interface token {
    function transfer(address receiver, uint amount);
}

contract Crowdsale {
    address public beneficiary;
    uint public fundingGoal;
    uint public amountRaised;
    uint public deadline;
    uint public price;
    token public tokenReward;
    mapping(address => uint256) public balanceOf;
    bool fundingGoalReached = false;
    bool crowdsaleClosed = false;

    event GoalReached(address recipient, uint totalAmountRaised);
    event FundTransfer(address backer, uint amount, bool isContribution);

    /**
     * Constrctor function
     *
     * Setup the owner
     */
    function Crowdsale(
        address ifSuccessfulSendTo,
        uint fundingGoalInEthers,
        uint durationInMinutes,
        uint etherCostOfEachToken,
        address addressOfTokenUsedAsReward
    ) {
        beneficiary = ifSuccessfulSendTo;
        fundingGoal = fundingGoalInEthers * 1 ether;
        deadline = now + durationInMinutes * 1 minutes;
        price = etherCostOfEachToken * 1 ether;
        tokenReward = token(addressOfTokenUsedAsReward);
    }

    /**
     * Fallback function
     *
     * The function without name is the default function that is called whenever anyone sends funds to a contract
     */
    function () payable {
        require(!crowdsaleClosed);
        uint amount = msg.value;
        balanceOf[msg.sender] += amount;
        amountRaised += amount;
        tokenReward.transfer(msg.sender, amount / price);
        FundTransfer(msg.sender, amount, true);
    }

    modifier afterDeadline() { if (now >= deadline) _; }

    /**
     * Check if goal was reached
     *
     * Checks if the goal or time limit has been reached and ends the campaign
     */
    function checkGoalReached() afterDeadline {
        if (amountRaised >= fundingGoal){
            fundingGoalReached = true;
            GoalReached(beneficiary, amountRaised);
        }
        crowdsaleClosed = true;
    }


    /**
     * Withdraw the funds
     *
     * Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached,
     * sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw
     * the amount they contributed.
     */
    function safeWithdrawal() afterDeadline {
        if (!fundingGoalReached) {
            uint amount = balanceOf[msg.sender];
            balanceOf[msg.sender] = 0;
            if (amount > 0) {
                if (msg.sender.send(amount)) {
                    FundTransfer(msg.sender, amount, false);
                } else {
                    balanceOf[msg.sender] = amount;
                }
            }
        }

        if (fundingGoalReached && beneficiary == msg.sender) {
            if (beneficiary.send(amountRaised)) {
                FundTransfer(beneficiary, amountRaised, false);
            } else {
                //If we fail to send the funds to beneficiary, unlock funders balance
                fundingGoalReached = false;
            }
        }
    }
}

CODE HIGHLIGHTS

Notice that in the Crowdsale function (the one that is called upon contract creation), how the variables deadline and fundingGoal are set:

fundingGoal = fundingGoalInEthers * 1 ether;
deadline = now + durationInMinutes * 1 minutes;
price = etherCostOfEachToken * 1 ether;

Those are some of the special keywords in solidity that help you code, allowing you to evaluate some things like 1 ether == 1000 finney or 2 days == 48 hours. Inside the system all ether amounts are kept track in wei, the smallest divisible unit of ether. The code above converts the funding goal into wei by multiplying it by 1,000,000,000,000,000,000 (which is what the special keyword ether converts into). The next line creates a timestamp that is exactly X minutes away from today by also using a combination of the special keywords now and minutes. For more global keywords, check the solidity documentation on Globally available variables.

The following line will instantiate a contract at a given address:

tokenReward = token(addressOfTokenUsedAsReward);

Notice that the contract understands what a token is because we defined it earlier by starting the code with:

interface token { function transfer(address receiver, uint amount){  } }

This doesn’t fully describe how the contract works or all the functions it has, but describes only the ones this contract needs: a token is a contract with a transfer function, and we have one at this address.

HOW TO USE

Go to contracts and then deploy contract:

Crowdsale deployment

  • Put the address of the organization you just created in the field if successful, send to.
  • Put 250 ethers as the funding goal
  • If you are just doing it for a test or demonstration, put the crowdsale duration as 3-10 minutes, but if you are really raising funds you can put a larger amount, like 45,000 (31 days).
  • The ether cost of each token should be calculated based on how many tokens you are putting up for sale (a maximum of how many you added as “initial supply” of your token on the previous step). In this example, put 5 ethers.
  • The address of the token you created should be added to the token reward address

Put a gas price, click deploy and wait for your crowdsale to be created. Once the crowdsale page is created, you now need to deposit enough rewards so it can pay the rewards back. Click the address of the crowdsale, then deposit and send 50 gadgets to the crowdsale.

I have 100 gadgets. Why not sell them all?

This is a very important point. The crowdsale we are building will be completely controlled by the token holders. This creates the danger that someone controlling 50%+1 of all the tokens will be able to send all the funds to themselves. You can try to create special code on the association contract to prevent these hostile takeovers, or you can instead have all the funds sent to a simple address. To simplify we are simply selling off half of all the gadgets: if you want to further decentralize this, split the remaining half between trusted organizations.

RAISE FUNDS

Once the crowdsale has all the necessary tokens, contributing to it is easy and you can do it from any ethereum wallet: just send funds to it. You can see the relevant code bit here:

function () {
    require(!crowdsaleClosed);
    uint amount = msg.value;
    // ...

The unnamed function is the default function executed whenever a contract receives ether. This function will automatically check if the crowdsale is active, calculate how many tokens the caller bought and send the equivalent. If the crowdsale has ended or if the contract is out of tokens the contract will throw meaning the execution will be stopped and the ether sent will be returned (but all the gas will be spent).

Crowdsale error

This has the advantage that the contract prevents falling into a situation that someone will be left without their ether or tokens. In a previous version of this contract we would also self destruct the contract after the crowdsale ended: this would mean that any transaction sent after that moment would lose their funds. By creating a fallback function that throws when the sale is over, we prevent anyone losing money.

The contract has a safeWithdrawl() function, without any parameters, that can be executed by the beneficiary to access the amount raised or by the funders to get back their funds in the case of a failed fundraise.

Crowdsale execution

Extending the crowdsale

WHAT IF THE CROWDSALE OVERSHOOTS ITS TARGET?

In our code, only two things can happen: either the crowdsale reaches its target or it doesn’t. Since the token amount is limited, it means that once the goal has been reached no one else can contribute. But the history of crowdfunding is full of projects that overshoot their goals in much less time than predicted or that raised many times over the required amount.

UNLIMITED CROWDSALE

So we are going to modify our project slightly so that instead of sending a limited set of tokens, the project actually creates a new token out of thin air whenever someone sends them ether. First of all, we need to create a Mintable token.

Then modify the crowdsale to rename all mentions of transfer to mintToken:

contract token { function mintToken(address receiver, uint amount){  } }
// ...
    function () {
        // ...
        tokenReward.mintToken(msg.sender, amount / price);
        // ...
    }

Once you published the crowdsale contract, get its address and go into your Token Contract to execute a Change Ownership function. This will allow your crowdsale to call the Mint Token function as much as it wants.

Warning: This opens you to the danger of hostile takeover. At any point during the crowdsale anyone who donates more than the amount already raised will be able to control the whole pie and steal it. There are many strategies to prevent that, but implementing will be left as an exercise to the reader:

  • Modify the crowdsale such that when a token is bought, also send the same quantity of tokens to the founder’s account so that they always control 50% of the project
  • Modify the Organization to create a veto power to some trusted third party that could stop any hostile proposal
  • Modify the token to allow a central trusted party to freeze token accounts, so as to require a verification that there isn’t any single entity controlling a majority of them

SCHEDULING A CALL

Ethereum contracts are passive, in that they can only do something once they have been activated. Fortunately there are some third party community services that provide that service for you: the Ethereum Alarm Clock is an open marketplace where anyone can receive ether to execute scheduled calls or pay ether to schedule them. This tutorial will be using the 0.6.0 version of the Alarm service. Documentation for this version available here.

Add the alarm clock

First, you need to add the contract to your watchlist. Go to your Contracts tab and then Watch contract (not deploy contract): Give the name “Ethereum Alarm Clock”, use 0xe109EcB193841aF9dA3110c80FDd365D1C23Be2a as address (the icon should look like a green eyed creature) and add this code as the Json Interface:

[{"constant":false,"inputs":[{"name":"contractAddress","type":"address"},{"name":"abiSignature","type":"bytes4"},{"name":"targetBlock","type":"uint256"}],"name":"scheduleCall","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"contractAddress","type":"address"},{"name":"abiSignature","type":"bytes4"},{"name":"targetBlock","type":"uint256"},{"name":"suggestedGas","type":"uint256"},{"name":"gracePeriod","type":"uint8"}],"name":"scheduleCall","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"getDefaultPayment","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"getDefaultFee","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"contractAddress","type":"address"},{"name":"abiSignature","type":"bytes4"},{"name":"targetBlock","type":"uint256"},{"name":"suggestedGas","type":"uint256"}],"name":"scheduleCall","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"callAddress","type":"address"}],"name":"getNextCallSibling","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"callAddress","type":"address"}],"name":"isKnownCall","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"basePayment","type":"uint256"}],"name":"getMinimumCallCost","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"contractAddress","type":"address"},{"name":"abiSignature","type":"bytes4"},{"name":"targetBlock","type":"uint256"},{"name":"suggestedGas","type":"uint256"},{"name":"gracePeriod","type":"uint8"},{"name":"basePayment","type":"uint256"}],"name":"scheduleCall","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"getMinimumCallCost","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"contractAddress","type":"address"},{"name":"abiSignature","type":"bytes4"},{"name":"targetBlock","type":"uint256"},{"name":"suggestedGas","type":"uint256"},{"name":"gracePeriod","type":"uint8"},{"name":"basePayment","type":"uint256"},{"name":"baseFee","type":"uint256"}],"name":"scheduleCall","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"basePayment","type":"uint256"},{"name":"baseFee","type":"uint256"}],"name":"getMinimumCallCost","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"getMinimumCallGas","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"getCallWindowSize","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"blockNumber","type":"uint256"}],"name":"getNextCall","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"getMinimumGracePeriod","outputs":[{"name":"","type":"uint256"}],"type":"function"}]

Tip: if you are on the test net, use the address 0xb8Da699d7FB01289D4EF718A55C3174971092BEf instead

Click on the green icon that you just added and then choose a function call under the Write to contract title. There will be multiple Schedule Call functions, choose the first one, that only has three fields:

  • contractAddress will be the address of the deployed crowdsale contract.
  • abiSignature will be 0x01cb3b20. You can figure out the signature for any function by trying to execute them but in the confirmation window, instead of typing your password, copy the code in the Data field. The function signature is the first 10 characters in bold.
  • targetBlock is the block number in which you want the function to be executed, read below to calculate an estimation.

The crowdsale contract specifies a deadline using a timestamp, but the Alarm clock currently schedules calls based on block numbers. Since ethereum has a block time of approximately 17 seconds, we need to compute a block number that is going to be probabilistically past the deadline. We can do this with the formula current_block_number + duration_in_minutes * 60 / 17 + buffer where buffer is a number of blocks that is sufficiently large that you can rely on it occurring after the crowdsale deadline. For short crowdsales less than a day in duration a buffer of 200 blocks should be sufficient. For durations closer to 30 days, you should probably pick a number closer to 5,000.

You can use the following chart for rough estimates for how many blocks to add to the current block to compute the targetBlock.

  • 1 hour duration (60 minutes): 212 blocks
  • 1 day duration (1440 minutes): 5082 blocks
  • 1 week duration (10,800 minutes): 38,117 blocks
  • 1 month duration (44,640 minutes): 157,553 blocks

On the Send field, you need to send enough ether to pay the transaction fee, plus some more to pay the scheduler. Any extra money sent will be refunded, so sending at least 0.25 ether will probably keep you on the safe side.

After that, just press execute and your call will be scheduled. There are no guarantees that someone will actually execute it, so you should check back after the deadline has passed to be sure.

响应式布局的宽度设置

参考bootstrap的标准:

/* 超小屏幕(手机,小于 768px) */
/* 没有任何媒体查询相关的代码,因为这在 Bootstrap 中是默认的(还记得 Bootstrap 是移动设备优先的吗?) */

/* 小屏幕(平板,大于等于 768px) */
@media (min-width: @screen-sm-min) { ... }

/* 中等屏幕(桌面显示器,大于等于 992px) */
@media (min-width: @screen-md-min) { ... }

/* 大屏幕(大桌面显示器,大于等于 1200px) */
@media (min-width: @screen-lg-min) { ... }

常见设置如下:

/* media */
/* 横屏 */
@media screen and (orientation:landscape){

}
/* 竖屏 */
@media screen and (orientation:portrait){

}
/* 窗口宽度<960,设计宽度=768 */
@media screen and (max-width:959px){

}
/* 窗口宽度<768,设计宽度=640 */
@media screen and (max-width:767px){

}
/* 窗口宽度<640,设计宽度=480 */
@media screen and (max-width:639px){

}
/* 窗口宽度<480,设计宽度=320 */
@media screen and (max-width:479px){

}
/* windows UI 贴靠 */
@media screen and (-ms-view-state:snapped){

}
/* 打印 */
@media print{

}

ethereum初探二:创建自己的加密货币

本文由以太坊官方文档翻译
有不正之处欢迎指正。可留言或者发送我的邮箱:i@tangc.me

转载此翻译需要注明来源本站。

代币

我们将要创建一个数字代币(Token). 在以太坊生态中的代币可以象征任何可替代的、可买卖的行为:货币,可信任节点,黄金凭证,各种借据凭证,游戏道具等等。由于所有的代币都通过通用的标准实现基本特点,这也就意味着你按此标准创造出来的代币就可以立马和以太坊钱包、其它轻钱包或者同样标准的合约相兼容。

最小可行代币

标准优秀的代币合约可能是相当复杂的,但是实质上一个很基础的代币可以浓缩成如下代码所示:


contract MyToken {
    /* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;

    /* Initializes contract with initial supply tokens to the creator of the contract */
    function MyToken(
        uint256 initialSupply
        ) {
        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens
    }

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        require(balanceOf[msg.sender] >= _value);           // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
        balanceOf[msg.sender] -= _value;                    // Subtract from the sender
        balanceOf[_to] += _value;                           // Add the same to the recipient
    }
}

代码THE CODE

当然,如果你只是想要复制粘贴一段完整的代码,那么你就用这个:

pragma solidity ^0.4.16;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    uint256 public totalSupply;

    // This creates an array with all balances
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // This generates a public event on the blockchain that will notify clients
    event Transfer(address indexed from, address indexed to, uint256 value);

    // This notifies clients about the amount burnt
    event Burn(address indexed from, uint256 value);

    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    function TokenERC20(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` in behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other account
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}

理解代码

Deploy New Contract

这样,让我们从最基本的开始。打开以太坊钱包,到 合约(CONTRACTS) 选项,然后在下面选择 部署新合约(DEPLOY NEW CONTRACT) ,在合约原始代码文本框里编写如下代码:

    contract MyToken {
        /* This creates an array with all balances */
        mapping (address => uint256) public balanceOf;
    }

一个 mapping 表示一个关联数组,一个拥有余额的地址。这个地址是一个基本十六进制的以太坊形态,当地址里的余额是整数时,可从0到115  quattuorvigintillion. 如果你不知道一个 quattuorvigintillion 是多少, 是很多个 vigintillions(译者:1后面跟63个0) 反正超过你想发行的代币数量。 关键词 public , 意思是这个变量是公开的,可以被区块链上的任何人访问,这就意味着所有的余额都是公开的。(其它应用客户端都可以显示这个余额了)Edit New Contract

如何你现在立刻发布你的这个合约,它可以工作但是好像并不是那么有用:它将会是一个可以查询任何地址上你发行代币余额的合约。但是你永远不会创建一个这么单一的代币,每个拥有它的人余额永远是0. 因此我们将要在启动处创建一些代码。 在最后大括号前,mapping下面一行加上如下代码:

    function MyToken() {
        balanceOf[msg.sender] = 21000000;
    }

注意 MyToken  这个方法必须和 contract MyToken 同名。这是非常重要的,并且如果你要重名一个,那个也要跟着变。这是一个很特别的方法,这个启动函数仅只执行一次,且在合约首次部署到以太坊主网络上。这个方法将设置合约的创建者拥有代币的全部,即拥有2100万个代币余额。

2100万这个选择我们更希望它是可任意设置的,你可以在代码里随意修改这个值。但是我们一个更好的方法,将这个数设置成一个变量参数。像这样:

    function MyToken(uint256 initialSupply) {
        balanceOf[msg.sender] = initialSupply;
    }

看一眼合约右边的列,你会看见 选择合约 一个下拉选择,选择 “MyToken” 你会看见一个 Constructor parameters 区域,这些都是代币合约的可改变参数,你可以在将来用同样的代码重复使用只需要改变几个值就可以。

Edit New Contract

现在你已经拥有一个有余额的功能性的合约,但是你好像还是没有办法去转移这个余额,它只能始终在创建者一个账户里。因此,我们现在将要实现这个功能,在最后大括号前加上如下代码:

    /* 发送代币 */
    function transfer(address _to, uint256 _value) {
        /* 增加、扣除余额 */
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }

这是一个很直接明了的方法:它有一个 接受者数量 作为参数,不管何时有人调用它,它将从调用者余额里扣除  _value 并在 _to 增加同样的余额数 _value . 可是立马有个显而易见的问题出现了: 当一个人想发送超过他账户里余额的数量时会发生什么?因为我们不想在这个特别的合约里去处理负债问题,我们将简单快速地做一个校验:如果发送者没有足够的资金那合约就停止执行。这也同样可以校验溢出问题从而避免大额再变成0.

为了阻止合约的执行,你可以用  return or throw 这种形式会花费很少的GAS 但这可能会让你更头疼,你对合约做的任何改变都会被记录保存. 另一方面“throw”将取消所有的合约执行,交易已经回滚但是发送者为GAS发送的以太币将失去. 但是由于钱包可以检测一个合约 抛出异常,所以它总是会显示一条警告 防止你的以太币被浪费掉。

    function transfer(address _to, uint256 _value) {
        /* 检测发送者是否有足够的余额 */
        require(balanceOf[msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]);

        /* 加减余额 */
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }

现在确实的是关于合同的一些基本信息。在不久的将来,这可以由一个代币注册处来实现,但是现在我们将直接把它们加入到合约中:

string public name;
string public symbol;
uint8 public decimals;

现在我们更新构造函数,以允许在开始时设置所有这些变量:

    /* Initializes contract with initial supply tokens to the creator of the contract */
    function MyToken(uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) {
        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
        decimals = decimalUnits;                            // Amount of decimals for display purposes
    }

最后,我们需要一些所谓的事件。这些特殊、空的函数,您可以调用它们来帮助像以太坊钱包这样的客户端去跟踪合约中发送的活动。事件应该以大写字母开头。在合约的开头我们需要提前声明事件:

    event Transfer(address indexed from, address indexed to, uint256 value);

然后你只需要在 “transfer” 这个方法中增加下面两行:

        /* Notify anyone listening that this transfer took place */
        Transfer(msg.sender, _to, _value);

至此,你的代币已经准备好了!

注意到注释了没?

你可能会问那些 @notice 和 @param 那些注释是什么? 那是 Natspec 一个自然语言规法的新兴标准, 它允许钱包向用户展示合约要做的语言描述。虽然目前很多钱包目前还不支持,但是这在将来会改变的。提前做好准备是个好事。

如何部署

如果你已经准备好了,打开以太坊钱包,到合约选项页。然后点击 部署新合约。

现在从上面的代码我们粘贴到 Solidity源码编辑器中,如果代码编译没有任何错误,你可以看到 选择一个合约 的下拉选择。我们点击下拉 选择 “MyToken” 这个合约. 在右侧列表的下面你会看到所有的关于你代币的个人定制化参数。你可以按你要求进行调整,但是为了这个教程我们建议您选择这些参数 :

supply:10000
name :随便起一个
symbol: %
decimal:2

你的软件应该是像这样的:

Ethereum Wallet Screenshot 2015-12-03 at 3.50.36 PM 10

滚动到页面的末尾,你会看到创建这个合约所需要的成本估计,你可以选择你愿意支付多少以太坊作为费用。超额的以太坊会返回给你,你可以保留默认的设置,点击发送部署。输入你的密码并等待几秒钟以便你的交易被记录。

Ethereum Wallet Screenshot 2015-12-03 at 3.50.36 PM 11

你将会被重定向到前端页面,这里你可以看到你创建合约的交易正在等待确认。点击被命名为 “Etherbase” 的账户(你的主账户)你会发现你的账户将显示你已经拥有了你刚刚创建的 100%股份。发送一些给你的一些朋友:
选择 发送
选择你想发送的货币(以太或者你刚创建的份额),将你朋友的地址粘贴到 To 下面文本框 并按下 发送。

Screen Shot 2015-12-03 at 9.48.15 AM

如果你把代币发送给一个朋友,他的钱包里是看不到任何东西的。这样因为以太坊钱包只追踪它所知道的代币。所以你必须手动添加这些。现在切换到 合约 界面 ,你会看到一个 你刚部署合约 链接。点击跳转页面,因为这是一个很简单的合约没有什么太多可做的,你只需要点击 复制地址,然后找个文本编辑器保存好地址,因为我们一会会用到它。

添加一个代币进行查看,到合约页面 然后点击 查看代币(Watch Token). 一个弹出窗口会出现,你需要粘贴刚才的合约地址到第一行。然后 token name, symbol and decimal number 这些会自动显示出来。但是如果不是,你可以设置你想要的 (它仅仅只影响在你当前钱包里的显示). 一旦做好这,你的钱包就会自动显示这个代币的余额,你就可以把它发送给其他人。

Ethereum Wallet Beta 4 Screen Shot 2015-12-03 at 9.44.42 AM

现在你有了属于自己的加密代币!代币可以作为价值交换 在你的社区,可以支付工作小时和其它绩效考核。但是,我们能否让代币具有内在价值,让它更有用呢?

提高代币价值

你可以在不触及一行代码的情况下部署整个加密代币,但是当你开始定制时真正的神奇正在发生。接下来的章节里功能建议,你可以加到你的代币里让它更适合你的需要。

更多的基础功能

你将注意到,在你基本代币合约中有一些更多的函数,像 approve, sendFrom 和其它函数. 这些函数用户您的代币和其它合约进行交互:如果你想卖一些代币给一个去中心化的交易所,只发送给他们一个地址是不够的。因为合约不能通过订阅时间来调用函数 。因此,对于合约,你首先应该批准他们可以从你的账户中移动的数量,然后通知他们让他们知道应该做的事 或者 做的动作 用 approveAndCall

因为许多这样的函数都不得不重新实现代币的传输,让它们转换一个内置函数是有意义的,让只能由合约自身调用。

    /* 内部转让,只能由本合约调用 */
    function _transfer(address _from, address _to, uint _value) internal {
        require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
        require (balanceOf[_from] >= _value);                // 校验发送者余额
        require (balanceOf[_to] + _value > balanceOf[_to]); // 校验溢出
        require(!frozenAccount[_from]);                     // 检验发送者是否被冻结
        require(!frozenAccount[_to]);                       // 检验接收者是否被冻结
        balanceOf[_from] -= _value;                         // 减去发送者余额
        balanceOf[_to] += _value;                           // 增加接收者余额
        Transfer(_from, _to, _value);
    }

现在你代币转让的所有功能可以自己做检查,然后用正确的参数调用 transfer . 注意,该方法把代币从任何其它账户转到另一个账户,不需要任何人权限的原因在于:它是一个内部函数,只有合同本身可以调用。 如果你添加任何函数调用它,要确保调用者有授权。

集中管理

所有 dapps 默认都是去中心化的,但这并不意味着他们不可以拥有某种集中管理。如果你想要,这完全可以。也许你想铸造更多的货币,也许你想禁止一些人使用你的货币,你可以添加任何这些特性. 但是,关键是你必须在开始就加上这些功能,所以所有代币持有者在决定拥有一个代币前已经知道这个代币所有的规则。

要实现这一点,你需要一个货币的中央控制者。这可以是一个简单的账户,但也可以是一个合约, 因此决定创造更多代币将取决于合约:如一个自治组织通过投票或者限制代币拥有者的权力。

为了实现那样的功能,我们将学习一个非常有用的合约属性:继承 。继承允许一个合约继承父合约的所有属性。而不需要再重新定义它们。这使得代码更简洁更重复易用。加上这些代码在第一行 在 contract MyToken {. 前面

    contract owned {
        address public owner;

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

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

        function transferOwnership(address newOwner) onlyOwner {
            owner = newOwner;
        }
    }

这就创建了一个基本合约,除了定义一些通用函数外没有做任何事。现在,下一步我们将文本 is owned 加到我们的合约中:

    contract MyToken is owned {
        /* the rest of the contract as usual */

这意味着在  MyToken 中的所有方法都可以访问 owner 和modifier onlyOwner. 这个合约还具有转移所有权的功能. 因为在启动时直接设置所有者可能会很有趣,你也可以将这些加入到构造函数里:

    function MyToken(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol,
        address centralMinter
        ) {
        if(centralMinter != 0 ) owner = centralMinter;
    }

中央铸币厂

假设你想要改变正在流通中代币数量。举个例子:当你的代币已经代表了一个区块链资产(黄金凭证、法币)时,你希望虚拟库存反映真实库存。这也可能是另一个案例:当货币持有者期望对代币进行价格控制,增发或者废除一些流通中的代币。

首先我们要加一个变量  totalSupply 来存储总数,并在构造函数里进行赋值。

    contract MyToken {
        uint256 public totalSupply;

        function MyToken(...) {
            totalSupply = initialSupply;
            ...
        }
        ...
    }

现在让我们添加一个新的功能,它将使所有者能够创建新的代币:

    function mintToken(address target, uint256 mintedAmount) onlyOwner {
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;
        Transfer(0, owner, mintedAmount);
        Transfer(owner, target, mintedAmount);
    }

请注意,在函数名的末尾有一个 onlyOwner 这意味着该函数将在编译时重写,以从我们之前定义的 modifier onlyOwner 中继承代码。该函数的代码将插入到 modifier 函数 并有一个下划线,这意味着该函数只能由设置为所有者的帐户调用。只需将其添加到与所有者修改器的合约中,您就可以创建更多的货币。

冻结资产

根据您的用例,您可能有对谁使用,谁不使用您的代币进行一些管理上的障碍。为此,您可以添加一个参数,使合同所有者可以冻结或解冻资产。

在合约中添加这个变量和函数。您可以将它们放到任何地方,但是我们建议您将映射与其他映射 和事件与其他事件放在一起。

    mapping (address => bool) public frozenAccount;
    event FrozenFunds(address target, bool frozen);

    function freezeAccount(address target, bool freeze) onlyOwner {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

有了这个代码,所有帐户默认都是未冻结的,但是所有者可以通过调用冻结帐户将任何帐户设置为冻结状态。不幸的是,冻结没有实际效果,因为我们没有给传递函数添加任何东西。我们现在就可以改变这点了:

    function transfer(address _to, uint256 _value) {
        require(!frozenAccount[msg.sender]);

现在任何冻结的账户都将保留其资金,但无法移动它们。默认情况下,所有帐户都是未冻结的,除非您冻结它们,但是您可以很容易地将该行为转换为白名单,您需要手动审批每个帐户。将frozenAccount重新命名为approvedAccount并更改最后一行:

        require(approvedAccount[msg.sender]);

自动买卖

到目前为止,您已经依赖于实用程序和信任来评估您的代币。但是,如果你想让这个代币的价值得到以太(或其他代币)的支持,就会创建一个基金,它可以以市场价自动买卖。

首先,让我们设置买卖的价格:

   uint256 public sellPrice;
    uint256 public buyPrice;

    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

这是可以接受的价格,不会经常改变,因为每一个新的价格变化将要求你执行一个交易和花费一点以太。如果你想要一个恒定的浮动价格,我们建议你调查标准的数据输入(standard data feeds)

下一步实现买卖功能:

    function buy() payable returns (uint amount){
        amount = msg.value / buyPrice;                    // 计算总额
        require(balanceOf[this] >= amount);               // 校验是否足够卖出
        balanceOf[msg.sender] += amount;                  // 给购买者加余额
        balanceOf[this] -= amount;                        // 给售出者减余额
        Transfer(this, msg.sender, amount);               // 执行事件反映交易
        return amount;                                    // ends function and returns
    }

    function sell(uint amount) returns (uint revenue){
        require(balanceOf[msg.sender] >= amount);         // checks if the sender has enough to sell
        balanceOf[this] += amount;                        // adds the amount to owner's balance
        balanceOf[msg.sender] -= amount;                  // subtracts the amount from seller's balance
        revenue = amount * sellPrice;
        require(msg.sender.send(revenue));                // sends ether to the seller: 这是非常重要的防止递归攻击
        Transfer(msg.sender, this, amount);               // executes an event reflecting on the change
        return revenue;                                   // ends function and returns
    }

注意这不会创建新的代币,而是更改合约的余额。合约可以同时拥有自己的代币和以太币以及合同所有者,当它可以设置价格,或者在某些情况下创建新的代币(如果可以的),它不能触及本来存储的代币和以太币。这个合约转移资金的唯一方式就是出售和购买。

备注:买卖的价格不是用 以太币 为单位的,而是用以太生态系统里的最小计量单位: wei (相当于欧元和美元的分,或者比特币的 satoshi)。一个ETH = 1000000000000000000 wei. 所以在设定价格的时候你要在结尾加18个0。

当创建合约时,要发送足够的ETH,这样它就可以在市场上买回所有的代币,否则你的合同就会破产,你的用户将无法出售他们的代币。

当然前面的例子描述的是一个单一集中的买家和卖家的合约。一个更有趣的合约会允许一个任何人出不同价格,或者从外部直接报价的市场。

AUTOREFILL

每次你在以太坊上发起一个交易时,你都需要给区块记账的矿工一笔费用,那样才能创建你的智能合约。虽然这可能在将来会改变,不过目前你还是要用ETH去支付,所有持有你代币的用户也同样需要。合约的代币持有者如果没有ETH会被卡住直到有足够的费用。但在一些情况下,你可能不想让用户考虑以太坊,区块链或者如何支付多少ETH,你可以让以太坊自动填充用户的余额。

为了做到这点,你需要创建一个变量,该变量有个阈值并且有个函数可以更改。如果你不知道任何值,将它设置成  5 finney (0.005 ether).

 

    uint minBalanceForAccounts;

    function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
         minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
    }

然后,把这行加到 transfer  函数中,这样发送者就能得到退款:

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        ...
        if(msg.sender.balance < minBalanceForAccounts)
            sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
    }

你也可以更改它让这笔费用由发送方支付给接收者:

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        ...
        if(_to.balance<minBalanceForAccounts)
            _to.send(sell((minBalanceForAccounts - _to.balance) / sellPrice));
    }

这会确保任何接收代币的用户没必要支付费用。

PROOF OF WORK

There are some ways to tie your coin supply to a mathematical formula. One of the simplest ways would be to make it a “merged mining” with ether, meaning that anyone who finds a block on ethereum would also get a reward from your coin, given that anyone calls the reward function on that block. You can do it using the special keyword coinbase that refers to the miner who finds the block.

    function giveBlockReward() {
        balanceOf[block.coinbase] += 1;
    }

It’s also possible to add a mathematical formula, so that anyone who can do math can win a reward. On this next example you have to calculate the cubic root of the current challenge gets a point and the right to set the next challenge:

    uint currentChallenge = 1; // Can you figure out the cubic root of this number?

    function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {
        require(answerToCurrentReward**3 == currentChallenge); // If answer is wrong do not continue
        balanceOf[msg.sender] += 1;         // Reward the player
        currentChallenge = nextChallenge;   // Set the next challenge
    }

Of course while calculating cubic roots can be hard for someone to do on their heads, they are very easy with a calculator, so this game could be easily broken by a computer. Also since the last winner can choose the next challenge, they could pick something they know and therefore would not be a very fair game to other players. There are tasks that are easy for humans but hard for computers but they are usually very hard to code in simple scripts like these. Instead a fairer system should be one that is very hard for a computer to do, but isn’t very hard for a computer to verify. A great candidate would be to create a hash challenge where the challenger has to generate hashes from multiple numbers until they find one that is lower than a given difficulty.

This process was first proposed by Adam Back in 1997 as Hashcash and then was implemented in Bitcoin by Satoshi Nakamoto as Proof of work in 2008. Ethereum was launched using such system for its security model, but is planning to move from a Proof of Work security model into a mixed proof of stake and betting system called Casper.

But if you like Hashing as a form of random issuance of coins, you can still create your own ethereum based currency that has a proof of work issuance:

    bytes32 public currentChallenge;                         // The coin starts with a challenge
    uint public timeOfLastProof;                             // Variable to keep track of when rewards were given
    uint public difficulty = 10**32;                         // Difficulty starts reasonably low

    function proofOfWork(uint nonce){
        bytes8 n = bytes8(sha3(nonce, currentChallenge));    // Generate a random hash based on input
        require(n >= bytes8(difficulty));                   // Check if it's under the difficulty

        uint timeSinceLastProof = (now - timeOfLastProof);  // Calculate time since last reward was given
        require(timeSinceLastProof >=  5 seconds);         // Rewards cannot be given too quickly
        balanceOf[msg.sender] += timeSinceLastProof / 60 seconds;  // The reward to the winner grows by the minute

        difficulty = difficulty * 10 minutes / timeSinceLastProof + 1;  // Adjusts the difficulty

        timeOfLastProof = now;                              // Reset the counter
        currentChallenge = sha3(nonce, currentChallenge, block.blockhash(block.number - 1));  // Save a hash that will be used as the next proof
    }

Also change the Constructor function (the one that has the same name as the contract, which is called at first upload) to add this line, so the difficulty adjustment will not go crazy:

    timeOfLastProof = now;

Once the contract is online, select the function “Proof of work”, add your favorite number on the nonce field and try to execute it. If the confirmation window gives a red warning saying “Data can’t be execute” go back and pick another number until you find one that allows the transaction to go forward: this process is random. If you find one you will be awarded 1 token for every minute that has passed since the last reward was given, and then the challenge difficulty will be adjusted up or down to target an average of 10 minutes per reward.

This process of trying to find the number that will give you a reward is what is called mining: if difficulty rises it can be very hard to find a lucky number, but it will always be easy to verify that you found one.

Improved Coin

FULL COIN CODE

If you add all the advanced options, this is how the final code should look like:

Advanced Token

pragma solidity ^0.4.16;

contract owned {
    address public owner;

    function owned() public {
        owner = msg.sender;
    }

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

    function transferOwnership(address newOwner) onlyOwner public {
        owner = newOwner;
    }
}

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }

contract TokenERC20 {
    // Public variables of the token
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    // 18 decimals is the strongly suggested default, avoid changing it
    uint256 public totalSupply;

    // This creates an array with all balances
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // This generates a public event on the blockchain that will notify clients
    event Transfer(address indexed from, address indexed to, uint256 value);

    // This notifies clients about the amount burnt
    event Burn(address indexed from, uint256 value);

    /**
     * Constrctor function
     *
     * Initializes contract with initial supply tokens to the creator of the contract
     */
    function TokenERC20(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) public {
        totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[msg.sender] = totalSupply;                // Give the creator all initial tokens
        name = tokenName;                                   // Set the name for display purposes
        symbol = tokenSymbol;                               // Set the symbol for display purposes
    }

    /**
     * Internal transfer, only can be called by this contract
     */
    function _transfer(address _from, address _to, uint _value) internal {
        // Prevent transfer to 0x0 address. Use burn() instead
        require(_to != 0x0);
        // Check if the sender has enough
        require(balanceOf[_from] >= _value);
        // Check for overflows
        require(balanceOf[_to] + _value > balanceOf[_to]);
        // Save this for an assertion in the future
        uint previousBalances = balanceOf[_from] + balanceOf[_to];
        // Subtract from the sender
        balanceOf[_from] -= _value;
        // Add the same to the recipient
        balanceOf[_to] += _value;
        Transfer(_from, _to, _value);
        // Asserts are used to use static analysis to find bugs in your code. They should never fail
        assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
    }

    /**
     * Transfer tokens
     *
     * Send `_value` tokens to `_to` from your account
     *
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    /**
     * Transfer tokens from other address
     *
     * Send `_value` tokens to `_to` in behalf of `_from`
     *
     * @param _from The address of the sender
     * @param _to The address of the recipient
     * @param _value the amount to send
     */
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    /**
     * Set allowance for other address
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     */
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /**
     * Set allowance for other address and notify
     *
     * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it
     *
     * @param _spender The address authorized to spend
     * @param _value the max amount they can spend
     * @param _extraData some extra information to send to the approved contract
     */
    function approveAndCall(address _spender, uint256 _value, bytes _extraData)
        public
        returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    /**
     * Destroy tokens
     *
     * Remove `_value` tokens from the system irreversibly
     *
     * @param _value the amount of money to burn
     */
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    /**
     * Destroy tokens from other account
     *
     * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
     *
     * @param _from the address of the sender
     * @param _value the amount of money to burn
     */
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }
}

/******************************************/
/*       ADVANCED TOKEN STARTS HERE       */
/******************************************/

contract MyAdvancedToken is owned, TokenERC20 {

    uint256 public sellPrice;
    uint256 public buyPrice;

    mapping (address => bool) public frozenAccount;

    /* This generates a public event on the blockchain that will notify clients */
    event FrozenFunds(address target, bool frozen);

    /* Initializes contract with initial supply tokens to the creator of the contract */
    function MyAdvancedToken(
        uint256 initialSupply,
        string tokenName,
        string tokenSymbol
    ) TokenERC20(initialSupply, tokenName, tokenSymbol) public {}

    /* Internal transfer, only can be called by this contract */
    function _transfer(address _from, address _to, uint _value) internal {
        require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
        require (balanceOf[_from] >= _value);               // Check if the sender has enough
        require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
        require(!frozenAccount[_from]);                     // Check if sender is frozen
        require(!frozenAccount[_to]);                       // Check if recipient is frozen
        balanceOf[_from] -= _value;                         // Subtract from the sender
        balanceOf[_to] += _value;                           // Add the same to the recipient
        Transfer(_from, _to, _value);
    }

    /// @notice Create `mintedAmount` tokens and send it to `target`
    /// @param target Address to receive the tokens
    /// @param mintedAmount the amount of tokens it will receive
    function mintToken(address target, uint256 mintedAmount) onlyOwner public {
        balanceOf[target] += mintedAmount;
        totalSupply += mintedAmount;
        Transfer(0, this, mintedAmount);
        Transfer(this, target, mintedAmount);
    }

    /// @notice `freeze? Prevent | Allow` `target` from sending & receiving tokens
    /// @param target Address to be frozen
    /// @param freeze either to freeze it or not
    function freezeAccount(address target, bool freeze) onlyOwner public {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

    /// @notice Allow users to buy tokens for `newBuyPrice` eth and sell tokens for `newSellPrice` eth
    /// @param newSellPrice Price the users can sell to the contract
    /// @param newBuyPrice Price users can buy from the contract
    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

    /// @notice Buy tokens from contract by sending ether
    function buy() payable public {
        uint amount = msg.value / buyPrice;               // calculates the amount
        _transfer(this, msg.sender, amount);              // makes the transfers
    }

    /// @notice Sell `amount` tokens to contract
    /// @param amount amount of tokens to be sold
    function sell(uint256 amount) public {
        require(this.balance >= amount * sellPrice);      // checks if the contract has enough ether to buy
        _transfer(msg.sender, this, amount);              // makes the transfers
        msg.sender.transfer(amount * sellPrice);          // sends ether to the seller. It's important to do this last to avoid recursion attacks
    }
}

部署

Scroll down and you’ll see an estimated cost for deployment. If you want you can change the slider to set a smaller fee, but if the price is too below the average market rate your transaction might take longer to pick up. Click Deploy and type your password. After a few seconds you’ll be redirected to the dashboard and under Latest transactions you’ll see a line saying “creating contract”. Wait for a few seconds for someone to pick your transaction and then you’ll see a slow blue rectangle representing how many other nodes have seen your transaction and confirmed them. The more confirmations you have, the more assurance you have that your code has been deployed.

Created Token

点击 管理页面 你会的得到世界上最简单的中央银行仪表台,在这你可以用创建的货币做任何你想做的事。

在合同的左边,你有所有的选项和功能,你可以免费阅读合约的信息,如果您的代币拥有一个所有者,它将在这里显示它的地址。复制该地址并将其粘贴到余额中,它将显示任何帐户的余额(余额也会自动显示在任何有代币的帐户页面上)。

在右边,在Write to Contract下,你将看到所有可以用来改变或改变区块链的函数。这些操作会花费gas 。如果你创造了一个允许你铸造新硬币的合同,你应该有一个叫做“mint Token”的功能。选择它。

Manage central dollar

Select the address where those new currencies will be created and then the amount (if you have decimals set at 2, then add 2 zeros after the amount, to create the correct quantity). On Execute from select the account that set as owner, leave the ether amount at zero and then press execute.

After a few confirmations, the recipient balance will be updated to reflect the new amount. But your recipient wallet might not show it automatically: in order to be aware of custom tokens, the wallet must add them manually to a watch list. Copy your token address (at the admin page, press copy address) and send that to your recipient. If they haven’t already they should go to the contracts tab, press Watch Token and then add the address there. Name, symbols and decimal amounts displayed can be customized by the end user, especially if they have other tokens with similar (or the same) name. The main icon is not changeable and users should pay attention to them when sending and receiving tokens to ensure they are dealing with the real deal and not some copycat token.

add token

Using your coin

Once you’ve deployed your tokens, they will be added to your list of watched tokens, and the total balance will be shown on your account. In order to send tokens, just go to the Send tab and select an account that contains tokens. The tokens the account has will be listed just under Ether. Select them and then type the amount of tokens you want to send.

If you want to add someone else’s token, just go to the Contracts tab and click Watch token. For example, to add the Unicorn (🦄) token to your watch list, just add the address 0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7 and the remaining information will be loaded automatically. Click Ok and your token will be added.

Invisible Unicorns

Unicorn tokens are memorabilia created exclusively for those who have donated to the address 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359 that is controlled by the Ethereum Foundation. For more information about them read it here

原文地址:https://www.ethereum.org/token