合约调用的时候回产生event log(事件日志),这个event log会记录在一个调用合约的交易的receipt函数中。通过对event log的分析可以得到跟多这个交易的一些详细内容
- 获得event log
首先获得一个块中的交易数组 web3.eth.getBlock(blockHashOrNumber, true, function(error, blockData) {} 其次根据返回的blockData.transactions获得交易数组 随后可以根据交易数组获得每个交易的hash 最后 var data = web3.eth.getTransactionReceipt(hash); 获得这个交易的更多交易细节 var logsArr = data.logs; 得到了event log
- 分析
1. 规则 例如:代币的购买,会触发代币的Transfer事件,这是ERC20标准的基本规定 1. 对事件的基本定义 event Transfer(address indexed from, address indexed to, uint256 value); 事件名字:Transfer 事件的参数:address, address, uint256,且此事件的from和to参数前有indexed标记,value没有indexed标记 事件的规则是: topic[0]: keccak(Transfer(address,address,uint256)),对事件的字符做keccak散列运算 topic[1]: address类型from参数补齐64位 topic[1]: address类型to参数补齐64位 data: 没有indexed标记的value的值转化为16进制,并补齐64位 具体的实现如下: // 从from这个账户发送 value个toekn(代币) 到to这个账户 Transfer(address indexed from, address indexed to, uint256 value) // 事件名字散列值 topic[0] = keccak(Transfer(address,address,uint256)) = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef // 后续的就是参数中有indexed标志的参数 所传递的值 // fromeAddress 补齐64位 topic[1] = 0x0000000000000000000000000000000000000000000000000000000000000000 topic[2] = 0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550 // 然后是另一个不带indexed标志的参数的 值传入 // 这就是一个数字转化为16进制 然后补齐64位 data = 0x00000000000000000000000000000000000000000000002be4fb8c854544b555 2. 具体的事件的分析 1. 基本交易信息 - 交易hash <https://etherscan.io/tx/0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4> - 合约1的code <https://etherscan.io/address/0x20bf6672497941bd3e4ec5fd551de5c31e0a898a#code> - 合约2的code <https://etherscan.io/address/0x78b039921e84e726eb72e7b1212bb35504c645ca#code> 事件的记录由合约代码决定 2. 交易描述 普通用户地址向合约地址1转账1.75eth,所触发的事件分析 3.事件日志字段 1. 第一个事件日志 "address": "0x78b039921e84e726eb72e7b1212bb35504c645ca", "topics": [ "0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885", "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550" ], "data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555" 即合约地址为0x78b039921e84e726eb72e7b1212bb35504c645ca的合约触发了事件,这个合约地址时合约2 根据对代码的分析以及keccak散列后的结果对比,确定是 Mint(address indexed to, uint256 amount) 即对字符:Mint(address,uint256) 处理后的0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885 这个日志,就是对第二个参数topics[1]的地址0xd53487c6b3a88dded611079b7ae7b377f4888550分发了data字段内容(转化后为809.709931333333333333),即转了809.709931333333333333个Sether币 2. 第二个事件日志 "address": "0x78b039921e84e726eb72e7b1212bb35504c645ca", "topics": [ "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550" ], "data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555" 合约2触发的事件,将代币分配到某个地址的操作 Transfer(address indexed from, address indexed to, uint256 value) 对Transfer(address,address,uint256) 处理得0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef 从0x地址,实际就是从空地址转账,也就是代币增发(类似于系统又印了多少钞票),然后分配给0xd53487c6b3a88dded611079b7ae7b377f4888550 data字段是分配的具体数字: 809.709931333333333333 3. 第三个事件日志 "address": "0x20bf6672497941bd3e4ec5fd551de5c31e0a898a", "topics": [ "0xde080401e79886a3afdeac3fa6426ade55f095bfd9fbe1065b017f1e90479a86", "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550", "0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550" ], "data": "0x000000000000000000000000000000000000000000000000181449a077eb7c0000000000000000000000000000000000000000000000002be4fb8c854544b555" 这个是合约1触发的事件日志 SethTokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount) 对字符:SethTokenPurchase(address,address,uint256,uint256) 处理后0xde080401e79886a3afdeac3fa6426ade55f095bfd9fbe1065b017f1e90479a86 这件事我的理解就是,记录了0xd53487c6b3a88dded611079b7ae7b377f4888550地址花了1.73509271 eth购买了809.709931333333333333 个Sether币 data字段是两个字段,可以去除0x字符后,然后64位分割为两个字段,然后转化为10进制即可。
- 遗留问题
从普通地址转账给合约地址的eth,通过合约处理后,转给了合约的钱包地址,这个交易信息,怎么获取?
参考:合约内部交易信息获取被下面合约代码处理了 // 钱包地址调用transfer函数,这个函数是一个公开函数,就是类似于evm中默认有的 wallet.transfer(msg.value); 这种合约内部的交易怎么处理?
交易的真实数据信息如下
{
"blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
"blockNumber": 4906655,
"contractAddress": null,
"cumulativeGasUsed": 2815773,
"from": "0xd53487c6b3a88dded611079b7ae7b377f4888550",
"gasUsed": 73886,
"logs": [
{
"address": "0x78b039921e84e726eb72e7b1212bb35504c645ca",
"topics": [
"0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885",
"0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
],
"data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555",
"blockNumber": 4906655,
"transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
"transactionIndex": 105,
"blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
"logIndex": 16,
"removed": false
},
{
"address": "0x78b039921e84e726eb72e7b1212bb35504c645ca",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
],
"data": "0x00000000000000000000000000000000000000000000002be4fb8c854544b555",
"blockNumber": 4906655,
"transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
"transactionIndex": 105,
"blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
"logIndex": 17,
"removed": false
},
{
"address": "0x20bf6672497941bd3e4ec5fd551de5c31e0a898a",
"topics": [
"0xde080401e79886a3afdeac3fa6426ade55f095bfd9fbe1065b017f1e90479a86",
"0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550",
"0x000000000000000000000000d53487c6b3a88dded611079b7ae7b377f4888550"
],
"data": "0x000000000000000000000000000000000000000000000000181449a077eb7c0000000000000000000000000000000000000000000000002be4fb8c854544b555",
"blockNumber": 4906655,
"transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
"transactionIndex": 105,
"blockHash": "0xb385e463f51b3f482bf229fe8aa1a61ecfcd3a3619d617afe58517b42e56500a",
"logIndex": 18,
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000800800000020008020000008000000000002000000000000200000000000000000000000020000000000000000000800000000000000400000000010000000000000000000000000000000200000000000000000000000000000000000000000000080000000000000000000000000000000002000000000000000000000000000000002000000000000000000000000000044000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": "0x20bf6672497941bd3e4ec5fd551de5c31e0a898a",
"transactionHash": "0xd54e1fbc350dac428ca65a4abef6db4e343e1367e6cd9434bb14949a469cefc4",
"transactionIndex": 105
}
合约1的代码摘抄如下
合约1
....上半部分和合约2的代码一样....
部分代码如下
contract MintableToken is StandardToken, Ownable {
// 定义的时间Mint
event Mint(address indexed to, uint256 amount);
event MintFinished();
bool public mintingFinished = false;
.....
/**
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
// mint函数
function mint(address _to, uint256 _amount) onlyOwner canMint public returns (bool) {
totalSupply = totalSupply.add(_amount);
balances[_to] = balances[_to].add(_amount);
// 调用Mint事件
Mint(_to, _amount);
// 调用Transfer事件进行代币分发
Transfer(0x0, _to, _amount);
return true;
}
....
}
/**
* @title SetherToken
* @dev Sether ERC20 Token that can be minted.
* It is meant to be used in sether crowdsale contract.
*/
// 继承于MintableToken,可以使用它的函数调用
contract SetherToken is MintableToken {
string public constant name = "Sether";
string public constant symbol = "SETH";
uint8 public constant decimals = 18;
function getTotalSupply() public returns (uint256) {
return totalSupply;
}
}
/**
* @title SetherBaseCrowdsale
* @dev SetherBaseCrowdsale is a base contract for managing a sether token crowdsale.
*/
contract SetherBaseCrowdsale {
using SafeMath for uint256;
// The token being sold
// 被分发的代币
SetherToken public token;
....
// address where funds are collected
// 募集的资金都转到这个钱包地址
address public wallet;
......
/**
* event for token purchase logging
* @param purchaser who paid for the tokens
* @param beneficiary who got the tokens
* @param value weis paid for purchase
* @param amount amount of tokens purchased
*/
event SethTokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
// 和合约名字一样的函数,合约创建时自动调用
function SetherBaseCrowdsale(uint256 _rate, address _wallet) {
require(_rate > 0);
require(_wallet != address(0));
// 创建代币合约,并存储代币合约地址
token = createTokenContract();
rate = _rate;
// 钱包地址
wallet = _wallet;
}
// fallback function can be used to buy tokens
// fallback function 当有账户向这个地址转真实的eth时,自动触发这个函数
function () payable {
// 调用buyTokens函数
buyTokens(msg.sender);
}
// low level token purchase function
function buyTokens(address beneficiary) public payable {
....
// 代币调用mint函数,进而触发这个函数中事件
token.mint(beneficiary, tokens);
// 触发SethTokenPurchase事件
SethTokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
// 调用forwardFunds函数
forwardFunds();
}
// send ether to the fund collection wallet
// 将收到的eth转到钱包地址
function forwardFunds() internal {
// 钱包地址调用transfer函数,这个函数是一个公开函数,就是类似于evm中默认有的
wallet.transfer(msg.value);
}
.....
}
/**
* @title SetherMultiStepCrowdsale
* @dev Multi-step payment policy contract that extends SetherBaseCrowdsale
*/
contract SetherMultiStepCrowdsale is SetherBaseCrowdsale {
.....
}
/**
* @title SetherCappedCrowdsale
* @dev Extension of SetherBaseCrowdsale with a max amount of funds raised
*/
contract SetherCappedCrowdsale is SetherMultiStepCrowdsale {
.....
}
/**
* @title SetherStartableCrowdsale
* @dev Extension of SetherBaseCrowdsale where an owner can start the crowdsale
*/
contract SetherStartableCrowdsale is SetherBaseCrowdsale, Ownable {
.....
}
/**
* @title SetherFinalizableCrowdsale
* @dev Extension of SetherBaseCrowdsale where an owner can do extra work
* after finishing.
*/
contract SetherFinalizableCrowdsale is SetherBaseCrowdsale, Ownable {
.....
}
/**
* @title SetherCrowdsale
* @dev This is Sether's crowdsale contract.
*/
contract SetherCrowdsale is SetherCappedCrowdsale, SetherStartableCrowdsale, SetherFinalizableCrowdsale {
.....
}
合约2的代码摘抄如下,合约2的代码就是合约1代码的上半部分
合约2<SetherToken>
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
....
}
library SafeMath {
....
}
contract ERC20Basic {
....
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
....
}
contract BasicToken is ERC20Basic {
....
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
....
}
contract MintableToken is StandardToken, Ownable {
....
}
/**
* @title SetherToken
* @dev Sether ERC20 Token that can be minted.
* It is meant to be used in sether crowdsale contract.
*/
contract SetherToken is MintableToken {
....
}