概述
简单看了下初链钱包的开源代码,发现初链钱包中主要是调用了LightWallet及web3的相关API。
LightWallet
是一个实现了BIP32
,BIP39
和BIP44
的HD钱包。LightWallet
提供 API
来创建和签署交易,或者使用LightWallet
生成的地址和密钥加密和解密数据。
下面就分别从创建钱包、导入钱包、备份钱包、账户余额、交易、交易记录等几个主要功能来详解。
1. 创建钱包
核心代码
let randomSeed = lightwallet.keystore.generateRandomSeed();
lightwallet.keystore.createVault(
{
password: this.state.pwd,
seedPhrase: randomSeed,
hdPathString: "m/44'/60'/0'/0"
},
(err, ks) => {
ks.keyFromPassword(this.state.pwd, (err, pwDerivedKey) => {
ks.generateNewAddress(pwDerivedKey, 1);
var address = ks.getAddresses();
let keystoreV3 = web3.eth.accounts
.privateKeyToAccount('0x' + ks.exportPrivateKey(address[0], pwDerivedKey))
.encrypt(this.state.pwd);
storage.save({
key: 'walletInfo',
data: {
walletAddress: address[0],
keystoreV3: keystoreV3,
ks: ks
},
expires: null
});
storage.save({
key: 'walletName',
data: {
walletName: this.state.walletName
},
expires: null
});
/** 跳转到导出钱包页面 **/
});
}
);
主要方法
a.
let randomSeed = lightwallet.keystore.generateRandomSeed();
- 生成由12个随机单词组成的字符串。
- 参数
-
extraEntropy
可选。如果传入了该值,那么该值将会与javascript
中RNG
(随机数生成器)随机生成的数据以某种方式关联起来,然后再对其进行hash
生成最终的种子。
-
b.
lightwallet.keystore.createVault(
{
password: this.state.pwd,
seedPhrase: randomSeed,
hdPathString: "m/44'/60'/0'/0"
},
(err, ks) => {
/** 其他逻辑代码 **/
});
- 创建
lightwallet keystore
的实例(ks
)。 - 参数:
-
password
必填。序列化时用于加密keystore
的字符串。 -
seedPharse
必填。用来生成所有账户的12个单词的助记词。 -
salt
选填。用来加密和解密keystore
的盐。如果未填则将随机生成一个。 -
hdPathString
必填。用户必须提供一个符合BIP32
的HD
路径字符串,以前的默认值为m/0'/0'/0'
,现在常用的是BIP44
的路径m/44'/60'/0'/0
。
-
c.
ks.keyFromPassword(this.state.pwd, (err, pwDerivedKey) => {
/** 一些逻辑代码 **/
})
- 通过用户输入的密码及内置的
salt
生成Uint8Array
类型的派生密钥,派生密钥用于加密和解密keystore
。 - 参数
-
password
用户输入的密码。 -
callback
回调有两个参数,错误信息(err
)及派生密钥(pwDerivedKey
)。
-
d.
ks.generateNewAddress(pwDerivedKey, 1);
- 在步骤c得到派生密钥的回调中,调用此方法得到地址/私钥对。
- 参数
-
pwDerivedKey
必填。上一步中得到的派生密钥。 -
num
选填。想要生成的地址/私钥对个数。默认值为1。
-
e.
var address = ks.getAddresses();
- 返回当前存储在
keystore
中的十六进制字符串地址列表。 - 返回值
- 地址列表
f.
ks.exportPrivateKey(address[0], pwDerivedKey)
- 导出私钥。此方法应谨慎调用,因为推荐使用
keystore
进行签名,所以通常不需要导出私钥。 - 参数
-
address
必填。导出私钥的地址。 -
pwDerivedKey
必填。派生密钥,通过派生密钥解密并返回与该地址对应的私钥。
-
g.
web3.eth.accounts.privateKeyToAccount('0x' + ks.exportPrivateKey(address[0], pwDerivedKey))
- 使用指定的私钥创建一个账户对象。
- 参数
-
privateKey
必填。私钥。
-
- 返回值
- 账户对象(
Account
)。结构如下:
export declare interface Account { address: string privateKey: string publicKey: string }
- 账户对象(
h.
let keystoreV3 = web3.eth.accounts
.privateKeyToAccount('0x' + ks.exportPrivateKey(address[0],pwDerivedKey))
.encrypt(this.state.pwd);
- 通过
web3.eth.accounts.encrypt
方法将私钥加密变换为keystorev3
标准格式。 - 参数
-
privateKey
要加密的私钥。 -
password
用于加密的密码。
-
- 返回值
- 加密后的
keystorev3
JSON
。
- 加密后的
至此,钱包的创建已完成。客户端保存的钱包信息如下:
storage.save({
key: 'walletInfo',
data: {
walletAddress: address[0],
keystoreV3: keystoreV3,
ks: ks
},
expires: null
});
2. 导入钱包
i. 助记词导入
核心代码
lightWallet.keystore.createVault(
{
password: option.password,
seedPhrase: option.mnemonic,
hdPathString: option.hdPathString
},
(err, ks) => {
ks.keyFromPassword(option.password, (err, pwDerivedKey) => {
ks.generateNewAddress(pwDerivedKey, 1);
var address = ks.getAddresses();
let keystoreV3 = web3.eth.accounts
.privateKeyToAccount('0x' + ks.exportPrivateKey(address[0], pwDerivedKey))
.encrypt(option.password);
storage.save({
key: 'walletInfo',
data: {
walletAddress: address[0],
keystoreV3: keystoreV3,
ks: ks
},
expires: null
});
storage.save({
key: 'walletName',
data: {
walletName: '新钱包'
},
expires: null
});
setTimeout(() => {
option._this.refs.loading.close();
option._this.props.navigation.navigate('Home');
}, 100);
});
}
);
助记词导入和创建钱包流程大致相同,这里将不再赘述。
ii. 私钥导入
核心代码
try {
let keystoreV3 = web3.eth.accounts.encrypt(this.state.privateFile, this.state.privatePwd);
storage.save({
key: 'walletInfo',
data: {
walletAddress: '0x' + keystoreV3.address,
keystoreV3: keystoreV3
},
expires: null
});
storage.save({
key: 'walletName',
data: {
walletName: '新钱包'
},
expires: null
});
setTimeout(() => {
this.refs.loading.close();
this.props.navigation.navigate('Home');
}, 100);
} catch (err) {
this.refs.loading.close();
setTimeout(() => {
Alert.alert(null, I18n.t('wallet.privateKeyIsWrong')); // '提示', '私钥无效,请重新输入!'
}, 100);
}
私钥导入调用方法上文均已详解,此处将不再赘述。
iii. keystore导入
核心代码
try {
let account = web3.eth.accounts.decrypt(this.state.keystoreFile, this.state.keystorePwd);
storage.save({
key: 'walletInfo',
data: {
walletAddress: account.address,
keystoreV3: JSON.parse(this.state.keystoreFile)
},
expires: null
});
storage.save({
key: 'walletName',
data: {
walletName: '新钱包'
},
expires: null
});
setTimeout(() => {
this.refs.loading.close();
this.props.navigation.navigate('Home');
}, 100);
} catch (e) {
this.refs.loading.close();
setTimeout(() => {
Alert.alert(null, I18n.t('wallet.wrongByKeystoreOrPwd'));
// '提示', '导入钱包失败, 请检查keystore或者密码是否正确');
}, 100);
}
主要方法
a.
let account = web3.eth.accounts.decrypt(this.state.keystoreFile, this.state.keystorePwd);
- 解密给定的
keystore
对象,并创建账户。 - 参数
-
keystoreJsonV3
要解密的keystore
文件。 -
password
用来解密的密码。
-
- 返回值
- 解密的账户对象(
Account
)
- 解密的账户对象(
3. 备份钱包
i. 导出助记词
核心代码
storage.load({ key: 'walletInfo' }).then((res) => {
let mneKeystore = lightwallet.keystore.deserialize(JSON.stringify(res.ks));
mneKeystore.keyFromPassword(params.walletPassword, (err, pwDerivedKey) => {
let Mnemonic = mneKeystore.getSeed(pwDerivedKey);
this.setState({
Mnemonic: Mnemonic
});
});
});
主要方法
a.
let mneKeystore = lightwallet.keystore.deserialize(JSON.stringify(res.ks));
- 传入一个序列化的
keystore
字符串返回一个反序列化keystore
- 参数
-
serialized_keystore
序列化的keystore
文件
-
- 返回值
- 反序列化
keystore
- 反序列化
b.
let Mnemonic = mneKeystore.getSeed(pwDerivedKey);
- 给定派生密钥,解密并返回12个单词的助记词种子。
- 参数
-
pwDerivedKey
派生密钥
-
- 返回值
助记词
ii. 导出keystore
核心代码
componentDidMount() {
const { params } = this.props.navigation.state;
this.setState({
keystoreV3: JSON.stringify(params.keystoreV3)
});
}
将保存在本地的keystorev3
文件取出,展示给用户导出即可。
4. 账户余额
i. 获取ETH余额
核心代码
web3.eth.getBalance(this.state.walletAddress).then((res) => {
let eth_banlance = this.show(web3.utils.fromWei(res, 'ether'));
this.setState({ eth_banlance });
});
主要方法
a.
web3.eth.getBalance(this.state.walletAddress).then((res) => {
});
- 获取指定块中特定账户地址的余额。
- 参数
-
address
要检查余额的账户地址。 -
defaultBlock
可选,使用该参数覆盖web3.eth.defaultBlock
属性值。 -
callback
可选的回调函数,该回调的第一个参数为error
对象,第二个参数为结果值。
-
- 返回值
- 一个
Promise
对象,其解析值为指定账户地址的余额字符串,以wei
为单位。
- 一个
b.
let eth_banlance = this.show(web3.utils.fromWei(res, 'ether'));
- 将给定的以
wei
为单位的值转换为其他单位的数值。注意,wei
是最小的以太单位,应当总是使用wei
进行计算,仅在需要显示时进行转换。
ii. 获取truechain测试网余额
核心代码
webtrue.getBalance(this.state.walletAddress).then((res) => {
let true_beta_banlance = this.show(web3.utils.fromWei(res, 'ether'));
this.setState({ true_beta_banlance });
});
webtrue
定义如下:
const Web3 = require('web3');
const WebTrue = require('etrue');
function check(host) {
if (host.includes('ropsten')) {
store.dispatch({
type: 'CONTRACTADDR',
TRUEContractAddr: '0x2792d677B7Ba6B7072bd2293F64BC0C1CDe23ac1',
TTRContractAddr: '0x635AfeB8739f908A37b3d312cB4958CB2033F456'
});
} else {
store.dispatch({
type: 'CONTRACTADDR',
TRUEContractAddr:
'0xa4d17ab1ee0efdd23edc2869e7ba96b89eecf9ab',
TTRContractAddr: '0xf2bb016e8c9c8975654dcd62f318323a8a79d48e'
});
}
global.host = host;
const web3 = new Web3(new Web3.providers.HttpProvider(host));
const webtrue = new WebTrue.modules.ETrue(trueHost);
global.webtrue = webtrue;
}
主要方法
a.
const web3 = new Web3(new Web3.providers.HttpProvider(host));
- 该方法返回当前有效的通信服务提供器。调用的服务器类型选择有以下几种
-
HttpProvider
HTTP
服务提供器已经被弃用,因为它不支持订阅 -
WebsocketProvider
Websocket
服务提供器是用于传统的浏览器中的标准方法 -
IpcProvider
当运行一个本地节点时,IPC
服务提供器用于node.js
下的DApp
环境,该方法提供最安全的连接。
-
关于eture
了解不是很多,可以参考一下这篇运用docker镜像搭建TrueChain测试私有环境
iii. 获取truechain主网余额
核心代码
// TRUE合约余额
getBalance(
iterface,
this.state.walletAddress,
store.getState().contractAddr.TRUEContractAddr,
(true_banlance) => {
true_banlance = this.show(true_banlance);
this.setState({ true_banlance });
}
);
// TTR合约余额
getBalance(
iterface,
this.state.walletAddress,
store.getState().contractAddr.TTRContractAddr,
(ttr_banlance) => {
ttr_banlance = this.show(ttr_banlance);
this.setState({ ttr_banlance });
}
);
getBalance
方法实现:
function getBalance(iterface, address, ContractAddr, callback) {
var myContract = new web3.eth.Contract(iterface, ContractAddr);
myContract.methods.balanceOf(address).call().then(function(res) {
let balance = web3.utils.fromWei(res, 'ether');
callback(balance);
});
}
PS: 项目在初链币(TRUE)之外,于2018年5月2日发布新ERC20代币TTR白皮书。
主要方法
a.
var myContract = new web3.eth.Contract(iterface, ContractAddr);
-
web3.eth.Contract
类简化了与以太坊区块链上智能合约的交互。创建合约对象时, 只需指定相应智能合约的json
接口,web3
就可以自动地将所有的调用转换为底层基于RPC
的ABI
调用。
通过web3
的封装,与智能合约的交互就像与JavaScript
对象一样简单。 - 参数
-
jsonInterface
要实例化的合约的json
接口。 -
address
可选,要调用的合约的地址,也可以在之后使用myContract.options.address = '0x1234..'
来指定该地址。 -
options
可选,合约的配置对象,其中某些字段用作调用和交易的回调:-
from
交易发送方地址。 -
gasPrice
用于交易的gas
价格,单位:wei
。 -
gas
交易可用的最大gas
量,即gas limit
。 -
data
合约的字节码,部署合约时需要。
-
-
- 返回值
- 带有所有合约方法和合约事件的合约实例对象。
在truechain-light-wallet
中,合约接口定义如下
- 带有所有合约方法和合约事件的合约实例对象。
const iterface = [
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_spender",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "supply",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
},
{
"name": "",
"type": "address"
}
],
"name": "votingInfo",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_from",
"type": "address"
},
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "balances",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "ticketsOf",
"outputs": [
{
"name": "tickets",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "kill",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "founder",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "vote",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_owner",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"name": "balance",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "newFounder",
"type": "address"
}
],
"name": "changeFounder",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "voteEndTime",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "totalVotes",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_tos",
"type": "address[]"
},
{
"name": "_values",
"type": "uint256[]"
}
],
"name": "distributeMultiple",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
}
],
"name": "voteAll",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_endTime",
"type": "uint256"
}
],
"name": "setEndTime",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "frozen",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "_owner",
"type": "address"
},
{
"name": "_spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"name": "remaining",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "distributed",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_amount",
"type": "uint256"
}
],
"name": "distribute",
"outputs": [
{
"name": "success",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_from",
"type": "address"
},
{
"indexed": true,
"name": "_to",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Vote",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "_owner",
"type": "address"
},
{
"indexed": true,
"name": "_spender",
"type": "address"
},
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
}
];
export default iterface;
-
ABI
全称Application Binary Interface
, 是调用智能合约函数以及合约之间函数调用的消息编码格式定义,也可以理解为智能合约函数调用的接口说明。 类似Webservice
里的SOAP
协议一样;也就是定义操作函数签名,参数编码,返回结果编码等。使用ABI
协议时必须要求在编译时知道类型,即强类型相关。 -
ABI
定义
{
"constant": false, //方法修饰符,false表示函数内可以修改状态变量
"inputs": [ //方法参数,它是一个对应数组,数组里的每个对象都是一个参数说明
{
"name": "a", //第一个参数的名字
"type": "uint256" //第一个参数的类型
},
{
"name": "b", //第二个参数的名字
"type": "bytes32" //第二个参数的类型
},
{
"name": "c", //第三个参数的名字
"type": "bytes32[]" ////第三个参数的类型
}
],
"name": "lotus", //方法名
"outputs": [], //方法返回值,格式和inputs类型相同
"payable": false,
"stateMutability": "nonpayable",
"type": "function" //方法类型,function, constructor, fallback,event
},
b.
myContract.methods.balanceOf(address).call().then(function(res) {
}
-
myContract.methods
为指定的合约方法创建一个交易对象,以便使用该交易对象进行调用、发送或估算gas。- 调用
可以使用以下语法获得指定方法的交易对象:myContract.methods.myMethod([param1[, param2[, ...]]])
这样就可以支持从// 1. 名称 myContract.methods.myMethod(123) // 2. 带参名称 myContract.methods'myMethod(uint256)' // 3. 签名 myContract.methods'0x58cf5f10'
javascript
合约对象调用同名但参数不同的合约方法。- 参数
取决于在JSON
接口中定义的合约方法。返回一个交易对象。 - 返回值
交易对象,包含以下字段:-
arguments(Array)
之前传入方法的参数,可修改 -
call(Function)
用来调用只读的合约方法,在EVM直接执行而不必发出交易,因此不会改变合约的状态 -
send(Function)
用来向合约发送交易并执行方法,因此可以改变合约的状态 -
estimateGas(Function)
用来估算方法在链上执行时的gas
用量 -
encodeABI(Function)
用来为合约方法进行ABI
编码。
示例:
-
// 调用合约方法 myContract.methods.myMethod(123).call({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}, function(error, result){ ... }); // 发送交易,使用Promise对象获取返回结果 myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}) .then(function(receipt){ // receipt can also be a new contract instance, when coming from a "contract.deploy({...}).send()" }); // 发送交易,使用事件获取返回结果 myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'}) .on('transactionHash', function(hash){ ... }) .on('receipt', function(receipt){ ... }) .on('confirmation', function(confirmationNumber, receipt){ ... }) .on('error', console.error);
-
myContract.methods.balanceOf
其中balanceOf
即为上文中实例化的合约的json
接口中的方法。通过传入地址返回用户余额。
5. 交易
核心代码
web3.eth.getGasPrice().then((res) => {
this.setState({
gasPrice: res,
cost: res * web3.utils.fromWei(this.state.gas.toString(), 'ether')
});
});
storage.load({ key: 'walletInfo' }).then((res) => {
this.setState({
fromAddr: res.walletAddress,
keystoreV3: res.keystoreV3
});
});
const { params } = this.props.navigation.state;
if (params.currencyName == 'ETH') {
this.setState({
gas: 25200
},
() => {
this._sendTokens = () =>
sendEth(
this.state.fromAddr,
this.state.toAddress,
this.state.amount,
this.state.password,
this.state.keystoreV3,
this.state.gas.toString(),
this.state.gasPrice.toString(),
(err, tx) => {
if (err) {
this.refs.loading.close();
setTimeout(() => {
Alert.alert(null, I18n.t('public.transactionFailed'));
// Alert.alert(null, '发布交易失败,请稍后重试!');
}, 100);
console.log(err);
} else {
this.refs.loading.close();
setTimeout(() => {
// 发布交易成功!
Alert.alert(null, I18n.t('public.transactionSuccess'), [
{
text: 'OK',
onPress: () => {
this.props.navigation.navigate('Home');
}
}
]);
}, 100);
console.log(tx, '=======');
}
}
);
});
} else {
this.setState(
{
gas: 80000
},
() => {
this._sendTokens = () =>
sendTokens(
iterface,
this.state.fromAddr,
this.state.toAddress,
this.state.amount,
this.state.password,
this.state.keystoreV3,
this.state.ContractAddr,
this.state.gas.toString(),
this.state.gasPrice.toString(),
(err, tx) => {
if (err) {
this.refs.loading.close();
setTimeout(() => {
Alert.alert(null, I18n.t('public.transactionFailed'));
// Alert.alert(null, '发布交易失败,请稍后重试!');
}, 100);
console.log(err);
} else {
this.refs.loading.close();
setTimeout(() => {
// 发布交易成功!
Alert.alert(null, I18n.t('public.transactionSuccess'), [
{
text: 'OK',
onPress: () => {
this.props.navigation.navigate('Home');
}
}
]);
}, 100);
console.log(tx, '=======');
}
}
);
}
);
let ContractAddr = params.currencyName + 'ContractAddr';
this.setState({
ContractAddr: store.getState().contractAddr[ContractAddr]
});
}
sendEth
方法如下:
function sendEth(fromAddress, toAddress, amount, password, keystore, gas, gasPrice, callback) {
let account = web3.eth.accounts.decrypt(keystore, password),
value = web3.utils.toWei(amount, 'ether');
web3.eth.accounts.wallet.add(account);
web3.eth.sendTransaction({
from: fromAddress,
to: toAddress,
value: value,
gasPrice: gasPrice,
gas: gas
}, function (error, txhash) {
callback(error, txhash)
});
};
sendTokens
方法如下:
function sendTokens(iterface, fromAddr, toAddr, value, password, keystore, contractAddress, gas, gasPrice, callabck) {
let contract = new web3.eth.Contract(iterface);
contract.options.address = contractAddress;
const account = web3.eth.accounts.decrypt(keystore, password);
web3.eth.accounts.wallet.add(account);
let value_wei = web3.utils.toWei(value, 'ether'),
data = contract.methods.transfer(toAddr, value_wei).encodeABI();
web3.eth.sendTransaction({
from: fromAddr,
to: contractAddress,
value: '0x00',
gasPrice: gasPrice,
gas: gas,
data: data
},
function (error, txhash) {
callabck(error, txhash)
})
}
主要方法
a.
web3.eth.getGasPrice().then((res) => {
this.setState({
gasPrice: res,
cost: res * web3.utils.fromWei(this.state.gas.toString(), 'ether')
});
});
- 用来获取当前
gas
价格,该价格由最近的若干块的gas
价格中值决定。 - 返回值
- 一个
Promise
对象,其解析值为表示当前gas
价格的字符串,单位为wei
。
- 一个
b.
value = web3.utils.toWei(amount, 'ether');
- 将给定的以太金额转换为以
wei
为单位的数值。注意,wei
是最小的以太单位,应当总是使用wei
进行计算,仅在需要显示时进行转换。
c.
web3.eth.accounts.wallet.add(account);
- 使用私钥或账户对象向钱包中添加一个账户。
- 参数
-
account
私钥,或者使用web3.eth.accounts.create()
创建的账户对象。
-
- 返回值
-
Object
被添加的账户
-
d.
web3.eth.sendTransaction({
}, function (error, txhash) {
});
- 向以太坊网络提交一个交易。
- 参数
-
transactionObject
,要发送的交易对象,包含以下字段:-
from
交易发送方账户地址,不设置该字段的话,则使用web3.eth.defaultAccount
属性值。可设置为一个地址或本地钱包web3.eth.accounts.wallet
中的索引序号 -
to
可选,消息的目标地址,对于合约创建交易该字段为null
-
value
以wei
为单位的交易金额,如果是创建合约的话则为合约基金? -
gas
可选,默认值:待定,用于交易的gas
总量,未用完的gas
会退还 -
gasPrice
可选,该交易的gas
价格,单位为wei
,默认值为web3.eth.gasPrice
属性值 -
data
可选,可以是包含合约方法数据的ABI
字符串,或者是合约创建交易中的初始化代码 -
nonce
可选,使用该字段覆盖使用相同nonce
值的挂起交易
-
-
callback
可选的回调函数,其第一个参数为错误对象,第二个参数为结果
-
- 返回值
- 32字节长的交易哈希值。
有三种调用方式
- 32字节长的交易哈希值。
// compiled solidity source code using https://remix.ethereum.org
var code = "603d80600c6000396000f3007c01000000000000000000000000000000000000000000000000000000006000350463c6888fa18114602d57005b6007600435028060005260206000f3";
// 使用回调函数
web3.eth.sendTransaction({
from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe',
data: code // deploying a contracrt
}, function(error, hash){
...
});
// 使用promise
web3.eth.sendTransaction({
from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe',
to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe',
value: '1000000000000000'
})
.then(function(receipt){
...
});
// 使用事件发生器
web3.eth.sendTransaction({
from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe',
to: '0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe',
value: '1000000000000000'
})
.on('transactionHash', function(hash){
/** 在交易发出并得到有效的交易哈希值后立刻触发 */
...
})
.on('receipt', function(receipt){
/** 当交易收据有效后立刻触发 */
...
})
.on('confirmation', function(confirmationNumber, receipt){
/** 在每次确认后立刻触发,最多12次确认。确认编号为第一个参数,收据为第二个参数。从0号确认开始触发 */
...
})
.on('error', console.error); // 在发送交易的过程中如果出现错误则立刻触发。如果是out of gas错误,则传入第二个参数为交易收据
e.
data = contract.methods.transfer(toAddr, value_wei).encodeABI();
- 其中
transfer
即为上文中实例化的合约的json
接口中的方法。参数为转入地址和以wei
为单位的转账金额。
6. 交易记录
i. 获取ETH交易记录
核心代码
const getTransactionRecord = (walletAddress) => {
if (host.includes('ropsten')) {
return axios.get(
'http://api-ropsten.etherscan.io/api?module=account&action=txlist&address=' +
walletAddress +
'&sort=desc&apikey=YourApiKeyToken'
);
} else {
return axios.get(
'http://api.etherscan.io/api?module=account&action=txlist&address=' +
walletAddress +
'&sort=desc&apikey=YourApiKeyToken'
);
}
};
ropsten
为ETH
测试地址。
ii. 获取ERC20交易记录
核心代码
const getERC20TransactionRecord = (walletAddress, contractaddress) => {
if (host.includes('ropsten')) {
return axios.get(
'https://api-ropsten.etherscan.io/api?module=account&action=tokentx&contractaddress=' +
contractaddress +
'&address=' +
walletAddress +
'&sort=desc&apikey=YourApiKeyToken'
);
} else {
return axios.get(
'https://api.etherscan.io/api?module=account&action=tokentx&contractaddress=' +
contractaddress +
'&address=' +
walletAddress +
'&sort=desc&apikey=YourApiKeyToken'
);
}
};
ropsten
为ETH
测试地址。