parent
e0b73bb178
commit
eac87f49f3
@ -0,0 +1,19 @@ |
||||
pragma solidity ^0.5.1; |
||||
|
||||
contract Calc { |
||||
|
||||
uint count; |
||||
|
||||
function getCount() public returns (uint) { |
||||
|
||||
return count; |
||||
|
||||
} |
||||
|
||||
function add(uint a, uint b) public returns (uint) { |
||||
count++; |
||||
return a + b; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,8 @@ |
||||
pragma solidity ^0.5.1; |
||||
|
||||
|
||||
contract MyContract { |
||||
function myFunction() public returns(uint256 myNumber, string memory myString) { |
||||
return (23456, "Hello!%"); |
||||
} |
||||
} |
@ -0,0 +1,23 @@ |
||||
pragma solidity ^0.5.1; |
||||
|
||||
contract SimpleStorage { |
||||
|
||||
event ValueChanged(address indexed author, string oldValue, string newValue); |
||||
|
||||
string _value; |
||||
|
||||
constructor(string memory value) public { |
||||
emit ValueChanged(msg.sender, _value, value); |
||||
_value = value; |
||||
} |
||||
|
||||
function getValue() view public returns (string memory) { |
||||
return _value; |
||||
} |
||||
|
||||
function setValue(string memory value) public { |
||||
emit ValueChanged(msg.sender, _value, value); |
||||
_value = value; |
||||
} |
||||
|
||||
} |
@ -1,57 +1,212 @@ |
||||
const { AbiCoder, toUtf8Bytes } = require('@harmony/contract'); |
||||
const abiDecoder = require('abi-decoder'); |
||||
const { |
||||
keccak256, |
||||
hexToByteArray, |
||||
hexToIntArray, |
||||
arrayify, |
||||
hexlify, |
||||
padZeros, |
||||
bytesPadRight, |
||||
bytesPadLeft, |
||||
|
||||
BN, |
||||
} = require('@harmony/crypto'); |
||||
const { hexToBN } = require('@harmony/utils'); |
||||
|
||||
const abiCoder = new AbiCoder(); |
||||
// console.log(arrayify('8bd02b7b'));
|
||||
|
||||
// const encoded = abiCoder.encodeParameter('bytes32', '0xdf3234');
|
||||
// console.log(encoded);
|
||||
|
||||
const encoded = abiCoder.encodeParameter('uint256', '2345675643'); |
||||
console.log(encoded); |
||||
|
||||
const decoded = abiCoder.decodeLog( |
||||
[ |
||||
{ |
||||
type: 'string', |
||||
name: 'myString', |
||||
const { Harmony } = require('@harmony/core'); |
||||
const { BN } = require('@harmony/crypto'); |
||||
const { isArray, ChainType, ChainID } = require('@harmony/utils'); |
||||
const fs = require('fs'); |
||||
const solc = require('solc'); |
||||
|
||||
function constructInput(file) { |
||||
const content = fs.readFileSync(`./contracts/${file}`, { encoding: 'utf8' }); |
||||
const input = { |
||||
language: 'Solidity', |
||||
sources: {}, |
||||
settings: { |
||||
outputSelection: { |
||||
'*': { |
||||
'*': ['*'], |
||||
}, |
||||
{ |
||||
type: 'uint256', |
||||
name: 'myNumber', |
||||
indexed: true, |
||||
}, |
||||
{ |
||||
type: 'uint8', |
||||
name: 'mySmallNumber', |
||||
indexed: true, |
||||
}, |
||||
], |
||||
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000748656c6c6f252100000000000000000000000000000000000000000000000000', |
||||
[ |
||||
'0x000000000000000000000000000000000000000000000000000000000000f310', |
||||
'0x0000000000000000000000000000000000000000000000000000000000000010', |
||||
], |
||||
}; |
||||
|
||||
input.sources[file] = { content }; |
||||
return JSON.stringify(input); |
||||
} |
||||
const fileName = 'MyContract.sol'; |
||||
|
||||
const output = JSON.parse(solc.compile(constructInput(fileName))); |
||||
|
||||
let abi; |
||||
let bin; |
||||
// `output` here contains the JSON output as specified in the documentation
|
||||
for (var contractName in output.contracts[fileName]) { |
||||
let contractAbi = output.contracts[fileName][contractName].abi; |
||||
let contractBin = |
||||
output.contracts[fileName][contractName].evm.bytecode.object; |
||||
if (contractAbi) { |
||||
abi = contractAbi; |
||||
} |
||||
if (contractBin) { |
||||
bin = contractBin; |
||||
} |
||||
} |
||||
|
||||
// const harmony = new Harmony('ws://localhost:18545', 1);
|
||||
const harmony = new Harmony( |
||||
// 'https://ropsten.infura.io/v3/4f3be7f5bbe644b7a8d95c151c8f52ec',
|
||||
'wss://Ropsten.infura.io/ws/v3/4f3be7f5bbe644b7a8d95c151c8f52ec', |
||||
// 'https://testnet-rpc.thundercore.com:8544',
|
||||
ChainType.Ethereum, |
||||
ChainID.Ropsten, |
||||
); |
||||
console.log(decoded); |
||||
|
||||
// const mm = hexToBN(
|
||||
// '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
// );
|
||||
const mne = |
||||
'food response winner warfare indicate visual hundred toilet jealous okay relief tornado'; |
||||
const acc1 = harmony.wallet.addByMnemonic(mne, 0); |
||||
|
||||
const myContract = harmony.contracts.createContract(abi); |
||||
|
||||
acc1.getBalance().then((res) => { |
||||
console.log(`-- hint: account balance of ${acc1.address}`); |
||||
console.log(``); |
||||
console.log({ account: res }); |
||||
console.log(``); |
||||
console.log(``); |
||||
}); |
||||
|
||||
const deployContract = async () => { |
||||
const deployed = await myContract |
||||
.deploy({ |
||||
data: `0x${bin}`, |
||||
arguments: [], |
||||
}) |
||||
.send({ |
||||
gasLimit: new harmony.crypto.BN('1000000'), |
||||
gasPrice: new harmony.crypto.BN('10000'), |
||||
}) |
||||
.on('transactionHash', (transactionHash) => { |
||||
console.log(`-- hint: we got Transaction Hash`); |
||||
console.log(``); |
||||
console.log(`${transactionHash}`); |
||||
console.log(``); |
||||
console.log(``); |
||||
|
||||
harmony.blockchain |
||||
.getTransactionByHash({ |
||||
txnHash: transactionHash, |
||||
}) |
||||
.then((res) => { |
||||
console.log(`-- hint: we got transaction detail`); |
||||
console.log(``); |
||||
console.log(res); |
||||
console.log(``); |
||||
console.log(``); |
||||
}); |
||||
}) |
||||
.on('receipt', (receipt) => { |
||||
console.log(`-- hint: we got transaction receipt`); |
||||
console.log(``); |
||||
console.log(receipt); |
||||
console.log(``); |
||||
console.log(``); |
||||
}) |
||||
.on('confirmation', (confirmation) => { |
||||
console.log(`-- hint: the transaction is`); |
||||
console.log(``); |
||||
console.log(confirmation); |
||||
console.log(``); |
||||
console.log(``); |
||||
}) |
||||
.on('error', (error) => { |
||||
console.log(`-- hint: someting wrong happens`); |
||||
console.log(``); |
||||
console.log(error); |
||||
console.log(``); |
||||
console.log(``); |
||||
}); |
||||
return deployed; |
||||
}; |
||||
|
||||
deployContract().then((deployed) => { |
||||
harmony.blockchain.getCode({ address: deployed.address }).then((res) => { |
||||
if (res.result) { |
||||
console.log(`--hint: contract :${deployed.address}--`); |
||||
console.log(``); |
||||
console.log(`${res.result}`); |
||||
console.log(``); |
||||
console.log(``); |
||||
deployed.methods |
||||
.myFunction() |
||||
.call() |
||||
.then((result) => { |
||||
console.log(`--hint: we got contract called, this is result`); |
||||
console.log(``); |
||||
console.log(result); |
||||
console.log(``); |
||||
console.log(``); |
||||
}); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
// harmony.blockchain.newPendingTransactions().then((p) => {
|
||||
// console.log({ txns: p });
|
||||
// p.onData(async (res) => {
|
||||
// const txn = await harmony.blockchain.getTransactionByHash({
|
||||
// txnHash: res.params.result,
|
||||
// });
|
||||
// console.log(txn);
|
||||
// });
|
||||
// });
|
||||
|
||||
// const getValue = async (address) => {
|
||||
// const newContract = harmony.contracts.createContract(abi, address);
|
||||
|
||||
// const value = await newContract.methods
|
||||
// .getValue()
|
||||
// .call({ from: acc1.address });
|
||||
// return value;
|
||||
// };
|
||||
|
||||
// async function setValue(address) {
|
||||
// const newContract = harmony.contracts.createContract(abi, address);
|
||||
|
||||
// await newContract.methods.setValue('KKKK').call({
|
||||
// from: acc1.address,
|
||||
// });
|
||||
// }
|
||||
|
||||
// async function myfunc(address) {
|
||||
// const newContract = harmony.contracts.createContract(abi, address);
|
||||
// const result = await newContract.methods
|
||||
// .myFunction()
|
||||
// .call({ from: acc1.address });
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// const contractAddress = '0xbdce52076e5d8b95cadf5086ff429e33ce641374';
|
||||
|
||||
// const newContract = harmony.contracts.createContract(abi, contractAddress);
|
||||
|
||||
// harmony.blockchain.getCode({ address: contractAddress }).then(console.log);
|
||||
|
||||
// newContract.methods
|
||||
// .myFunction()
|
||||
// .call({ from: acc1.address, gas: new harmony.crypto.BN(30000000) }, 'latest')
|
||||
// .then(console.log);
|
||||
|
||||
// deployContract().then((deployed) => {
|
||||
// deployed.methods
|
||||
// .add(1, 3)
|
||||
// .call()
|
||||
// .then(console.log);
|
||||
// });
|
||||
|
||||
// myContract.methods
|
||||
// .getValue()
|
||||
// .call({ from: acc1.address })
|
||||
// .then((res) => {
|
||||
// console.log(res);
|
||||
// });
|
||||
// myContract.methods
|
||||
// .setValue('shit!')
|
||||
// .call({ from: acc1.address })
|
||||
// .then((res) => {
|
||||
// console.log(res);
|
||||
// });
|
||||
|
||||
// const ttt = new BN(434);
|
||||
// console.log(ttt.lt(new BN(0)));
|
||||
// console.log(ttt.gt(mm.maskn(64)));
|
||||
// .on('confirmation', (e) => {
|
||||
// console.log(e);
|
||||
// });
|
||||
// .then((res) => {
|
||||
// console.log(res);
|
||||
// });
|
||||
|
@ -0,0 +1,71 @@ |
||||
import { AbiCoder } from '../src/abi/abiCoder'; |
||||
import { BN } from '@harmony/crypto'; |
||||
import { isArray } from '@harmony/utils'; |
||||
import { abis } from './fixtures/abiv2'; |
||||
|
||||
function getValues(object: any, format?: any, named?: any): any { |
||||
if (isArray(object)) { |
||||
// tslint:disable-next-line: no-shadowed-variable
|
||||
const result: any[] = []; |
||||
// tslint:disable-next-line: no-shadowed-variable
|
||||
object.forEach((object: any) => { |
||||
result.push(getValues(object, format, named)); |
||||
}); |
||||
return result; |
||||
} |
||||
|
||||
switch (object.type) { |
||||
case 'number': |
||||
return new BN(object.value); |
||||
case 'boolean': |
||||
case 'string': |
||||
return object.value; |
||||
|
||||
case 'buffer': |
||||
return object.value; |
||||
|
||||
case 'tuple': |
||||
const result = getValues(object.value, format, named); |
||||
if (named) { |
||||
const namedResult: any = {}; |
||||
result.forEach((value: any, index: number) => { |
||||
namedResult['r' + String(index)] = value; |
||||
}); |
||||
return namedResult; |
||||
} |
||||
return result; |
||||
|
||||
default: |
||||
throw new Error('invalid type - ' + object.type); |
||||
} |
||||
} |
||||
|
||||
describe('test abiv2', () => { |
||||
it('should encode abiv2', () => { |
||||
const coder = new AbiCoder(); |
||||
for (const abiItem of abis) { |
||||
const test: any = abiItem; |
||||
const values = getValues(JSON.parse(test.values)); |
||||
const types = JSON.parse(test.types); |
||||
const expected = test.result; |
||||
const encoded = coder.encode(types, values); |
||||
|
||||
expect(JSON.stringify(encoded)).toEqual(JSON.stringify(expected)); |
||||
|
||||
const namedEncoded = coder.encode(types, values); |
||||
|
||||
expect(JSON.stringify(namedEncoded)).toEqual(JSON.stringify(expected)); |
||||
} |
||||
}); |
||||
it('should decode abiv2', () => { |
||||
const coder = new AbiCoder(); |
||||
for (const abiItem of abis) { |
||||
const test = abiItem; |
||||
const values = getValues(JSON.parse(test.values)); |
||||
const types = JSON.parse(test.types); |
||||
const expected = test.result; |
||||
const decoded = coder.decode(types, expected); |
||||
expect(JSON.stringify(decoded)).toEqual(JSON.stringify(values)); |
||||
} |
||||
}); |
||||
}); |
File diff suppressed because one or more lines are too long
@ -1,34 +0,0 @@ |
||||
interface AbiModel { |
||||
getMethod(name: string): AbiItemModel | false; |
||||
getMethods(): AbiItemModel[]; |
||||
hasMethod(name: string): boolean; |
||||
getEvent(name: string): AbiItemModel | false; |
||||
getEvents(): AbiItemModel[]; |
||||
getEventBySignature(signature: string): AbiItemModel; |
||||
hasEvent(name: string): boolean; |
||||
} |
||||
|
||||
interface AbiItemModel { |
||||
name: string; |
||||
signature: string; |
||||
payable: boolean; |
||||
anonymous: boolean; |
||||
getInputLength(): Number; |
||||
getInputs(): AbiInput[]; |
||||
getIndexedInputs(): AbiInput[]; |
||||
getOutputs(): AbiOutput[]; |
||||
isOfType(): boolean; |
||||
} |
||||
|
||||
interface AbiInput { |
||||
name: string; |
||||
type: string; |
||||
indexed?: boolean; |
||||
components?: AbiInput[]; |
||||
} |
||||
|
||||
interface AbiOutput { |
||||
name: string; |
||||
type: string; |
||||
components?: AbiOutput[]; |
||||
} |
@ -0,0 +1,103 @@ |
||||
import { Wallet } from '@harmony/account'; |
||||
// import { Emitter } from '@harmony/network';
|
||||
import { Transaction } from '@harmony/transaction'; |
||||
import { AbiCoder } from './abi/index'; |
||||
import { abiMapper } from './utils/mapper'; |
||||
import { ContractOptions } from './utils/options'; |
||||
import { AbiModel } from './models/types'; |
||||
import { AbiCoderClass } from './abi/api'; |
||||
import { MethodFactory } from './methods/methodFactory'; |
||||
import { EventFactory } from './events/eventFactory'; |
||||
import { ContractStatus } from './utils/status'; |
||||
|
||||
// class Contract
|
||||
export class Contract { |
||||
methods: any; |
||||
events: any; |
||||
abiModel: any | AbiModel; |
||||
abiCoder: AbiCoderClass; |
||||
options: ContractOptions | any; |
||||
wallet: Wallet; |
||||
transaction?: Transaction; |
||||
status: ContractStatus; |
||||
|
||||
constructor( |
||||
abi: any = [], |
||||
address: string = '0x', |
||||
options: ContractOptions = {}, |
||||
wallet: Wallet, |
||||
status: ContractStatus = ContractStatus.INITIALISED, |
||||
) { |
||||
// super();
|
||||
this.abiCoder = AbiCoder(); |
||||
this.abiModel = abiMapper(abi, this.abiCoder); |
||||
this.options = options; |
||||
this.address = this.options.address || address; |
||||
this.wallet = wallet; |
||||
this.methods = {}; |
||||
this.events = {}; |
||||
this.runMethodFactory(); |
||||
this.runEventFactory(); |
||||
this.status = status; |
||||
// tslint:disable-next-line: no-unused-expression
|
||||
} |
||||
isInitialised() { |
||||
return this.status === ContractStatus.INITIALISED; |
||||
} |
||||
isSigned() { |
||||
return this.status === ContractStatus.SIGNED; |
||||
} |
||||
isSent() { |
||||
return this.status === ContractStatus.SENT; |
||||
} |
||||
isDeployed() { |
||||
return this.status === ContractStatus.DEPLOYED; |
||||
} |
||||
isRejected() { |
||||
return this.status === ContractStatus.REJECTED; |
||||
} |
||||
isCalled() { |
||||
return this.status === ContractStatus.CALLED; |
||||
} |
||||
setStatus(status: ContractStatus) { |
||||
this.status = status; |
||||
} |
||||
|
||||
get jsonInterface(): any[] { |
||||
return this.abiModel; |
||||
} |
||||
|
||||
set jsonInterface(value: any[]) { |
||||
this.abiModel = abiMapper(value, this.abiCoder); |
||||
this.runMethodFactory(); |
||||
this.runEventFactory(); |
||||
} |
||||
|
||||
get address() { |
||||
return this.options.address || this.address; |
||||
} |
||||
|
||||
set address(value: string) { |
||||
this.options.address = value; |
||||
} |
||||
|
||||
get data() { |
||||
return this.options.data; |
||||
} |
||||
|
||||
set data(value) { |
||||
this.options.data = value; |
||||
} |
||||
|
||||
// deploy
|
||||
deploy(options: any) { |
||||
return this.methods.contractConstructor(options); |
||||
} |
||||
|
||||
runMethodFactory(): Contract { |
||||
return new MethodFactory(this).addMethodsToContract(); |
||||
} |
||||
runEventFactory(): Contract { |
||||
return new EventFactory(this).addEventsToContract(); |
||||
} |
||||
} |
@ -0,0 +1,14 @@ |
||||
import { Wallet } from '@harmony/account'; |
||||
import { Contract } from './contract'; |
||||
import { ContractOptions } from './utils/options'; |
||||
|
||||
export class ContractFactory { |
||||
wallet: Wallet; |
||||
|
||||
constructor(wallet: Wallet) { |
||||
this.wallet = wallet; |
||||
} |
||||
createContract(abi: any[], address?: string, options?: ContractOptions) { |
||||
return new Contract(abi, address, options, this.wallet); |
||||
} |
||||
} |
@ -0,0 +1,90 @@ |
||||
import { AbiItemModel } from '../models/types'; |
||||
import { Messenger, RPCMethod, WSProvider } from '@harmony/network'; |
||||
import { Contract } from '../contract'; |
||||
import { decode as eventLogDecoder } from '../utils/decoder'; |
||||
import { inputLogFormatter, outputLogFormatter } from '../utils/formatter'; |
||||
export class EventMethod { |
||||
params: any; |
||||
methodKey: string; |
||||
contract: Contract; |
||||
messenger?: Messenger; |
||||
abiItem: AbiItemModel; |
||||
constructor( |
||||
methodKey: string, |
||||
params: any, |
||||
abiItem: AbiItemModel, |
||||
contract: Contract, |
||||
) { |
||||
this.methodKey = methodKey; |
||||
this.contract = contract; |
||||
this.messenger = contract.wallet.messenger; |
||||
this.params = params; |
||||
this.abiItem = abiItem; |
||||
} |
||||
send() {} |
||||
call() {} |
||||
estimateGas() {} |
||||
encodeABI() {} |
||||
subscribe(options: any) { |
||||
if ( |
||||
options && |
||||
typeof options.filter !== 'undefined' && |
||||
typeof options.topics !== 'undefined' |
||||
) { |
||||
throw new Error( |
||||
'Invalid subscription options: Only filter or topics are allowed and not both', |
||||
); |
||||
} |
||||
if (this.emitter && this.messenger) { |
||||
const messenger = this.messenger; |
||||
const emitter = this.emitter; |
||||
const inputOptions = inputLogFormatter(options); |
||||
// 1. getLog
|
||||
// 2. subscribe pastlog
|
||||
// 3. emit data
|
||||
messenger |
||||
.send(RPCMethod.GetPastLogs, [inputOptions]) |
||||
.then((logs: any) => { |
||||
logs.forEach((log: any) => { |
||||
const formattedLog = this.onNewSubscriptionItem(log); |
||||
emitter.emit('data', formattedLog); |
||||
}); |
||||
messenger.subscribe('logs', [inputOptions] || []); |
||||
}) |
||||
.catch((error) => { |
||||
emitter.emit('error', error); |
||||
}); |
||||
} |
||||
return this.contract; |
||||
// return this.eventSubscriptionFactory
|
||||
// .createEventLogSubscription(
|
||||
// this.eventLogDecoder,
|
||||
// this.contract,
|
||||
// this.eventOptionsMapper.map(abiItemModel, this.contract, options),
|
||||
// abiItemModel,
|
||||
// )
|
||||
// .subscribe(callback);
|
||||
// this.messenger.subscribe()
|
||||
} |
||||
onNewSubscriptionItem(subscriptionItem: any) { |
||||
// const log = outputLogFormatter(subscriptionItem);
|
||||
const log = eventLogDecoder( |
||||
this.contract.abiCoder, |
||||
this.abiItem, |
||||
outputLogFormatter(subscriptionItem), |
||||
); |
||||
|
||||
if (log.removed && this.emitter) { |
||||
this.emitter.emit('changed', log); |
||||
} |
||||
|
||||
return log; |
||||
} |
||||
get emitter() { |
||||
if (this.messenger && this.messenger.provider instanceof WSProvider) { |
||||
return this.messenger.provider.emitter; |
||||
} else { |
||||
return undefined; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,41 @@ |
||||
import { AbiCoderClass } from '../abi/api'; |
||||
import { AbiModel } from '../models/types'; |
||||
import { Contract } from '../contract'; |
||||
import { EventMethod } from './event'; |
||||
|
||||
export class EventFactory { |
||||
contract: Contract; |
||||
abiModel: any | AbiModel; |
||||
abiCoder: AbiCoderClass; |
||||
private eventKeys: string[]; |
||||
|
||||
// constructor
|
||||
constructor(contract: Contract) { |
||||
this.contract = contract; |
||||
this.abiModel = this.contract.abiModel; |
||||
this.abiCoder = this.contract.abiCoder; |
||||
this.eventKeys = this.mapEventKeys(); |
||||
} |
||||
|
||||
addEventsToContract() { |
||||
this.eventKeys.forEach((key: string) => { |
||||
const newObject: any = {}; |
||||
newObject[key] = (params: any) => |
||||
new EventMethod( |
||||
key, |
||||
params, |
||||
this.abiModel.getEvent(key), |
||||
this.contract, |
||||
); |
||||
Object.assign(this.contract.events, newObject); |
||||
}); |
||||
return this.contract; |
||||
} |
||||
/** |
||||
* @function mapMethodKeys |
||||
* @return {string[]} {description} |
||||
*/ |
||||
private mapEventKeys(): string[] { |
||||
return Object.keys(this.abiModel.abi.events); |
||||
} |
||||
} |
@ -0,0 +1,222 @@ |
||||
import { Wallet } from '@harmony/account'; |
||||
import { |
||||
TransactionFactory, |
||||
Transaction, |
||||
// TxStatus,
|
||||
} from '@harmony/transaction'; |
||||
import { RPCMethod, getResultForData } from '@harmony/network'; |
||||
import { hexToNumber, hexToBN } from '@harmony/utils'; |
||||
import { AbiItemModel } from '../models/types'; |
||||
import { Contract } from '../contract'; |
||||
import { methodEncoder } from '../utils/encoder'; |
||||
import { ContractStatus } from '../utils/status'; |
||||
|
||||
// todo: have to judge if it is contractConstructor
|
||||
|
||||
export class ContractMethod { |
||||
contract: Contract; |
||||
params: any; |
||||
methodKey: string; |
||||
wallet: Wallet; |
||||
abiItem: AbiItemModel; |
||||
|
||||
protected transaction: Transaction; |
||||
constructor( |
||||
methodKey: string, |
||||
params: any, |
||||
abiItem: AbiItemModel, |
||||
contract: Contract, |
||||
) { |
||||
this.methodKey = methodKey; |
||||
this.contract = contract; |
||||
this.wallet = contract.wallet; |
||||
this.params = params; |
||||
this.abiItem = abiItem; |
||||
this.transaction = this.createTransaction(); |
||||
|
||||
// this.addEventListeners();
|
||||
} |
||||
send(params: any) { |
||||
try { |
||||
this.transaction = this.transaction.map((tx: any) => { |
||||
return { ...tx, ...params }; |
||||
}); |
||||
|
||||
this.signTransaction().then((signed) => { |
||||
this.sendTransaction(signed).then((sent) => { |
||||
const [txn, id] = sent; |
||||
this.transaction = txn; |
||||
this.contract.transaction = this.transaction; |
||||
this.confirm(id).then(() => { |
||||
this.transaction.emitter.resolve(this.contract); |
||||
}); |
||||
}); |
||||
}); |
||||
return this.transaction.emitter; |
||||
} catch (error) { |
||||
throw error; |
||||
} |
||||
} |
||||
async call(options: any, blockNumber: any = 'latest') { |
||||
try { |
||||
const nonce = getResultForData( |
||||
await this.wallet.messenger.send(RPCMethod.GetTransactionCount, [ |
||||
this.wallet.signer ? this.wallet.signer.address : options.from, |
||||
blockNumber, |
||||
]), |
||||
); |
||||
|
||||
let gasLimit: any; |
||||
// tslint:disable-next-line: prefer-conditional-expression
|
||||
if (options) { |
||||
gasLimit = options.gas || options.gasLimit; |
||||
} else { |
||||
gasLimit = hexToBN(await this.estimateGas()); |
||||
} |
||||
|
||||
this.transaction = this.transaction.map((tx: any) => { |
||||
return { |
||||
...tx, |
||||
...options, |
||||
from: options |
||||
? options.from |
||||
: this.wallet.signer |
||||
? this.wallet.signer.address |
||||
: tx.from, |
||||
gasPrice: options ? options.gasPrice : tx.gasPrice, |
||||
gasLimit: gasLimit || tx.gasLimit, |
||||
nonce: Number.parseInt(hexToNumber(nonce), 10), |
||||
}; |
||||
}); |
||||
|
||||
const result = getResultForData( |
||||
await this.wallet.messenger.send(RPCMethod.Call, [ |
||||
this.transaction.txPayload, |
||||
blockNumber, |
||||
]), |
||||
); |
||||
if (result.responseType === 'result') { |
||||
return this.afterCall(result); |
||||
} else if (result.responseType === 'error') { |
||||
throw result.message; |
||||
} else { |
||||
return this.afterCall(undefined); |
||||
} |
||||
} catch (error) { |
||||
throw error; |
||||
} |
||||
} |
||||
async estimateGas() { |
||||
try { |
||||
const result = getResultForData( |
||||
await this.wallet.messenger.send(RPCMethod.EstimateGas, [ |
||||
{ |
||||
to: this.transaction.txParams.to, |
||||
data: this.transaction.txParams.data, |
||||
}, |
||||
]), |
||||
); |
||||
if (result.responseType === 'result') { |
||||
return result; |
||||
} else if (result.responseType === 'error') { |
||||
throw result.message; |
||||
} else if (result.responseType === 'raw') { |
||||
throw new Error('Get estimateGas fail'); |
||||
} |
||||
} catch (error) { |
||||
throw error; |
||||
} |
||||
} |
||||
encodeABI() {} |
||||
|
||||
protected async signTransaction() { |
||||
try { |
||||
const signed = await this.wallet.signTransaction( |
||||
this.transaction, |
||||
undefined, |
||||
undefined, |
||||
true, |
||||
'rlp', |
||||
'pending', |
||||
); |
||||
this.contract.address = TransactionFactory.getContractAddress(signed); |
||||
this.contract.setStatus(ContractStatus.SIGNED); |
||||
return signed; |
||||
} catch (error) { |
||||
throw error; |
||||
} |
||||
} |
||||
protected async sendTransaction(signed: Transaction) { |
||||
try { |
||||
const result = await signed.sendTransaction(); |
||||
this.contract.setStatus(ContractStatus.SENT); |
||||
return result; |
||||
} catch (error) { |
||||
throw error; |
||||
} |
||||
} |
||||
protected async confirm(id: string) { |
||||
try { |
||||
const result = await this.transaction.confirm(id); |
||||
if (result.receipt && result.receipt.status === '0x1') { |
||||
if (this.methodKey === 'contractConstructor') { |
||||
this.contract.setStatus(ContractStatus.DEPLOYED); |
||||
} else { |
||||
this.contract.setStatus(ContractStatus.CALLED); |
||||
} |
||||
} else { |
||||
this.contract.setStatus(ContractStatus.REJECTED); |
||||
} |
||||
} catch (error) { |
||||
throw error; |
||||
} |
||||
} |
||||
|
||||
protected createTransaction() { |
||||
if (this.wallet.messenger) { |
||||
if (this.methodKey === 'contractConstructor') { |
||||
// tslint:disable-next-line: no-string-literal
|
||||
this.contract.data = this.params[0]['data'] || '0x'; |
||||
|
||||
this.abiItem.contractMethodParameters = |
||||
// tslint:disable-next-line: no-string-literal
|
||||
this.params[0]['arguments'] || []; |
||||
} else { |
||||
this.abiItem.contractMethodParameters = this.params || []; |
||||
} |
||||
const txObject = { |
||||
...this.params[0], |
||||
to: this.contract.address, |
||||
data: this.encodeData(), |
||||
}; |
||||
|
||||
const result = new TransactionFactory(this.wallet.messenger).newTx( |
||||
txObject, |
||||
); |
||||
return result; |
||||
} else { |
||||
throw new Error('Messenger is not found'); |
||||
} |
||||
} |
||||
|
||||
protected encodeData() { |
||||
return methodEncoder( |
||||
this.contract.abiCoder, |
||||
this.abiItem, |
||||
this.contract.data, |
||||
); |
||||
} |
||||
|
||||
protected afterCall(response: any) { |
||||
if (!response || response === '0x') { |
||||
return null; |
||||
} |
||||
|
||||
const outputs = this.abiItem.getOutputs(); |
||||
if (outputs.length > 1) { |
||||
return this.contract.abiCoder.decodeParameters(outputs, response); |
||||
} |
||||
return this.contract.abiCoder.decodeParameter(outputs[0], response); |
||||
// return outputs;
|
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
import { AbiCoderClass } from '../abi/api'; |
||||
import { AbiModel } from '../models/types'; |
||||
import { Contract } from '../contract'; |
||||
import { ContractMethod } from './method'; |
||||
|
||||
export class MethodFactory { |
||||
contract: Contract; |
||||
abiModel: any | AbiModel; |
||||
abiCoder: AbiCoderClass; |
||||
private methodKeys: string[]; |
||||
|
||||
// constructor
|
||||
constructor(contract: Contract) { |
||||
this.contract = contract; |
||||
this.abiModel = this.contract.abiModel; |
||||
this.abiCoder = this.contract.abiCoder; |
||||
this.methodKeys = this.mapMethodKeys(); |
||||
} |
||||
|
||||
addMethodsToContract() { |
||||
this.methodKeys.forEach((key: string) => { |
||||
const newObject: any = {}; |
||||
newObject[key] = (...params: any[]) => |
||||
new ContractMethod( |
||||
key, |
||||
params, |
||||
this.abiModel.getMethod(key), |
||||
this.contract, |
||||
); |
||||
|
||||
Object.assign(this.contract.methods, newObject); |
||||
}); |
||||
return this.contract; |
||||
} |
||||
/** |
||||
* @function mapMethodKeys |
||||
* @return {string[]} {description} |
||||
*/ |
||||
private mapMethodKeys(): string[] { |
||||
return Object.keys(this.abiModel.abi.methods); |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
import { isArray } from '@harmony/utils'; |
||||
import { AbiItemModel, AbiOutput, AbiInput } from './types'; |
||||
|
||||
export class AbiItem { |
||||
abiItem: AbiItemModel; |
||||
signature: string; |
||||
name: string; |
||||
payable: boolean; |
||||
anonymous: boolean; |
||||
type?: string; |
||||
inputs?: AbiInput[]; |
||||
outputs?: AbiOutput[]; |
||||
contractMethodParameters: any[]; |
||||
|
||||
// constructor
|
||||
constructor(abiItem: AbiItemModel | any) { |
||||
this.abiItem = abiItem; |
||||
this.signature = this.abiItem.signature; |
||||
this.name = this.abiItem.name; |
||||
this.payable = this.abiItem.payable; |
||||
this.anonymous = this.abiItem.anonymous; |
||||
this.type = this.abiItem.type; |
||||
this.inputs = this.abiItem.inputs; |
||||
this.outputs = this.abiItem.outputs; |
||||
this.contractMethodParameters = []; |
||||
} |
||||
|
||||
getInputLength() { |
||||
if (isArray(this.abiItem.inputs)) { |
||||
return this.abiItem.inputs.length; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
getInputs() { |
||||
if (isArray(this.abiItem.inputs)) { |
||||
return this.abiItem.inputs; |
||||
} |
||||
|
||||
return []; |
||||
} |
||||
|
||||
getOutputs() { |
||||
if (isArray(this.abiItem.outputs)) { |
||||
return this.abiItem.outputs; |
||||
} |
||||
|
||||
return []; |
||||
} |
||||
|
||||
getIndexedInputs() { |
||||
return this.getInputs().filter((input) => { |
||||
return input.indexed === true; |
||||
}); |
||||
} |
||||
|
||||
isOfType(type: string) { |
||||
return this.abiItem.type === type; |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
import { AbiItemModel } from './types'; |
||||
|
||||
export class AbiModel { |
||||
abi: any; |
||||
|
||||
constructor(mappedAbi: any) { |
||||
this.abi = mappedAbi; |
||||
} |
||||
|
||||
getMethod(name: string): AbiItemModel | false { |
||||
if (this.hasMethod(name)) { |
||||
return this.abi.methods[name]; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
getMethods(): AbiItemModel[] { |
||||
return this.abi.methods; |
||||
} |
||||
|
||||
getEvent(name: string): AbiItemModel | false { |
||||
if (this.hasEvent(name)) { |
||||
return this.abi.events[name]; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
getEvents(): AbiItemModel[] { |
||||
return this.abi.events; |
||||
} |
||||
|
||||
getEventBySignature(signature: string): AbiItemModel | undefined { |
||||
let event; |
||||
|
||||
Object.keys(this.abi.events).forEach((key) => { |
||||
if (this.abi.events[key].signature === signature) { |
||||
event = this.abi.events[key]; |
||||
} |
||||
}); |
||||
|
||||
return event; |
||||
} |
||||
|
||||
hasMethod(name: string): boolean { |
||||
return typeof this.abi.methods[name] !== 'undefined'; |
||||
} |
||||
|
||||
hasEvent(name: string): boolean { |
||||
return typeof this.abi.events[name] !== 'undefined'; |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
// defined by web3.js
|
||||
// fixed
|
||||
export interface AbiModel { |
||||
getMethod(name: string): AbiItemModel | false; |
||||
getMethods(): AbiItemModel[]; |
||||
hasMethod(name: string): boolean; |
||||
getEvent(name: string): AbiItemModel | false; |
||||
getEvents(): AbiItemModel[]; |
||||
getEventBySignature(signature: string): AbiItemModel | undefined; |
||||
hasEvent(name: string): boolean; |
||||
} |
||||
|
||||
export interface AbiItemModel { |
||||
name: string; |
||||
signature: string; |
||||
payable: boolean; |
||||
anonymous: boolean; |
||||
inputs: AbiInput[]; |
||||
outputs: AbiOutput[]; |
||||
type: string; |
||||
stateMutability?: string; |
||||
constant?: boolean; |
||||
funcName: string; |
||||
contractMethodParameters: any[]; |
||||
getInputLength(): number; |
||||
getInputs(): AbiInput[]; |
||||
getIndexedInputs(): AbiInput[]; |
||||
getOutputs(): AbiOutput[]; |
||||
isOfType(value: string): boolean; |
||||
} |
||||
|
||||
export interface AbiInput { |
||||
name: string; |
||||
type: string; |
||||
indexed?: boolean; |
||||
components?: AbiInput[]; |
||||
} |
||||
|
||||
export interface AbiOutput { |
||||
name: string; |
||||
type: string; |
||||
components?: AbiOutput[]; |
||||
} |
@ -0,0 +1,39 @@ |
||||
import { AbiItemModel } from '../models/types'; |
||||
import { AbiCoderClass } from '../abi/api'; |
||||
|
||||
export const decode = ( |
||||
abiCoder: AbiCoderClass, |
||||
abiItemModel: AbiItemModel, |
||||
response: any, |
||||
) => { |
||||
let argumentTopics = response.topics; |
||||
|
||||
if (!abiItemModel.anonymous) { |
||||
argumentTopics = response.topics.slice(1); |
||||
} |
||||
|
||||
if (response.data === '0x') { |
||||
response.data = null; |
||||
} |
||||
|
||||
response.returnValues = abiCoder.decodeLog( |
||||
abiItemModel.getInputs(), |
||||
response.data, |
||||
argumentTopics, |
||||
); |
||||
response.event = abiItemModel.name; |
||||
response.signature = abiItemModel.signature; |
||||
response.raw = { |
||||
data: response.data, |
||||
topics: response.topics, |
||||
}; |
||||
|
||||
if (abiItemModel.anonymous || !response.topics[0]) { |
||||
response.signature = null; |
||||
} |
||||
|
||||
delete response.data; |
||||
delete response.topics; |
||||
|
||||
return response; |
||||
}; |
@ -0,0 +1,33 @@ |
||||
import { AbiItemModel } from '../models/types'; |
||||
import { AbiCoderClass } from '../abi/api'; |
||||
|
||||
export const methodEncoder = ( |
||||
abiCoder: AbiCoderClass, |
||||
abiItemModel: AbiItemModel, |
||||
deployData: string, |
||||
) => { |
||||
let encodedParameters = abiCoder.encodeParameters( |
||||
abiItemModel.getInputs(), |
||||
abiItemModel.contractMethodParameters, |
||||
); |
||||
|
||||
if (encodedParameters.startsWith('0x')) { |
||||
encodedParameters = encodedParameters.slice(2); |
||||
} |
||||
|
||||
if (abiItemModel.isOfType('constructor')) { |
||||
if (!deployData) { |
||||
throw new Error( |
||||
'The contract has no contract data option set. This is necessary to append the constructor parameters.', |
||||
); |
||||
} |
||||
|
||||
return deployData + encodedParameters; |
||||
} |
||||
|
||||
if (abiItemModel.isOfType('function')) { |
||||
return abiItemModel.signature + encodedParameters; |
||||
} |
||||
|
||||
return encodedParameters; |
||||
}; |
@ -0,0 +1,142 @@ |
||||
import { |
||||
hexlify, |
||||
isHexString, |
||||
keccak256, |
||||
toChecksumAddress, |
||||
} from '@harmony/crypto'; |
||||
import { |
||||
numberToHex, |
||||
isArray, |
||||
hexToNumber, |
||||
isString, |
||||
isAddress, |
||||
} from '@harmony/utils'; |
||||
import { toUtf8Bytes } from '../abi/abiCoder'; |
||||
|
||||
export const inputLogFormatter = (options: any) => { |
||||
if (options.fromBlock) { |
||||
options.fromBlock = inputBlockNumberFormatter(options.fromBlock); |
||||
} |
||||
|
||||
if (options.toBlock) { |
||||
options.toBlock = inputBlockNumberFormatter(options.toBlock); |
||||
} |
||||
|
||||
// make sure topics, get converted to hex
|
||||
options.topics = options.topics || []; |
||||
options.topics = options.topics.map((topic: any) => { |
||||
return isArray(topic) ? topic.map(toTopic) : toTopic(topic); |
||||
}); |
||||
|
||||
if (options.address) { |
||||
if (isArray(options.address)) { |
||||
options.address = options.address.map((addr: string) => { |
||||
return inputAddressFormatter(addr); |
||||
}); |
||||
} else { |
||||
options.address = inputAddressFormatter(options.address); |
||||
} |
||||
} |
||||
|
||||
return options; |
||||
}; |
||||
|
||||
/** |
||||
* Formats the output of a log |
||||
* |
||||
* @method outputLogFormatter |
||||
* |
||||
* @param {Object} log object |
||||
* |
||||
* @returns {Object} log |
||||
*/ |
||||
export const outputLogFormatter = (log: any) => { |
||||
// generate a custom log id
|
||||
if ( |
||||
typeof log.blockHash === 'string' && |
||||
typeof log.transactionHash === 'string' && |
||||
typeof log.logIndex === 'string' |
||||
) { |
||||
const shaId = keccak256( |
||||
log.blockHash.replace('0x', '') + |
||||
log.transactionHash.replace('0x', '') + |
||||
log.logIndex.replace('0x', ''), |
||||
); |
||||
|
||||
shaId.replace('0x', '').substr(0, 8); |
||||
|
||||
log.id = `log_${shaId}`; |
||||
} else if (!log.id) { |
||||
log.id = null; |
||||
} |
||||
|
||||
if (log.blockNumber !== null) { |
||||
log.blockNumber = hexToNumber(log.blockNumber); |
||||
} |
||||
|
||||
if (log.transactionIndex !== null) { |
||||
log.transactionIndex = hexToNumber(log.transactionIndex); |
||||
} |
||||
|
||||
if (log.logIndex !== null) { |
||||
log.logIndex = hexToNumber(log.logIndex); |
||||
} |
||||
|
||||
if (log.address) { |
||||
log.address = toChecksumAddress(log.address); |
||||
} |
||||
|
||||
return log; |
||||
}; |
||||
|
||||
export const inputBlockNumberFormatter = (blockNumber: any) => { |
||||
if ( |
||||
blockNumber === undefined || |
||||
blockNumber === null || |
||||
isPredefinedBlockNumber(blockNumber) |
||||
) { |
||||
return blockNumber; |
||||
} |
||||
|
||||
if (isHexString(blockNumber)) { |
||||
if (isString(blockNumber)) { |
||||
return blockNumber.toLowerCase(); |
||||
} |
||||
|
||||
return blockNumber; |
||||
} |
||||
|
||||
return numberToHex(blockNumber); |
||||
}; |
||||
|
||||
export const isPredefinedBlockNumber = (blockNumber: string) => { |
||||
return ( |
||||
blockNumber === 'latest' || |
||||
blockNumber === 'pending' || |
||||
blockNumber === 'earliest' |
||||
); |
||||
}; |
||||
|
||||
export const inputAddressFormatter = (address: string) => { |
||||
if (isAddress(address)) { |
||||
return `0x${address.toLowerCase().replace('0x', '')}`; |
||||
} |
||||
|
||||
throw new Error( |
||||
`Provided address "${address}" is invalid, the capitalization checksum test failed, or its an indrect IBAN address which can't be converted.`, |
||||
); |
||||
}; |
||||
|
||||
export const toTopic = (value: any) => { |
||||
if (value === null || typeof value === 'undefined') { |
||||
return null; |
||||
} |
||||
|
||||
value = String(value); |
||||
|
||||
if (value.indexOf('0x') === 0) { |
||||
return value; |
||||
} |
||||
|
||||
return hexlify(toUtf8Bytes(value)); |
||||
}; |
@ -0,0 +1,97 @@ |
||||
import { isArray } from '@harmony/utils'; |
||||
import { AbiItem } from '../models/AbiItemModel'; |
||||
import { AbiModel } from '../models/AbiModel'; |
||||
import { AbiItemModel } from '../models/types'; |
||||
import { jsonInterfaceMethodToString } from '../abi/utils'; |
||||
import { AbiCoderClass } from '../abi/api'; |
||||
|
||||
export const abiMapper = (abi: any[], abiCoder: AbiCoderClass): AbiModel => { |
||||
const mappedAbiItems: any = { |
||||
methods: {}, |
||||
events: {}, |
||||
}; |
||||
let hasConstructor = false; |
||||
|
||||
abi.forEach((abiItem: AbiItemModel) => { |
||||
abiItem.constant = isConstant(abiItem); |
||||
abiItem.payable = isPayable(abiItem); |
||||
|
||||
if (abiItem.name) { |
||||
abiItem.funcName = jsonInterfaceMethodToString(abiItem); |
||||
} |
||||
|
||||
let abiItemModel; |
||||
|
||||
if (abiItem.type === 'function') { |
||||
abiItem.signature = abiCoder.encodeFunctionSignature(abiItem.funcName); |
||||
|
||||
abiItemModel = new AbiItem(abiItem); |
||||
|
||||
// Check if an method already exists with this name and if it exists than create an array and push this abiItem
|
||||
// into it. This will be used if there are methods with the same name but with different arguments.
|
||||
if (!mappedAbiItems.methods[abiItem.name]) { |
||||
mappedAbiItems.methods[abiItem.name] = abiItemModel; |
||||
} else { |
||||
if (isArray(mappedAbiItems.methods[abiItem.name])) { |
||||
mappedAbiItems.methods[abiItem.name].push(abiItemModel); |
||||
} else { |
||||
mappedAbiItems.methods[abiItem.name] = [ |
||||
mappedAbiItems.methods[abiItem.name], |
||||
abiItemModel, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
mappedAbiItems.methods[abiItem.signature] = abiItemModel; |
||||
mappedAbiItems.methods[abiItem.funcName] = abiItemModel; |
||||
|
||||
return; |
||||
} |
||||
|
||||
if (abiItem.type === 'event') { |
||||
abiItem.signature = abiCoder.encodeEventSignature(abiItem.funcName); |
||||
|
||||
abiItemModel = new AbiItem(abiItem); |
||||
|
||||
if ( |
||||
!mappedAbiItems.events[abiItem.name] || |
||||
mappedAbiItems.events[abiItem.name].name === 'bound ' |
||||
) { |
||||
mappedAbiItems.events[abiItem.name] = abiItemModel; |
||||
} |
||||
|
||||
mappedAbiItems.events[abiItem.signature] = abiItemModel; |
||||
mappedAbiItems.events[abiItem.funcName] = abiItemModel; |
||||
} |
||||
|
||||
if (abiItem.type === 'constructor') { |
||||
abiItem.signature = abiItem.type; |
||||
// tslint:disable-next-line: no-string-literal
|
||||
mappedAbiItems.methods['contractConstructor'] = new AbiItem(abiItem); |
||||
|
||||
hasConstructor = true; |
||||
} |
||||
}); |
||||
if (!hasConstructor) { |
||||
// tslint:disable-next-line: no-string-literal
|
||||
mappedAbiItems.methods['contractConstructor'] = new AbiItem({ |
||||
inputs: [], |
||||
payable: false, |
||||
constant: false, |
||||
type: 'constructor', |
||||
}); |
||||
} |
||||
return new AbiModel(mappedAbiItems); |
||||
}; |
||||
|
||||
export const isConstant = (abiItem: AbiItemModel) => { |
||||
return ( |
||||
abiItem.stateMutability === 'view' || |
||||
abiItem.stateMutability === 'pure' || |
||||
abiItem.constant |
||||
); |
||||
}; |
||||
|
||||
export const isPayable = (abiItem: AbiItemModel) => { |
||||
return abiItem.stateMutability === 'payable' || abiItem.payable; |
||||
}; |
@ -0,0 +1,12 @@ |
||||
export interface ContractOptions { |
||||
data?: string; |
||||
address?: string; |
||||
defaultAccount?: string; |
||||
defaultBlock?: string; |
||||
defaultGas?: string; |
||||
defaultGasPrice?: string; |
||||
transactionBlockTimeout?: number; |
||||
transactionConfirmationBlocks?: string; |
||||
transactionPollingTimeout?: number; |
||||
transactionSigner?: any; |
||||
} |
@ -0,0 +1,10 @@ |
||||
export enum ContractStatus { |
||||
INITIALISED = 'initialised', |
||||
TESTED = 'tested', |
||||
ERROR = 'error', |
||||
SIGNED = 'signed', |
||||
SENT = 'sent', |
||||
REJECTED = 'rejected', |
||||
DEPLOYED = 'deployed', |
||||
CALLED = 'called', |
||||
} |
@ -0,0 +1,66 @@ |
||||
import mitt from 'mitt'; |
||||
|
||||
class Emitter { |
||||
handlers?: any = {}; |
||||
emitter: mitt.Emitter; |
||||
off: (type: string, handler: mitt.Handler) => void; |
||||
emit: (type: string, event?: any) => void; |
||||
promise: Promise<{}>; |
||||
resolve?: any; |
||||
reject?: any; |
||||
then?: any; |
||||
constructor() { |
||||
this.emitter = new mitt(this.handlers); |
||||
this.off = this.emitter.off.bind(this); |
||||
this.emit = this.emitter.emit.bind(this); |
||||
// tslint:disable-next-line: no-empty
|
||||
this.promise = new Promise((resolve, reject) => { |
||||
this.resolve = resolve; |
||||
this.reject = reject; |
||||
}); |
||||
this.then = this.promise.then.bind(this.promise); |
||||
} |
||||
|
||||
resetHandlers() { |
||||
// tslint:disable-next-line: forin
|
||||
for (const i in this.handlers) { |
||||
delete this.handlers[i]; |
||||
} |
||||
} |
||||
on(type: string, handler: mitt.Handler) { |
||||
this.emitter.on(type, handler); |
||||
return this; |
||||
} |
||||
once(type: string, handler: mitt.Handler) { |
||||
this.emitter.on(type, (e: any) => { |
||||
handler(e); |
||||
this.removeEventListener(type); |
||||
}); |
||||
} |
||||
|
||||
addEventListener(type: string, handler: mitt.Handler) { |
||||
this.emitter.on(type, handler); |
||||
} |
||||
|
||||
removeEventListener(type?: string, handler?: mitt.Handler) { |
||||
if (!type) { |
||||
this.handlers = {}; |
||||
return; |
||||
} |
||||
if (!handler) { |
||||
delete this.handlers[type]; |
||||
} else { |
||||
return this.emitter.off(type, handler); |
||||
} |
||||
} |
||||
onError(error: any) { |
||||
this.emitter.on('error', error); |
||||
this.removeEventListener('*'); |
||||
} |
||||
onData(data: any) { |
||||
this.emitter.on('data', data); |
||||
this.removeEventListener('*'); |
||||
} |
||||
} |
||||
|
||||
export { Emitter }; |
@ -1,3 +1,4 @@ |
||||
export * from './factory'; |
||||
export * from './transaction'; |
||||
export * from './types'; |
||||
export * from './utils'; |
||||
|
Loading…
Reference in new issue