MyCrypto/common/libs/contracts/index.ts

155 lines
3.7 KiB
TypeScript

import AbiFunction, { IUserSendParams, ISendParams } from './ABIFunction';
import { IFullWallet } from 'libs/wallet/IWallet';
import { RPCNode } from 'libs/nodes';
import { ContractOutputMappings } from './types';
import { Wei } from 'libs/units';
const ABIFUNC_METHOD_NAMES = [
'encodeInput',
'decodeInput',
'decodeOutput',
'call'
];
export interface ISetConfigForTx {
wallet: IFullWallet;
nodeLib: RPCNode;
chainId: number;
gasPrice: Wei;
}
enum ABIMethodTypes {
FUNC = 'function'
}
export type TContract = typeof Contract;
export default class Contract {
public static setConfigForTx = (
contract: Contract,
{ wallet, nodeLib, chainId, gasPrice }: ISetConfigForTx
): Contract =>
contract
.setWallet(wallet)
.setNode(nodeLib)
.setChainId(chainId)
.setGasPrice(gasPrice);
public static getFunctions = (contract: Contract) =>
Object.getOwnPropertyNames(contract).reduce(
(accu, currContractMethodName) => {
const currContractMethod = contract[currContractMethodName];
const methodNames = Object.getOwnPropertyNames(currContractMethod);
const isFunc = ABIFUNC_METHOD_NAMES.reduce(
(isAbiFunc, currAbiFuncMethodName) =>
isAbiFunc && methodNames.includes(currAbiFuncMethodName),
true
);
return isFunc
? { ...accu, [currContractMethodName]: currContractMethod }
: accu;
},
{}
);
public address: string;
public abi;
private wallet: IFullWallet;
private gasPrice: Wei;
private chainId: number;
private node: RPCNode;
constructor(abi, outputMappings: ContractOutputMappings = {}) {
this.assignABIFuncs(abi, outputMappings);
}
public at = (addr: string) => {
this.address = addr;
return this;
};
public setWallet = (w: IFullWallet) => {
this.wallet = w;
return this;
};
public setGasPrice = (gasPrice: Wei) => {
this.gasPrice = gasPrice;
return this;
};
public setChainId = (chainId: number) => {
this.chainId = chainId;
return this;
};
public setNode = (node: RPCNode) => {
//TODO: caching
this.node = node;
return this;
};
private assignABIFuncs = (abi, outputMappings: ContractOutputMappings) => {
abi.forEach(currentABIMethod => {
const { name, type } = currentABIMethod;
if (type === ABIMethodTypes.FUNC) {
//only grab the functions we need
const {
encodeInput,
decodeInput,
decodeOutput,
call,
send,
constant,
outputs,
inputs
} = new AbiFunction(currentABIMethod, outputMappings[name]);
const proxiedCall = new Proxy(call, {
apply: this.applyTrapForCall
});
const proxiedSend = new Proxy(send, { apply: this.applyTrapForSend });
const funcToAssign = {
[name]: {
encodeInput,
decodeInput,
decodeOutput,
call: proxiedCall,
send: proxiedSend,
constant,
outputs,
inputs
}
};
Object.assign(this, funcToAssign);
}
});
};
private applyTrapForCall = (target, _, argumentsList) => {
return target(
//TODO: pass object instead
...(argumentsList.length > 0 ? argumentsList : [null]),
this.node,
this.address
);
};
private applyTrapForSend = (
target: (sendParams: ISendParams) => void,
_,
[userSendParams]: [IUserSendParams]
) => {
return target({
chainId: this.chainId,
gasPrice: this.gasPrice,
to: this.address,
nodeLib: this.node,
wallet: this.wallet,
...userSendParams
});
};
}