It can be very easy to send a simple transaction (transfer some CFX to another address), all you need is from
, to
, value
and from
's privateKey.
const { Conflux, Drip } = require('js-conflux-sdk');
const PRIVATE_KEY = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; // sender private key
const RECEIVER_ADDRESS = 'cfxtest:aaawgvnhveawgvnhveawgvnhveawgvnhvey1umfzwp';
async function main() {
const conflux = new Conflux({
url: 'https://test.confluxrpc.com',
networkId: 1,
});
const sender = conflux.wallet.addPrivateKey(PRIVATE_KEY); // add private to local wallet
const transactionHash = await conflux.cfx.sendTransaction({
from: sender.address, // account address or instance which added into conflux.wallet
to: RECEIVER_ADDRESS, // receiver address
value: Drip.fromCFX(0.1), // 0.1 CFX = 100000000000000000 Drip
});
console.log(transactionHash); // suggest store transactionHash in disk !!!
// 0x22e5ffefe4da995ebcb2847762b7acb1c03fd17c9ab010272965fa63c9590d6e
// that it, the transaction has been sended, after it was pack and executed your CFX will be transfer to the target account
}
main();
Normally if everything is well, the tx will be executed is at most 20s, then you can get it's receipt through hash
const receipt = await conflux.cfx.getTransactionReceipt(txHash);
console.log(receipt); // get receipt from remote
/*
{
index: 3,
epochNumber: 1344628,
outcomeStatus: 0,
gasUsed: 54349n,
gasFee: 56250n,
blockHash: '0x4ee16b530f6b6951122c1305b262eef5251ebff03498bc510610ed45fb72a014',
contractCreated: null,
from: 'cfxtest:aasm4c231py7j34fghntcfkdt2nm9xv1tu6jd3r1s7',
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
stateRoot: '0xf8d93f9f783605572fb48beb81e7450b25eae2eeb28310429c862a5c1956bd71',
to: 'cfxtest:aaawgvnhveawgvnhveawgvnhveawgvnhvey1umfzwp',
transactionHash: '0x22e5ffefe4da995ebcb2847762b7acb1c03fd17c9ab010272965fa63c9590d6e',
txExecErrorMsg: null
}
*/
If the transaction receipt's outcomeStatus
is 0
, congratulate you have send a transaction successfully.
Note: before sending transaction, you should add an account to wallet with conflux.wallet.addPrivateKey(PRIVATE_KEY)
After sending, a transaction could be in several different states, here is a detail explanation of transaction life cycle.
You can get a transaction's state by it's status
or it's receipt's outcomeStatus
null
The tx has not mined or executed0
Tx execute success1
Tx execute failed
Only after a transaction executed, you can get it's receipt. The receipt.outcomeStatus
may have two value:
0
Tx execute success1
Tx execute failed
Correspond to transaction stages, the SDK has provide several advanced sendTransaction
usage:
const { Conflux, Drip } = require('js-conflux-sdk');
const PRIVATE_KEY = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; // sender private key
const ADDRESS = 'cfxtest:aaawgvnhveawgvnhveawgvnhveawgvnhvey1umfzwp';
async function main() {
const conflux = new Conflux({
url: 'https://test.confluxrpc.com',
networkId: 1,
});
const sender = conflux.wallet.addPrivateKey(PRIVATE_KEY);
const pendingTransaction = conflux.cfx.sendTransaction({
from: sender.address, // account address or instance which added into conflux.wallet
to: ADDRESS, // receiver address
value: Drip.fromCFX(0.1), // 0.1 CFX = 100000000000000000 Drip
}); // NOTE: without await, transaction not send yet
const transactionHash = await pendingTransaction; // send and await endpoint return transaction hash
console.log(transactionHash);
// usually wait about 2 seconds
const packedTransaction = await pendingTransaction.get(); // await endpoint packed transaction
console.log(packedTransaction); // `blockHash` might still be `null`
// usually wait about 5 seconds
const minedTransaction = await pendingTransaction.mined(); // await transaction mined
console.log(minedTransaction); // already have `blockHash`
// usually wait about 10 seconds
const executedReceipt = await pendingTransaction.executed(); // await transaction executed
console.log(executedReceipt); // if `outcomeStatus` equal 0, return receipt, else throw error
// usually wait about 50 seconds
const confirmedReceipt = await pendingTransaction.confirmed(); // await transaction confirmed
console.log(confirmedReceipt); // usually same as executedReceipt, but transaction block risk is <= 1e-8
}
main();
Note: the mined
, executed
, confirmed
methods maybe will take a long time, even will be timeout.
Besides from
, to
, value
there are several other fields could be filled with:
nonce
: optional, the nonce of a transaction to keep the order of your sending transactions, increase one by one. If missing, the result of cfx_getNextNonce will be automatically filled in and it works for general scenarios. Some cases, like sending a lot of transactions in a short period. It's recommended to maintain the nonce on your own.gasPrice
: optional, the price in Drip that you would like to pay for each gas consumed. If missing, the result of cfx_gasPrice will be automatically filled in, which is the median of recent transactions.gas
: optional, the max gas you would like to use in the transaction. After the end of transaction processing, the unused gas will be refunded if used_gas >= gas * 0.75. If missing, the result of cfx_estimateGasAndCollateral will be automatically filled in and it works for general scenarios.to
: the receiver of the transaction, could be a personal account(start with 1) or contract(start with 8). Leave a null here to deploy a contract.value
: the value (in Drip) to be transferred.storageLimit
: optional, the max storage (in Byte) you would like to collateralize in the transaction. If missing, the result of cfx_estimateGasAndCollateral will be automatically filled in and it works for general senarios.epochHeight
: optional, a transaction is can be verified only in epochs in the range [epochHeight - 10000, epochHeight + 10000], so it's a timeout mechanism. If missing, the result of cfx_epochNumber will be automatically filled in and it works for general scenarios.data
: optional, it's either an attached message of a transaction or a function signature of a contract call. If missing, a null will be filled into it.chainId
: optional, it used for dealing with a hard fork or preventing a transaction replay attack. If missing SDK will get it from RPC.from
: The sender account(with private key) to sign the transaction.
Compare with ethereum Conflux transaction have three new field: storageLimit
, epochHeight
, chainId
const { Conflux, Drip } = require('js-conflux-sdk');
const PRIVATE_KEY = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; // sender private key
const ADDRESS = 'cfxtest:aaawgvnhveawgvnhveawgvnhveawgvnhvey1umfzwp';
async function main() {
const conflux = new Conflux({
url: 'https://test.confluxrpc.com',
networkId: 1,
});
const account = conflux.wallet.addPrivateKey(PRIVATE_KEY); // create account instance and add to wallet
const estimate = await conflux.cfx.estimateGasAndCollateral({ to, value });
const status = await conflux.cfx.getStatus();
const transactionHash = await conflux.cfx.sendTransaction({
from: account.address, // sender address which added into conflux.wallet
to: ADDRESS, // receiver address
value: Drip.fromCFX(0.1), // 0.1 CFX = 100000000000000000 Drip
data: null,
gas: estimate.gasUsed,
storageLimit: 0,
chainId: status.chainId,
nonce: await conflux.getNextNonce(account.address),
gasPrice: await conflux.getGasPrice(),
epochHeight: await conflux.getEpochNumber(),
});
console.log(transactionHash);
}
main();
If you are just transfer CFX, the gas should be 21000
, if you are interact with a contract it's a little complicated. Normally you can estimate it by call estimateGasAndCollateral
.
const conflux = new Conflux({
url: 'https://test.confluxrpc.com',
logger: console, // for debug
defaultGasPrice: 1,
defaultGasRatio: 1.1,
defaultStorageRatio: 1.1,
networkId: 1,
});
When sending transaction if you specify the gasPrice
it will be used, if not it will try to use cfx.defaultGasPrice
you can specify it when you initiallize the cfx object.
If both gasPrice
and defaultGasPrice
is not specified, the SDK will fill the result of getGasPrice()
to it.
Currently set the gasPrice to 1 Drip
will enough to send most transactions.
First you can specify the tx.gas
, if you don't it will use the result of estimateGasAndCollateral.gasUsed
, and this value will be multiply a ratio defaultGasRatio
(default 1.1), because the estimate result sometimes are not accurate, normally estimated gasUsed
will not enough.
Same as gas
you can specify storageLimit
when you send transaction (unit is Byte), if not
The sendTransaction
will get from
's privateKey from wallet, use it sign transaction and invoke sendRawTransaction
method, if you have a raw transaction you can use it directly. the sendRawTransaction
method also can use get
, mined
, executed
, confirmed
method.
const { Conflux, Transaction } = require('js-conflux-sdk');
const PRIVATE_KEY = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; // sender private key
const ADDRESS = 'cfxtest:aaawgvnhveawgvnhveawgvnhveawgvnhvey1umfzwp';
async function main() {
const conflux = new Conflux({
url: 'https://test.confluxrpc.com',
networkId: 1,
});
const transaction = new Transaction({
to: ADDRESS, // receiver address
nonce: 1, // sender next nonce
value: 1,
gas: 21000,
gasPrice: 1,
storageLimit: 0,
epochHeight: 100,
chainId: 1, // endpoint status.chainId
data: '0x'
});
transaction.sign(PRIVATE_KEY, 1); // sender privateKey
const transactionHash = await conflux.cfx.sendRawTransaction(transaction.serialize());
console.log(transactionHash)
}
main();
If your transaction always pending in the pool, normally you have use a incorrect nonce
or your balance
is not enough. You can get the transaction info by it's hash, and check it's nonce with your account's nonce, and check your balance is enough to cover the value + gasPrice * gas + storageLimit.
If your transaction is failed, you can find reason in below ways:
- Is your balance is enough cover your tx: check
value
,gas
,storageLimit
- Is your
gas
is enough, is yourstorageLimit
enough - Check receipt's
txExecErrorMsg
for detail error message
If you want send a transaction with a note, you can specify it through data
, the tricky is you need convert your message to a hex string.
const { Conflux, format } = require('js-conflux-sdk');
const PRIVATE_KEY = '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; // sender private key
async function main() {
const conflux = new Conflux({
url: 'https://test.confluxrpc.com',
networkId: 1,
});
const account = conflux.wallet.addPrivateKey(PRIVATE_KEY); // create account instance and add to wallet
const transactionHash = await conflux.cfx.sendTransaction({
from: account.address,
to: account.address, // if data is not contract bytecode, must have `to` address
data: format.bytes('Hello World')
});
console.log(transactionHash);
}
main()