以太坊介绍
Solidity语言介绍
Solidity编程教学
合约开发框架Truffle
本地以太坊模拟
一些很好的合约代码范例
一 环境搭建
Install and start Ganache as a local Ethereum test node.
npm install -g truffle
npm install -g solc solc-cli --save-dev
truffle compile
带参数 --all
truffle compile --all
Run migrations
truffle migrate
带参数 --reset
truffle migrate --reset
输出到文件
sudo truffle migrate --reset >> ./log/migrate.log && echo "--------add payable----" >>./log/migrate.log
重新部署,就是发一个新的合约,旧的合约还在链上
以太坊地址,20 个字节,使用uint160来存储的,而且他们之间可以相互强制转换:
address payable ap = address(uint160(addr));
地址之间是可以直接进行比较大小:<=, <, >= , >,==, !=
地址类型有两种形式,他们大致相同:
int/uint
变长的有符号或无符号整形。支持以8递增,uint8到uint256,uint默认为uint256。
键值对映射,例如:mapping(address => uint)
struct Solidity {
address addr;
uint amount;
}
三种类型:storage,memory,calldata。
默认存储类型
永久存储,费用相对较高。
临时变量存储,可以理解为栈空间
合约与合约之间以及合约与其子类之间的访问权限。类比 Java 的public、protected、private,用法相似,略有不同。
该修饰符修饰变量既可以被 internal 方式的访问,也可以通过 external 方式的调用。可以理解为既能够被内部合约访问,也能被外部合约调用。
能够被外部合约调用
只允许通过 internal 方式调用,不能被外部合约调用。
与 internal
一样,都不能被外部合约访问,唯一不同的是 private
函数不能被子类调用。
在 Solidity v4.17之前.只有 constant,后来有人嫌 constant这个词本身代表变量中的常量,不适合用来修饰函数,所以将constant拆成了view和pure。
当函数有返回值时,可以添加这三种定义.,用这三种方式定 义的函数都只执行读操作,不会进行编译执行。即用了这三 种方式定义的函数,不会执行函数里的逻辑.只会执行一个返回的读操作。所以执行这些函数不需要消耗gas费用。
pure 则更为严格,pure 修饰的函数不能改也不能读变量状态,否则编译通不过。
view 的作用和 constant一模一样,可以读取状态变量但是不能改。
相当于自定义函数的修饰符。
修改器(Modifiers)可以用来轻易的改变一个函数的行为。比如用于在函数执行前检查某种前置条件。
fallback function回退函数,每一个合约有且仅有一个没有名字的函数,往合约发送消息时,会执行该函数。如果合约要正常接受ether,需要加上payable声明。声明后,当有用户往合约转账时,将触发该函数,可以在里面写相应的逻辑。
使用状态恢复来处理异常,就是说当抛出异常时将恢复到调用前的状态。抛出异常的方式有assert,require,revert,throw。
assert函数,用于条件检查,只能测试内部错误和检查常量。
require函数,也是用于条件检查,用于测试调用的输入或者合约状态变量。
revert 函数用于标记错误并恢复当前调用。
throw 和 revert 一样,但是throw在0.4.13被弃用,将来会被淘汰。
pragma solidity >=0.4.22 <0.6.0;
contract HelloWorld {
//检查内部计算是否会溢出
function add(uint256 a, uint256 b) internal pure returns (uint256){
uint256 c = a + b;
assert(c >= a);
return c;
}
function send() payable public returns (uint balance){
require(msg.value % 2 == 0);
//只允许偶数
return msg.value;
msg.value;}
function buy(uint amount) payable public {
if (amount > msg.value / 2 ether) {
revert("not enough ether provided.");
}
}
}
this
(当前合约的类型):表示当前合约,可以显式的转换为 Address
selfdestruct(address recipient)
:销毁当前合约,并把它所有资金发送到给定的地址。
pragma solidity >=0.4.22<0.6.0;
contract Helloworld {
address payable public winner;
function sendTowinner(uint256 winAmount) public {
winner.send(winAmount);
winner.transfer(winAmount);
winner.call.value(winAmount);
}
}
<address>.transfer()
<address>.send()
当发送失败时会返回 false 布尔值;
只会传递 2300 Gas 供调用,防止重入;
<address>.gas().call.value()
block.blockhash(uint blockNumber) returns (bytes32)
:返回给定区块号的哈希值,只支持最近256个区块,且不包含当前区块。block.coinbase (address)
: 当前块矿工的地址。block.difficulty (uint)
:当前块的难度。block.gaslimit (uint)
:当前块的gaslimit。block.number (uint)
:当前区块的块号。block.timestamp (uint)
: 当前块的Unix时间戳(从1970/1/1 00:00:00 UTC开始所经过的秒数)msg.sender (address)
: 当前调用发起人的地址。msg.value (uint)
: 这个消息所附带的以太币,单位为wei。msg.sig (bytes4)
:调用数据(calldata)的前四个字节(例如为:函数标识符)。msg.data (bytes)
: 完整的调用数据(calldata)。tx.origin (address)
: 交易的发送者tx.gasprice (uint)
: 交易的 gas 价格。now (uint)
: 当前块的时间戳 (block.timestamp 的别名)https://learnblockchain.cn/docs/truffle/reference/configuration.html#id2
可以使用 import 关键字声明依赖
import "./AnotherContract.sol";
import "somepackage/SomeContract.sol";
truffle compile [--all]
每次编译只会涉及上一次修改后的代码,如果要强制重新编译,可以使用 —all
选项.编译结果保存在 build/contracts/
文件夹中.
部署脚本在 migrations 文件夹下
truffle migrate [--reset]
truffle migrate --network live
Migrations.sol合约会记录部署的历史,如果需要忽略部署历史,可以使用--reset
参数
const Migrations = artifacts.require("Migrations");
module.exports = function(deployer) {
deployer.deploy(Migrations);
};
artifacts.require
使用该方法获取合约的实例,传入的参数名应该与合约代码定义的名字一致(不是文件名).
contract ContractOne{
//...
}
contract ContractTwo{
//...
}
如果只是用使用其中一个合约,可以是这样的:
var ContractTwo = artifacts.require("ContractTwo");
如果只是用使用两个合约,可以是这样的:
var ContractOne = artifacts.require("ContractOne");
var ContractTwo = artifacts.require("ContractTwo");
module.exports
部署脚本的函数必须用module.exports语法兼容truffle框架,truffle在函数中注入了一个deployer对象,deployer对象提供了部署合约所需的所有API。
deployer.deploy(A).then(
function(){
return deployer.deploy(B,A.address);
}
);
module.exports=function(deployer,network){
if (network=="live"){
//Do something specific to the network named "live".
}else{
//Perform a different step otherwise.
}
}
module.exports = function(deployer,network,accounts){
// use the accounts whitin your migrations
}
pragma solidity ^0.6.11;
contract SimpleStorage {
int _age;
constructor () public {
}
function setAge(int age) public {
_age = age;
}
function getAge() public view returns (int){
return _age;
}
}
const SimpleStorage = artifacts.require("SimpleStorage");
module.exports = function(deployer) {
deployer.deploy(SimpleStorage);
};
truffle compile
truffle migrate
truffle 使用 Mocha测试框架和 Chai 断言为我们提供一个简便的,稳固的JS版本的测试框架,使用 **contract()**替代了 describe().
结构上来说,我们的测试基本上遵循 Mocha的测试风格:
./test
目录中.与 Mocha 不同的是 **contract()**函数,该函数跟 describe() 非常像,不同之处在于:
当然,在我们不需要一个纯净的测试合约状态的时候,依然可以使用 describe().
由于 Truffle没有方法检测到我们将使用哪一个合约进行交互式测试, **Contract abstractions **使得 js可以与智能合约交互.可以使用 truffle 提供的 artifacts.require("")
方法.
在测试脚本中使用artifacts.require("")
就如同在迁移脚本中使用一样,
web3 实例在每一个测试文件中都是可用的,可以直接使用 web3.eth.getBalance
.
./test/metacoin.js
const MetaCoin = artifacts.require("MetaCoin");
contract("MetaCoin", accounts => {
it("should put 10000 MetaCoin in the first account", () =>
MetaCoin.deployed()
.then(instance => instance.getBalance.call(accounts[0]))
.then(balance => {
assert.equal(
balance.valueOf(),
10000,
"10000 wasn't in the first account"
);
}));
it("should call a function that depends on a linked library", () => {
let meta;
let metaCoinBalance;
let metaCoinEthBalance;
return MetaCoin.deployed()
.then(instance => {
meta = instance;
return meta.getBalance.call(accounts[0]);
})
.then(outCoinBalance => {
metaCoinBalance = outCoinBalance.toNumber();
return meta.getBalanceInEth.call(accounts[0]);
})
.then(outCoinBalanceEth => {
metaCoinEthBalance = outCoinBalanceEth.toNumber();
})
.then(() => {
assert.equal(
metaCoinEthBalance,
2 * metaCoinBalance,
"Library function returned unexpected function, linkage may be broken"
);
});
});
it("should send coin correctly", () => {
let meta;
// Get initial balances of first and second account.
const account_one = accounts[0];
const account_two = accounts[1];
let account_one_starting_balance;
let account_two_starting_balance;
let account_one_ending_balance;
let account_two_ending_balance;
const amount = 10;
return MetaCoin.deployed()
.then(instance => {
meta = instance;
return meta.getBalance.call(account_one);
})
.then(balance => {
account_one_starting_balance = balance.toNumber();
return meta.getBalance.call(account_two);
})
.then(balance => {
account_two_starting_balance = balance.toNumber();
return meta.sendCoin(account_two, amount, { from: account_one });
})
.then(() => meta.getBalance.call(account_one))
.then(balance => {
account_one_ending_balance = balance.toNumber();
return meta.getBalance.call(account_two);
})
.then(balance => {
account_two_ending_balance = balance.toNumber();
assert.equal(
account_one_ending_balance,
account_one_starting_balance - amount,
"Amount wasn't correctly taken from the sender"
);
assert.equal(
account_two_ending_balance,
account_two_starting_balance + amount,
"Amount wasn't correctly sent to the receiver"
);
});
});
});
Contract: MetaCoin √ should put 10000 MetaCoin in the first account (83ms) √ should call a function that depends on a linked library (43ms) √ should send coin correctly (122ms) 3 passing (293ms)
const MetaCoin = artifacts.require("MetaCoin");
contract("2nd MetaCoin test", async accounts => {
it("should put 10000 MetaCoin in the first account", async () => {
let instance = await MetaCoin.deployed();
let balance = await instance.getBalance.call(accounts[0]);
assert.equal(balance.valueOf(), 10000);
});
it("should call a function that depends on a linked library", async () => {
let meta = await MetaCoin.deployed();
let outCoinBalance = await meta.getBalance.call(accounts[0]);
let metaCoinBalance = outCoinBalance.toNumber();
let outCoinBalanceEth = await meta.getBalanceInEth.call(accounts[0]);
let metaCoinEthBalance = outCoinBalanceEth.toNumber();
assert.equal(metaCoinEthBalance, 2 * metaCoinBalance);
});
it("should send coin correctly", async () => {
// Get initial balances of first and second account.
let account_one = accounts[0];
let account_two = accounts[1];
let amount = 10;
let instance = await MetaCoin.deployed();
let meta = instance;
let balance = await meta.getBalance.call(account_one);
let account_one_starting_balance = balance.toNumber();
balance = await meta.getBalance.call(account_two);
let account_two_starting_balance = balance.toNumber();
await meta.sendCoin(account_two, amount, { from: account_one });
balance = await meta.getBalance.call(account_one);
let account_one_ending_balance = balance.toNumber();
balance = await meta.getBalance.call(account_two);
let account_two_ending_balance = balance.toNumber();
assert.equal(
account_one_ending_balance,
account_one_starting_balance - amount,
"Amount wasn't correctly taken from the sender"
);
assert.equal(
account_two_ending_balance,
account_two_starting_balance + amount,
"Amount wasn't correctly sent to the receiver"
);
});
});
truffle test ./test/metacoin.js
结合 contract(),每次执行上述命令都会重新部署合约(貌似不会上链).
不使用 truffle 提供的测试框架 而是基于以太坊接口,来编写测试代码调试合约.
接口文档:https://web3js.readthedocs.io/en/v1.3.1/web3-eth-contract.html
使用 web3.eth.Contract 对象与以太坊上的智能合约交互
Contract: new (
jsonInterface: AbiItem[] | AbiItem,
address?: string,
options?: ContractOptions
) => Contract;
参数 | 含义 |
---|---|
jsonInterface | The json interface for the contract to instantiate |
address | The address of the smart contract to call. |
options | The options of the contract. Some are used as fallbacks for calls and transactions |
返回值 | 含义 |
---|---|
Object | The contract instance with all its methods and events |
export interface ContractOptions {
// Sender to use for contract calls,
//The address transactions should be made from.
from?: string;
// Gas price to use for contract calls,
// The gas price in wei to use for transactions.
gasPrice?: string;
// Gas to use for contract calls
// The maximum gas provided for a transaction (gas limit).
gas?: number;
// Contract code
// The byte code of the contract. Used when the contract gets deployed.
data?: string;
}
参数 | 含义 |
---|---|
from | The address transactions should be made from. |
gasPrice | The gas price in wei to use for transactions. |
gas | The maximum gas provided for a transaction (gas limit). |
data | The byte code of the contract. Used when the contract gets deployed. |
使用方式:
var myContract = new web3.eth.Contract([...], '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe', {
from: '0x1234567890123456789012345678901234567891', // default from address
gasPrice: '20000000000' // default gas price in wei, 20 gwei in this case
});
类型 | 含义 |
---|---|
String | 20 Bytes: Any ethereum address. You should have the private key for that address in your node or keystore. (Default is undefined ) |
web3.eth.defaultAccount = accounts[1];
以下方法在没有设置 from 属性的时候,该属性的值就会作为 from 的值来使用.
web3.eth.sendTransaction()
web3.eth.call()
new web3.eth.Contract() -> myContract.methods.myMethod().call()
new web3.eth.Contract() -> myContract.methods.myMethod().send()
The address used for this contract instance. All transactions generated by web3.js from this contract will contain this address as the "to"
.
类型 | 含义 | 备注 |
---|---|---|
String|null | 合约地址 | 没有指定则为 null |
The default block is used for certain methods. You can override it by passing in the defaultBlock as last parameter. The default value is "latest”
.
取值 | 类型 | 备注 |
---|---|---|
数值 | Number|BN|BigNumber | A block number |
earliest | String | The genesis block |
latest | String | The latest block (current head of the blockchain) |
pending | String | The currently mined block (including pending transactions) |
使用 NodeJs
进行合约的调试
数据的读写
transaction:写数据
call:读数据
如果合约需要监听业务,则可以使用 事件监听 来获取每次业务的执行.
const Web3 = require("web3");
const shelljs = require("shelljs");
(async () => {
// 合约abi
let loansAbi;
// 合约实例
let instance;
// 链上账户信息
let accounts;
// 合约地址,可以从链上查看或者从 Ganche 客户端查看
const loansAddress = '0x31b9dE9548200E200EB3d32671169fB3b9c394af'
//创建一个 web3 示例
const web3 = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:7545'))
accounts = await web3.eth.getAccounts();
console.log(accounts);
loansAbi = JSON.parse(shelljs.cat('./instance/abi.txt').stdout);
instance = new web3.eth.Contract(loansAbi, loansAddress);
// console.log(instance);
/**
* 此方式只输出一次事件
*/
instance.getPastEvents('set_age',{fromBlock:168},function (error, result) {
if (error) {
console.error(error);
} else {
console.log(result)
}
});
/**
* 持续监听事件
*/
instance.events.set_age( {fromBlock: 168}, function (error, result) {
if (error) {
console.error(error);
} else {
console.log(result)
}
})
})()
https://gitee.com/xubinbin1024/solidity-demo.git
three proof decorate oil solution injury knock weather proof copper join rabbit
pragma solidity ^0.6.11;
contract SimpleStorage {
int _age;
constructor () public {
}
function setAge(int age) public {
_age = age;
}
function getAge() public view returns (int){
return _age;
}
}
const SimpleStorage = artifacts.require("SimpleStorage");
module.exports = function(deployer) {
deployer.deploy(SimpleStorage);
};
const SimpleStorage = artifacts.require("SimpleStorage");
contract("SimpleStorage", function (accounts) {
it('所有账户信息', async function () {
console.log(accounts);
});
it('合约实例 ', async function () {
let instance = await SimpleStorage.deployed();
console.log('合约地址', instance.address);
});
it('账户余额 ', async function () {
let balance = await web3.eth.getBalance(accounts[0]);
console.log(balance);
});
});
1, 从以上代码的 合约实例的输出中,可以看出每次输出的合约地址是不同的,正如第五章所说,每次执行测试都会重新部署合约.
2, 可以在此这中测试代码中直接使用 web3对象.
上述代码看似方便,但是不便于调试.
本测试代码使用单纯的 Mocha框架,也因此不会每次都重新部署合约.也便于调试.这种方式的特点:
const Web3 = require("web3")
const shelljs = require("shelljs");
/**
* 用于离线签名
* @type {Transaction}
*/
const Tx = require('ethereumjs-tx').Transaction;
/**
* 合约地址:合约部署后,可以从 ganache 客户端获取
* @type {number}
*/
const address = '0xAa51C85b57b65f46B1b37A95C42C0B4532b317a9'
describe("SimpleStorage", function () {
let web3;
it('get web3', async function () {
web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:7545'))
});
let accounts;
it('所有账户信息', async function () {
accounts = await web3.eth.getAccounts();
console.log(accounts);
});
it('检查地址的合法性', async function () {
let myiban = web3.eth.Iban.fromAddress(address)
console.log(myiban)
});
let abi;
it('合约abi ', async function () {
abi = JSON.parse(shelljs.cat('./instance/abi.txt').stdout);
console.log(abi);
});
let instance;
it('获取合约实例', async function () {
instance = new web3.eth.Contract(abi, address, {
from: accounts[0]
});
shelljs.echo(instance).to('./instance/instance1.log');
});
let from;
it('设置默认 from 账户', async function () {
from = accounts[2];
web3.eth.defaultAccount = from;
console.log(from);
});
it('合约默认账户', async function () {
console.log(web3.eth.Contract.defaultAccount);
});
it('address', async function () {
console.log(instance.options.address);
});
it('账户余额 ', async function () {
let balance = await web3.eth.getBalance(accounts[0]);
console.log(balance);
});
it.skip('转账(在线签)', async function () {
let transactionReceipt = await web3.eth.sendTransaction({
to: accounts[0],
value: web3.utils.toWei('1', 'ether')
});
console.log(transactionReceipt);
});
let nonce;
it('当前账户的 nonce', async function () {
nonce = await web3.eth.getTransactionCount(from);
console.log(nonce);
});
const token_hex = '0x';
it.skip('离线签', async function () {
let priv = "69d3f76ddb441c41547caabcc5dbdaacedebc389ce0cc3eb3a240778384ee9a9";
let privateKey = Buffer.from(priv, 'hex');
let priceWei = await web3.eth.getGasPrice();
priceWei = token_hex + Number(priceWei).toString(16);
const gasLimitCounts = token_hex + Number(30000).toString(16);
const amountWei = web3.utils.toWei('1', 'ether');
// log('amountWei = ' + amountWei);
const amount = token_hex + Number(amountWei).toString(16);
let rawTx = {
nonce: nonce,
gasPrice: priceWei.toString(),
gasLimit: gasLimitCounts.toString(),
to: '0x0000000000000000000000000000000000000000',
value: amount.toString(),
data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057'
};
let tx = new Tx(rawTx);
tx.sign(privateKey);
let serializedTx = tx.serialize();
let transactionReceipt = await web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'));
console.log(transactionReceipt);
});
it('获取 from 余额', async function () {
let balance = await web3.eth.getBalance(from);
console.log(balance);
});
it('当前块高', async function () {
let number = await web3.eth.getBlockNumber();
console.log(number);
});
it('读数据', async function () {
let age = await instance.methods.getAge().call();
console.log(age);
});
it('写数据', async function () {
let newVar = await instance.methods.setAge(10).send();
console.log(newVar);
});
it('读数据', async function () {
let age = await instance.methods.getAge().call();
console.log(age);
});
it('获取 from 余额', async function () {
let balance = await web3.eth.getBalance(from);
console.log(balance);
});
});
pragma solidity >=0.5.0 <0.7.0;
/*
场景说明:
银行提供企业贷款服务,申请企业需要提交公司授信资质(法人征信正常 与否)以及其供应链上交易信息(是否有合约纠纷和违约逾期),
银行需要通过 银行人工审核和机审才可以给企业放款。机审时若碰到法人征信不正常、企业产 生供应链上有合约纠纷、违约逾期等情况,则判断审核不通过。
*/
contract Loans {
address company;//申请企业的地址
address owner;//合约的创建者,即审核人员的地址信息
struct Application {
bool credit;//法人征信是否正常
bool dispute;//是否有合约纠纷
bool offlineMac;//是否通过线下机审
bool offlineMan;//是否通过线下人工审核
bool overdue;//是否逾期
}
constructor () public {
owner = msg.sender;
}
mapping(address => Application) applicationInfo;
event ApplicationSubmit(address addr, bool credit, bool dispute, bool overdue);
//申请企业提交信息
function applicationSubmit(bool credit, bool dispute, bool overdue) public {
applicationInfo[msg.sender].credit = credit;
applicationInfo[msg.sender].dispute = dispute;
applicationInfo[msg.sender].overdue = overdue;
emit ApplicationSubmit(msg.sender, credit, dispute, overdue);
}
event OffLineStuffSubmit(address addr, bool offlineMan, bool offlineMac);
function offlineStuffSubmit(bool offlineMan, bool offlineMac, address addr) public {
require(msg.sender == owner, 'you dont have access to do this');
applicationInfo[addr].offlineMan = offlineMan;
applicationInfo[addr].offlineMac = offlineMac;
emit OffLineStuffSubmit(addr, offlineMan, offlineMac);
}
function getApplicationResult() public view returns (bool){
if (applicationInfo[msg.sender].credit == true
&& applicationInfo[msg.sender].overdue == true
&& applicationInfo[msg.sender].offlineMac == true
&& applicationInfo[msg.sender].offlineMan == true
&& applicationInfo[msg.sender].dispute == true) {
return true;
}
return false;
}
function getApplicationCredit() public view returns (bool){
return applicationInfo[msg.sender].credit;
}
function getApplicationDispute() public view returns (bool){
return applicationInfo[msg.sender].dispute;
}
function getApplicationOverdue() public view returns (bool){
return applicationInfo[msg.sender].overdue;
}
function getApplicationMan() public view returns (bool){
return applicationInfo[msg.sender].offlineMan;
}
function getApplicationMac() public view returns (bool){
return applicationInfo[msg.sender].offlineMac;
}
}
const Web3 = require("web3");
(async () => {
// 合约abi
let loansAbi;
// 合约实例
let instance;
// 链上账户信息
let accounts;
// 合约地址,可以从链上查看或者从 Ganche 客户端查看
const loansAddress = '0x8ebce3764E3b0442af914C4b6d9587512839901A'
//创建一个 web3 示例
const web3 = new Web3(new Web3.providers.WebsocketProvider('http://localhost:7545'))
accounts = await web3.eth.getAccounts();
console.log(accounts);
loansAbi = require("./build/contracts/Loans.json").abi;
instance = new web3.eth.Contract(loansAbi, loansAddress);
// console.log(instance);
instance.events.ApplicationSubmit({fromBlock:0},function (error, result) {
if (error) {
console.error(error);
} else {
console.log(result)
}
})
})()
truffle exec mytoken.js
let accounts = await web3.eth.getAccounts();
8 . 事件及订阅 event :
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
抛出事件:
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
订阅:
//监听代币交易事件
module.exports = async (callback) => {
try {
const MyToken = artifacts.require("MyToken");
let instance = await MyToken.deployed();
let BN = require("bn.js");
let frac = new BN(10).pow(new BN(18));
instance.Transfer().on('data', function (event) {
console.log('-----------------');
console.log('from', event.args.from);
console.log('to', event.args.to);
console.log('value', event.args.value.div(frac).toString());
});
} catch (e) {
console.log("e = " + e.stack);
callback(e)
}
};
//获取所有区块信息
module.exports = async (callback) => {
try {
const MyToken = artifacts.require("MyToken");
let instance = await MyToken.deployed();
let BN = require("bn.js");
let frac = new BN(10).pow(new BN(18));
let events = await instance.getPastEvents('allEvents',{
'fromBlock': 0,
'toBlock': 'latest'
},
function (error) {
console.log('error', error);
});
for (let i = 0; i < events.length; i++) {
let event = events[i];
console.log('-----------------------------------');
console.log(JSON.stringify(event,null,4));
// console.log('Transfer' + i);
// console.log('from', event.args.from);
// console.log('to', event.args.to);
// console.log('value', event.args.value.div(frac).toString());
}
callback();
} catch (e) {
console.log("e = " + e.stack);
callback(e)
}
};
国际银行账号,简单地说,以太坊中的iban账号是以太坊为了和传统的银行系统对接而引入的概念, web3.js中提供了以太坊地址和iban地址之间的转换方法。
iban这个概念源于传统的银行系统,其英文全称为 International Bank Account Number, 即国际银行帐号。iban的作用是为全球任意一家银行中的任意一个账户生成一个全球唯一的账号,以便进行跨行交易。一个iban账号看起来像这样:
XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS
iban地址最多可以包含34个字母和数字,其中的字母大小写不敏感。在iban 中包含以下信息:
BBAN
(Basic Bank Account Number),用来标识银行机构、网点及
客户在该机构内的账号,这三部分信息的编码方案依赖于前面提及的国别码新的国别码和BBAN编码方案.
以太坊引入了一个新的IBAN国别码:XE,其中E代表Ethereum,X代表非法币(non-jurisdictional currencies)。同时,以太坊提出了三种BBAN的编码格式:direct、basic和indirect。
编码方案中的BBAN为30个字母/数字,只有一个字段:账户编号。例如,以太坊
地址00c5496aee77c1ba1f0854206a26dda82a81d6d8
转换为direct方案的BBAN账号,就
得到XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS
。
let myiban=web3.eth.Iban.fromAddress('0x00c5496aee77c1ba1f0854206a26dda82a81d6d8');
console.log(myiban) //XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS
编码方案与direct方案的唯一区别在于,其BBAN长度为31个字母/数字,因此该方案 不兼容IBAN。
编码方案中的BBAN长度为16个字母/数字,包含三个字段:
例如,一个采用indrect编码方案的以太坊iban账号,看起来是这样:
XE81ETHXREGGAVOFYORK1
XE81-ETH-XREG-GAVOFYORK1前面的XE
表示国别码,81
为校验和,后面的16个字符就是indrect编码的BBAN,其中:
如前所述,使用web3.eth.Iban.fromEthereumAddress()
方法,可以将一个以太坊地址
转换为direct编码方案的iban账号。与之对应的,可以使用web3.eth.Iban.toAddress方法,
将一个采用direct编码方案的iban账号,转换回以太坊地址。例如:
let myaddr = web3.eth.Iban.toAddress("XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS")
console.log(myaddr) //0x00c5496aEe77C1bA1f0854206A26DdA82a81D6D812
iban账号中的校验和用来帮助核验一个给定字符串是否为有效的iban账号。可以使用 web3.js中的web3.eth.Iban.isValid() 来进行执行校验。例如:
let isValid = web3.eth.Iban.isValid("XE81ETHXREGGAVOFYORK")
console.log(isValid) // true
isValid = web3.eth.Iban.isValid("XE82ETHXREGGAVOFYORK")
console.log(isValid) // false,因为校验和无效
abi = [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"int256","name":"age","type":"int256"}],"name":"setAge","outputs":[],"stateMutability":"nonpayable","type":"function","signature":"0xf193dc1d"},{"inputs":[],"name":"getAge","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function","constant":true,"signature":"0x967e6e65"}];
address = '0xAa51C85b57b65f46B1b37A95C42C0B4532b317a9';
contract = web3.eth.contract(abi);
instance = contract.at(address);
accounts = web3.eth.accounts;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。