数字货币的基本架构

【艺观心相】一个行业是否进入现代化,有两个标志,1、是否获得新技术的赋能,2、是否获得金融的赋能。从这两个角度看,套用现在的一句流行语:任何行业都值得再用 Fin+Tech 来重做一遍。 
区块链是 fintech的一个融合实例,在价值互联网的发展上,区块链代表着一种底层的数学思维,创造信任,赋能新经济。

马云曾经提出:传统金融在过去服务了20%的人,赚了80%的利润,而现代金融未来必须服务80%的客户,赚20%的利润。我一直相信,看一个技术的创新不是看它能够带来多少利润、多少收入或者多大规模的应用,而是看它给多少人解决多大的问题、多大的困难。

对于立志要用 fintech 重做行业的各位程序猿们,首先来学习和理解一下什么是区块链下的数字货币发行,然后再手把手的走一遍,如何发行 token。

一、数字货币发行与管理

简介

该智能合约实现一个简单的商业应用案例,即数字货币的发行与转账。在这之中一共分为三种角色:中央银行,商业银行,企业。其中中央银行可以发行一定数量的货币,企业之间可以进行相互的转账。主要实现如下的功能:

  • 初始化中央银行及其发行的货币数量
  • 新增商业银行,同时央行并向其发行一定数量的货币
  • 新增企业
  • 商业银行向企业转给一定数量的数字货币
  • 企业之间进行相互的转账
  • 查询企业、银行、交易信息

主要函数

  • init:初始化中央银行,并发行一定数量的货币;
  • invoke:调用合约内部的函数;
  • query:查询相关的信息;
  • createBank:新增商业银行,同时央行向其发行一定数量的货币;
  • createCompany:新增企业;
  • issueCoin:央行再次发行一定数量的货币(归于交易);
  • issueCoinToBank:央行向商业银行转一定数量的数字货币(归于交易);
  • issueCoinToCp:商业银行向企业转一定数量的数字货币(归于交易行为);
  • transfer:企业之间进行相互转账(归于交易行为);
  • getCompanys:获取所有的公司信息,如果企业个数大于10,先访问前10个;
  • getBanks:获取所有的商业银行信息,如果商业银行个数大于10,先访问前 10 个
  • getTransactions:获取所有的交易记录 如果交易个数大于10,先访问前 10 个;
  • getCompanyById:获取某家公司信息;
  • getBankById:获取某家银行信息;
  • getTransactionBy:获取某笔交易记录;
  • writeCenterBank:修改央行信息;
  • writeBank:修改商业银行信息;
  • writeCompany:修改企业信息;
  • writeTransaction:写入交易信息。

数据结构设计

  • centerBank 中央银行
    • Name:名称
    • TotalNumber:发行货币总数额
    • RestNumber:账户余额
    • ID:ID固定为 0
  • bank 商业银行
    • Name:名称
    • TotalNumber:收到货币总数额
    • RestNumber:账户余额
    • ID:银行 ID
  • company 企业
    • Name:名称
    • Number:账户余额
    • ID:企业 ID
  • transaction 交易内容
    • FromType:发送方角色 //centerBank:0,Bank:1,Company:2
    • FromID:发送方 ID
    • ToType:接收方角色 //Bank:1,Company:2
    • ToID:接收方 ID
    • Time:交易时间
    • Number:交易数额
    • ID:交易 ID

接口设计

init

request 参数:

args[0] 银行名称
args[1] 初始化发布金额

response 参数:

{"Name":"XXX","TotalNumber":"0","RestNumber":"0","ID":"XX"}

createBank

request 参数:

args[0] 银行名称

response 参数:

{"Name":"XXX","TotalNumber":"0","RestNumber":"0","ID":"XX"}

createCompany

request 参数:

args[0] 公司名称

response 参数:

{"Name":"XXX","Number":"0","ID":"XX"}

issueCoin

request 参数:

args[0] 再次发行货币数额

response 参数:

{"FromType":"0","FromID":"0","ToType":"0","ToID":"0","Time":"XX","Number":"XX","ID":"XX"}

issueCoinToBank

request 参数:

args[0] 商业银行ID
args[1] 转账数额

response 参数:

