UNPKG

5.51 kBJavaScriptView Raw
1const lodash = require('lodash');
2const ContractABICoder = require('./ContractABICoder');
3const ContractConstructor = require('./ContractConstructor');
4const ContractMethod = require('./ContractMethod');
5const ContractEvent = require('./ContractEvent');
6
7/**
8 * Contract with all its methods and events defined in its abi.
9 */
10class Contract {
11 /**
12 *
13 * @param cfx {Conflux} - Conflux instance.
14 * @param options {object}
15 * @param options.abi {array} - The json interface for the contract to instantiate
16 * @param [options.address] {string} - The address of the smart contract to call, can be added later using `contract.address = '0x1234...'`
17 * @param [options.bytecode] {string} - The byte code of the contract, can be added later using `contract.constructor.code = '0x1234...'`
18 * @return {object}
19 *
20 * @example
21 * > const contract = cfx.Contract({ abi, bytecode });
22
23 * > contract.constructor.bytecode; // input code
24 "0x6080604052600080..."
25
26 * @example
27 * > const contract = cfx.Contract({ abi, address });
28 * > contract.address
29 "0xc3ed1a06471be1d3bcd014051fbe078387ec0ad8"
30
31 * > await contract.count(); // call a method without parameter, get decoded return value.
32 100n
33 * > await contract.inc(1); // call a method with parameters, get decoded return value.
34 101n
35 * > await contract.count().call({ from: account }); // call a method from a account.
36 100n
37 * > await contract.count().estimateGas();
38 21655n
39 * > await contract.count().estimateGas({ from: ADDRESS, nonce: 68 }); // if from is a address string, nonce is required
40 21655n
41
42 * // send transaction from account instance, then wait till confirmed, and get receipt.
43 * > await contract.inc(1)
44 .sendTransaction({ from: account1 })
45 .confirmed({ threshold: 0.01, timeout: 30 * 1000 });
46 {
47 "blockHash": "0xba948c8925f6d7f14faf540c3b9e6d24d33c78168b2dd81a6021a50949d9f0d7",
48 "index": 0,
49 "transactionHash": "0x8a5f48c2de0f1bdacfe90443810ad650e4b327a0d19ce49a53faffb224883e42",
50 "outcomeStatus": 0,
51 ...
52 }
53
54 * > tx = await cfx.getTransactionByHash('0x8a5f48c2de0f1bdacfe90443810ad650e4b327a0d19ce49a53faffb224883e42');
55 * > await contract.abi.decodeData(tx.data)
56 {
57 name: 'inc',
58 fullName: 'inc(uint256 num)',
59 type: 'inc(uint256)',
60 signature: '0x7f98a45e',
61 array: [ JSBI.BigInt(101) ],
62 object: { num: JSBI.BigInt(101) }
63 }
64
65 * > await contract.count(); // data in block chain changed by transaction.
66 JSBI.BigInt(101)
67
68 * > logs = await contract.SelfEvent(account1.address).getLogs()
69 [
70 {
71 address: '0xc3ed1a06471be1d3bcd014051fbe078387ec0ad8',
72 blockHash: '0xc8cb678891d4914aa66670e3ebd7a977bb3e38d2cdb1e2df4c0556cb2c4715a4',
73 data: '0x000000000000000000000000000000000000000000000000000000000000000a',
74 epochNumber: 545896,
75 logIndex: 0,
76 removed: false,
77 topics: [
78 '0xc4c01f6de493c58245fb681341f3a76bba9551ce81b11cbbb5d6d297844594df',
79 '0x000000000000000000000000bbd9e9be525ab967e633bcdaeac8bd5723ed4d6b'
80 ],
81 transactionHash: '0x9100f4f84f711aa358e140197e9d2e5aab1f99751bc26a660d324a8282fc54d0',
82 transactionIndex: 0,
83 transactionLogIndex: 0,
84 type: 'mined',
85 params: [ '0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b', '10' ]
86 }
87 ]
88
89 * > contract.abi.decodeLog(logs[0]);
90 {
91 name: 'SelfEvent',
92 fullName: 'SelfEvent(address indexed sender, uint256 current)',
93 type: 'SelfEvent(address,uint256))',
94 signature: '0xc4c01f6de493c58245fb681341f3a76bba9551ce81b11cbbb5d6d297844594df',
95 array: [ '0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b', JSBI.BigInt(100) ],
96 object: {
97 sender: '0xbbd9e9be525ab967e633bcdaeac8bd5723ed4d6b',
98 current: JSBI.BigInt(100),
99 },
100 }
101 */
102 constructor(cfx, { abi, address, bytecode }) {
103 const abiTable = lodash.groupBy(abi, 'type');
104
105 this.constructor = new ContractConstructor(cfx, this, lodash.first(abiTable.constructor), bytecode);
106 this.abi = new ContractABICoder(this); // XXX: Create a method named `abi` in solidity is a `Warning`.
107 this.address = address; // XXX: Create a method named `address` in solidity is a `ParserError`
108
109 const methodArray = lodash.map(abiTable.function, fragment => new ContractMethod(cfx, this, fragment));
110 const eventArray = lodash.map(abiTable.event, fragment => new ContractEvent(cfx, this, fragment));
111
112 // name to instance
113 lodash.forEach(lodash.groupBy(methodArray, 'name'), (array, name) => {
114 this[name] = array.length === 1
115 ? lodash.first(array) // no override
116 : new ContractMethod.ContractMethodOverride(cfx, this, array);
117 });
118 lodash.forEach(lodash.groupBy(eventArray, 'name'), (array, name) => {
119 this[name] = array.length === 1
120 ? lodash.first(array) // no override
121 : new ContractEvent.ContractEventOverride(cfx, this, array);
122 });
123
124 // type to instance
125 // signature for contract abi decoder to decode
126 methodArray.forEach(method => {
127 this[method.type] = method;
128 this[method.signature] = method; // signature for contract abi decoder to decode
129 });
130 eventArray.forEach(event => {
131 this[event.type] = event;
132 this[event.signature] = event; // signature for contract abi decoder to decode
133 });
134 }
135}
136
137module.exports = Contract;