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 { Harmony } = require('@harmony/core'); |
||||||
const abiDecoder = require('abi-decoder'); |
const { BN } = require('@harmony/crypto'); |
||||||
const { |
const { isArray, ChainType, ChainID } = require('@harmony/utils'); |
||||||
keccak256, |
const fs = require('fs'); |
||||||
hexToByteArray, |
const solc = require('solc'); |
||||||
hexToIntArray, |
|
||||||
arrayify, |
function constructInput(file) { |
||||||
hexlify, |
const content = fs.readFileSync(`./contracts/${file}`, { encoding: 'utf8' }); |
||||||
padZeros, |
const input = { |
||||||
bytesPadRight, |
language: 'Solidity', |
||||||
bytesPadLeft, |
sources: {}, |
||||||
|
settings: { |
||||||
BN, |
outputSelection: { |
||||||
} = 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', |
|
||||||
}, |
|
||||||
{ |
|
||||||
type: 'uint256', |
|
||||||
name: 'myNumber', |
|
||||||
indexed: true, |
|
||||||
}, |
|
||||||
{ |
|
||||||
type: 'uint8', |
|
||||||
name: 'mySmallNumber', |
|
||||||
indexed: true, |
|
||||||
}, |
}, |
||||||
], |
}; |
||||||
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000748656c6c6f252100000000000000000000000000000000000000000000000000', |
|
||||||
[ |
input.sources[file] = { content }; |
||||||
'0x000000000000000000000000000000000000000000000000000000000000f310', |
return JSON.stringify(input); |
||||||
'0x0000000000000000000000000000000000000000000000000000000000000010', |
} |
||||||
], |
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(
|
const mne = |
||||||
// '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
'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);
|
// .on('confirmation', (e) => {
|
||||||
// console.log(ttt.lt(new BN(0)));
|
// console.log(e);
|
||||||
// console.log(ttt.gt(mm.maskn(64)));
|
// });
|
||||||
|
// .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 './factory'; |
||||||
export * from './transaction'; |
export * from './transaction'; |
||||||
export * from './types'; |
export * from './types'; |
||||||
|
export * from './utils'; |
||||||
|
Loading…
Reference in new issue