{"FromType":"0","FromID":"0","ToType":"1","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}

issueCoinToCp

request 参数:

args[0] 商业银行ID
args[1] 企业ID
args[2] 转账数额

response 参数:

{"FromType":"1","FromID":"XX","ToType":"2","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}

transfer

request 参数:

args[0] 转账用户ID
args[1] 被转账用户ID
args[2] 转账余额

response 参数:

{"FromType":"2","FromID":"XX","ToType":"2","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}

getBanks

response 参数

[{"Name":"XXX","Number":"XX","ID":"XX"},{"Name":"XXX","Number":"XX","ID":"XX"},...]

getCompanys

response 参数

[{"Name":"XXX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"},{"Name":"XXX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"},...]

getTransactions

response 参数

[{"FromType":"XX","FromID":"XX","ToType":"XX","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"},{"FromType":"XX","FromID":"XX","ToType":"XX","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"},...]

getCenterBank

response 参数

[{"Name":"XX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"}]

getBankById

request 参数

args[0] 商业银行ID

response 参数

[{"Name":"XX","TotalNumber":"XX","RestNumber":"XX","ID":"XX"}]

getCompanyById

request 参数

args[0] 企业ID

response 参数

[{"Name":"XXX","Number":"XX","ID":"XX"}]

getTransactionById

request 参数

args[0] 交易ID

response 参数

{"FromType":"XX","FromID":"XX","ToType":"XX","ToID":"XX","Time":"XX","Number":"XX","ID":"XX"}

writeCenterBank

request 参数

CenterBank

response 参数

err  nil 为成功

writeBank

request 参数

Bank

response 参数

err  nil 为成功

writeCompany

request 参数

Company

response 参数

err  nil 为成功

writeTransaction

request 参数

Transaction

response 参数

“` err nil 为成功 ···

二、以以太坊ERC20 Token为例:手把手教你发行自己的Token(币)

一、安装环境

1、在chrome浏览器安装MetaMask插件

安装完成后,你会看到chrome右上角有一个新的图标。

安装MetaMask插件

2、新建钱包

输入密码,保存助记词

3、使用测试网络

由于使用以太坊主网络发布智能合约需要真正的ETH,在这里我使用的是测试网络(如果要在主网络发布一个Token,只需要选择主网络)

切换到测试网络

4、领取测试用的ETH

点击“Buy”

MetaMask插件

点击”ROPSTEN TEST FAUCET”

MetaMask插件

点击绿色的按钮“request 1 ether from faucet”,就可以领取1ETH

MetaMask Ether Faucet

或者在这个网址领取3个测试用的ETH

Ethereum Ropsten Faucet

如果两个都领取的话,那么此时你总共就有4个ETH了

MetaMask插件

二、编辑代码

由于以太坊智能合约使用的是Solidity语言,所以需要使用Solidity开发。

在这里我使用的是Remix,这是一个在线的Solidity编译器。

Remix是一个在线的Solidity编译器

1、拷贝代码

代码可以从这个网址拷,然后粘贴到Remix左边的代码编辑区就可以了,也可以直接拷贝下面的代码:

pragma solidity ^0.4.8;
contract Token{
// token总量,默认会为public变量生成一个getter函数接口,名称为totalSupply().
uint256 public totalSupply;
/// 获取账户_owner拥有token的数量 
function balanceOf(address _owner) constant returns (uint256 balance);
//从消息发送者账户中往_to账户转数量为_value的token
function transfer(address _to, uint256 _value) returns (bool success);
//从账户_from中往账户_to转数量为_value的token,与approve方法配合使用
function transferFrom(address _from, address _to, uint256 _value) returns   
(bool success);
//消息发送账户设置账户_spender能从发送账户中转出数量为_value的token
function approve(address _spender, uint256 _value) returns (bool success);
//获取账户_spender可以从账户_owner中转出token的数量
function allowance(address _owner, address _spender) constant returns 
(uint256 remaining);
//发生转账时必须要触发的事件 
event Transfer(address indexed _from, address indexed _to, uint256 _value);
//当函数approve(address _spender, uint256 _value)成功执行时必须触发的事件
event Approval(address indexed _owner, address indexed _spender, uint256 
_value);
}
contract StandardToken is Token {
function transfer(address _to, uint256 _value) returns (bool success) {
//默认totalSupply 不会超过最大值 (2^256 - 1).
//如果随着时间的推移将会有新的token生成,则可以用下面这句避免溢出的异常
//require(balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]);
require(balances[msg.sender] >= _value);
balances[msg.sender] -= _value;//从消息发送者账户中减去token数量_value
balances[_to] += _value;//往接收账户增加token数量_value
Transfer(msg.sender, _to, _value);//触发转币交易事件
return true;
}
function transferFrom(address _from, address _to, uint256 _value) returns 
(bool success) {
//require(balances[_from] >= _value && allowed[_from][msg.sender] >= 
// _value && balances[_to] + _value > balances[_to]);
require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value);
balances[_to] += _value;//接收账户增加token数量_value
balances[_from] -= _value; //支出账户_from减去token数量_value
allowed[_from][msg.sender] -= _value;//消息发送者可以从账户_from中转出的数量减少_value
Transfer(_from, _to, _value);//触发转币交易事件
return true;
}
function balanceOf(address _owner) constant returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) returns (bool success)   
{
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
return allowed[_owner][_spender];//允许_spender从_owner中转出的token数
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
}
contract HumanStandardToken is StandardToken { 
/* Public variables of the token */
string public name;                   //名称: eg Simon Bucks
uint8 public decimals;               //最多的小数位数,How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether.
string public symbol;               //token简称: eg SBX
string public version = 'H0.1';    //版本
function HumanStandardToken(uint256 _initialAmount, string _tokenName, uint8 _decimalUnits, string _tokenSymbol) {
balances[msg.sender] = _initialAmount; // 初始token数量给予消息发送者
totalSupply = _initialAmount;         // 设置初始总量
name = _tokenName;                   // token名称
decimals = _decimalUnits;           // 小数位数
symbol = _tokenSymbol;             // token简称
}
/* Approves and then calls the receiving contract */
function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
//call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this.
//receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData)
//it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead.
require(_spender.call(bytes4(bytes32(sha3("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData));
return true;
}
}

智能合约代码

2、设置参数

:大家注意在网页右边的”Run”中的“Environment”设置为“Injected Web3”,如果遇到无法选择“Injected Web3”的情况,请刷新一下。

“Create”中填入创建的Token参数,

第一个参数是Token的数量;

第二个参数是Token的全称;

第三个参数是Token的精度,即Token最小为小数点后几位;

第四个参数是Token的符号;

例如:100000000,”zhongxh’s test token”,8,”ZTT”

上述例子代表创建的Token的全称是”zhongxh’s test token”,符号是“ZTT”, ZTT的最大精度为小数点后8位,即最小是0.000000001ZTT,总共有1亿份,那么总共有100000000 * 10^(-8)ZTT,即1ZTT

点击“Create”按钮

以太坊智能合约的编辑、编译和创建

3、提交智能合约

点击“Create”后,就可以看到MetaMask弹出的“CONFIRM TRANSACTION”的页面,这个页面是用来确认发送的,点击“SUBMIT”提交。

以太坊智能合约的编辑、编译和创建

提交智能合约

点击“SENT”,可以看到刚刚创建的合约。

点击该智能合约

红色框里面是刚刚创建的智能合约

点击刚刚发布的智能合约地址,就会调转到Etherscan(以太坊的区块浏览器),耐性等待智能合约的创建。

以太坊的区块浏览器

4、发布智能合约

Contract address为上面创建的合约地址;

Contract name为在Remix中选择创建的合约名称HumanStandardToken;

Compiler版本需选择在Remix进行合约创建时选择的编译器版本一致;

是否优化Optimization也需要与Remix发布合约时保持一致;

在“Enter the Solidity Contract Code below”中填入之前在Remix中的solidity合约代码;

在“Constructor Arguments ABI-encoded”中填入构造函数参数(100000000,”zhongxh’s test token”,8,”ZTT”)的ABI编码,这个编码可以在创建合约的transaction的Input data中找出,如下图蓝色选中的部分即是构造函数参数的ABI编码(b0029之后所有的数据)。

请注意:以上都要和Remix发布合约时保持一致,否则没办法生成一个有效的ERC20 Token。

点击Verify and Publish,如果验证通过了就会出现如下页面:

刷新一下页面,点击Contract Source标签,就能看到上传的合约源代码了:

三、验证

因为Metamask不支持toekn的发送,我们切换到MyEtherWallet钱包。

需要注意的是,MyEtherWallet也需要切换到Rostpen网络

切换到Rostpen网络

接下来切换到“发送以太币/发送代币”

解锁以太坊钱包

在这里我选择用私钥的方式解锁钱包,私钥可以从Metamask获得

点击Metamask右上角的“…”,然后点击”Export Private Key “,输入密码,即可获得私钥

导出私钥

将私钥拷到MyEtherWallet,就可以解锁你的钱包了。

点击右下角的“Load Token Balance”

钱包已解锁

右下角显示的正是刚刚创建的Token,也就是ZTT,余额为1,这表明我的ERC20 Token发布成功了!

ERC20 Token发布成功了

四、后话

没想到发布一个以太坊的ERC20 token这么简单,这么方便,这也是为什么那么多个区块链项目利用以太坊进行ICO(众筹)的原因。

分类: 区块链

0 条评论

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注