用于创建令牌的全栈dapp
该dapp实现了最简单的加密货币形式,该形式使用ERC-20标准来定义可以创建并发送给他人的令牌。本教程旨在使用studio.ethereum.org上的在线IDE 并选择“ Coin”模板来进行。
智能合约
该合约仅允许其创建者创建新硬币(可以使用不同的发行方案)。任何人都可以彼此发送硬币,而无需使用用户名和密码进行注册,您所需要的只是一个以太坊密钥对。
该行address public minter;
声明一个address
type的状态变量。该address
类型是一个160位的值,它不允许任何算术运算。它适用于存储合约地址或属于外部帐户的密钥对的公共半部分的哈希。
关键字public
自动生成一个函数,该函数使您可以从合约外部访问状态变量的当前值。没有此关键字,其他合约将无法访问该变量。由编译器生成的函数的代码是等效于以下(忽略external
和view
现在):
function minter() external view returns (address) { return minter; }
您可以自己添加上述函数,但是您将拥有一个具有相同名称的函数和一个状态变量。这不是必需的,编译器会为您解决。
下一行mapping (address => uint) public balances;
还创建了一个公共状态变量,但是它是一个更复杂的数据类型。该映射类型映射地址无符号整数。
您可以将映射视为哈希表,该哈希表实际上已初始化,因此每个可能的键从一开始就存在,并映射到其字节表示全为零的值。无法获取映射的所有键的列表,也无法获取所有值的列表。记录您添加到映射中的内容,或在不需要它的上下文中使用它。更好的是,保留一个列表,或使用更合适的数据类型。
在映射的情况下,由关键字创建的getter函数public
更加复杂。看起来如下:
function balances(address _account) external view returns (uint) {
return balances[_account];
}
您可以使用此功能查询单个帐户的余额。
该行event Sent(address from, address to, uint amount);
声明一个“ 事件 ”,该事件在函数的最后一行发出send
。以太坊客户端(例如Web应用程序)可以侦听在区块链上发出的这些事件,而无需花费太多成本。一旦该事件被发出,侦听器能够接收参数from
,to
并且amount
,这使得它可以跟踪交易。
该构造函数是在创建合约的过程中运行一个特殊的功能,你不能事后调用它。在这种情况下,它将永久存储创建合约的人的地址。该msg
变量(连同tx
和block
)是一种特殊的全局变量包含属性允许访问blockchain。msg.sender
始终是当前(外部)函数调用来自的地址。
构成合约以及用户和合约可以调用的功能是mint
和send
。
该mint
功能将一定数量的新创建的硬币发送到另一个地址。在需要函数调用定义还原所有变更如果没有满足的条件。在此示例中,require(msg.sender == minter);
确保仅合约的创建者可以调用mint
,并require(amount < 1e60);
确保最大数量的令牌,否则,将来可能会导致溢出错误。
任何人都可以使用send
(已经拥有其中一些硬币)将硬币发送给其他任何人。如果发件人没有足够的硬币要发送,则require
呼叫将失败,并为发件人提供适当的错误消息字符串。
网络应用
本教程不涉及HTML或CSS,因为它不是web3特定的,除了JavaScript操纵的元素ID。许多JavaScript代码遵循面向对象JavaScript的标准模式,因此本教程重点介绍web3js的特定部分。
首先创建智能合约的实例,将其作为属性传递,使web3js与其进行交互。
function Coin(Contract) {
this.web3 = null;
this.instance = null;
this.Contract = Contract;
}
初始化Coin
对象并创建web3js库的实例,并将Metamask传递为合约或您可以在IDE设置中配置的任何其他网络终结点的提供程序。然后,初始化函数使用web3js合约对象定义合约的接口,然后为该对象定义合约实例的地址Coin
。
Coin.prototype.init = function() {
this.web3 = new Web3(
(window.web3 && window.web3.currentProvider) ||
new Web3.providers.HttpProvider(this.Contract.endpoint));
var contract_interface = this.web3.eth.contract(this.Contract.abi);
this.instance = contract_interface.at(this.Contract.address);
};
添加其他JavaScript样板来创建Coin
上面定义的对象的实例,并将用于与合约进行交互的功能绑定到HTML中定义的按钮:
Coin.prototype.bindButtons = function() {
var that = this;
$(document).on("click", "#button-create", function() {
that.createTokens();
});
$(document).on("click", "#button-check", function() {
that.showAddressBalance();
});
}
Coin.prototype.onReady = function() {
this.bindButtons();
this.init();
this.main();
};
var coin = new Coin(Contracts['Coin']);
$(document).ready(function() {
coin.onReady();
});
创建当某人单击“发送”按钮时触发的功能,该按钮创建指定数量的新硬币代币并将其发送到指定的地址。在执行此操作之前,它将使用我们稍后创建的两个实用程序功能来检查地址和金额是否有效。Web3js通过访问合约实例来提供对智能合约中功能的访问。例如,下面的函数使用调用mint
合约的函数来创建令牌this.instance.mint
,传递所需的变量,等待确认或返回错误。
Coin.prototype.createTokens = function() {
var that = this;
var address = $("#create-address").val();
var amount = $("#create-amount").val();
console.log(amount);
if(!isValidAddress(address)) {
console.log("Invalid address");
return;
}
if(!isValidAmount(amount)) {
console.log("Invalid amount");
return;
}
this.instance.mint(address, amount, { from: window.web3.eth.accounts[0], gas: 100000, gasPrice: 100000, gasLimit: 100000 },
function(error, txHash) {
if(error) {
console.log(error);
}
else {
that.waitForReceipt(txHash, function(receipt) {
if(receipt.status) {
$("#create-address").val("");
$("#create-amount").val("");
}
else {
console.log("error");
}
});
}
}
)
}
waitForReceipt
上面调用的函数使用web3js函数getTransactionReceipt
等待交易的收据,如果第一次尝试失败,则再次尝试:
Coin.prototype.waitForReceipt = function(hash, cb) {
var that = this;
this.web3.eth.getTransactionReceipt(hash, function(err, receipt) {
if (err) {
error(err);
}
if (receipt !== null) {
if (cb) {
cb(receipt);
}
} else {
window.setTimeout(function() {
that.waitForReceipt(hash, cb);
}, 2000);
}
});
}
创建当有人单击“检查余额”按钮时调用的函数。该函数采用指定的地址值,并使用先前使用的相同实用程序功能检查它是否为有效地址。然后,该函数调用该getBalance
函数,该函数又调用该合约balances
实例的函数Coin
,将地址值传递给它,并返回结果或错误。
Coin.prototype.showAddressBalance = function(hash, cb) {
var that = this;
var address = $("#balance-address").val();
if(!isValidAddress(address)) {
console.log("Invalid address");
return;
}
this.getBalance(address, function(error, balance) {
if(error) {
console.log(error)
}
else {
console.log(balance.toNumber());
$("#message").text(balance.toNumber());
}
})
}
Coin.prototype.getBalance = function(address, cb) {
this.instance.balances(address, function(error, result) {
cb(error, result);
})
}
这是两个用于检查有效地址和金额的实用程序功能:
function isValidAddress(address) {
return /^(0x)?[0-9a-f]{40}$/i.test(address);
}
function isValidAmount(amount) {
return amount > 0 && typeof Number(amount) == 'number';
}
这就是所有代码。要查看运行中的dapp,请点击编译,然后在合约文件的披露三角下找到Deploy,然后打开Preview选项卡以查看dapp的前端。