@bloom-trade/finance-connector
Version:
Is a package entended to be used with multiple web3 and web2 providers to interact with blockchains.
225 lines (224 loc) • 7.23 kB
text/typescript
import { ProviderConnector } from '../connector';
import { IProviderConnector, Balance } from '../../@types/index';
import axios from 'axios';
import {
convertToken,
getAssetDataByChain,
getDescription,
getSupportedContracts,
weiToEth,
} from '../../utils';
import Web3 from 'web3';
import { Asset, Chain, Provider, Transaction } from '@bloom-trade/types';
export class ProviderConnectorImpl
extends ProviderConnector
implements IProviderConnector
{
async getBalance(): Promise<
{
asset: string;
balance: string;
description: string;
detail: {
address: string;
provider: string;
chain: string;
balance: string;
}[];
}[]
> {
const apiKey = this._credentials.apiKey;
const balance: Balance = [];
const contracts = getSupportedContracts();
if (!this.addresses) {
return [] as any;
}
for (const address of this.addresses) {
if (
!balance.find((e) => {
if (
e.detail.find((e) => e.address === address) &&
e.asset === 'ETH'
) {
return e;
}
})
) {
const { data } = await axios.get(
`${this._baseurl}?module=account&action=balance&address=${address}&tag=latest&apikey=${apiKey}`
);
//TODO: Wei convertion
const web3 = new Web3(Web3.givenProvider || 'ws://localhost:8545');
balance.push({
asset: 'ETH',
description: 'Ethereum',
balance: web3.utils.fromWei(data.result, 'ether'),
detail: [
{
address,
provider: this._provider.id,
chain: this.chain as string,
balance: web3.utils.fromWei(data.result, 'ether'),
},
],
});
}
for (const contract of contracts) {
const { data } = await axios.get(
`${
this._baseurl
}?module=account&action=tokenbalance&contractaddress=${
getAssetDataByChain(contract, this.chain as Chain, this._provider)
.address
}&address=${address}&tag=latest&apikey=${apiKey}`
);
if (data.result !== '0') {
let retrievedBalance;
if (
getAssetDataByChain(contract, this.chain as Chain, this._provider)
.decimalPosition === 18
) {
retrievedBalance = weiToEth(data.result);
} else {
retrievedBalance = convertToken(
data.result,
getAssetDataByChain(contract, this.chain as Chain, this._provider)
.decimalPosition
);
}
balance.push({
asset: contract.token,
description: getDescription(contract.token),
balance: retrievedBalance,
detail: [
{
address,
provider: this._provider.id,
chain: this.chain as string,
balance: retrievedBalance,
},
],
});
}
continue;
}
}
return balance as any;
//staff
}
async getTransactionHistory(filters: {
from:
| 'beginning'
| {
selfCustodialProviders?: {
chain: Chain;
block: number;
}[];
custodialProviders?: {
id: Provider;
block?: number;
date: number; //NOT IN UNIX TIMESTAMP
}[];
};
}) {
const apiKey = this._credentials.apiKey;
const contracts = getSupportedContracts();
if (!this.addresses) {
return [] as any;
}
const transactions: Transaction[] = [];
let startingBlock = 0;
for (const address of this.addresses) {
if (
filters.from !== 'beginning' &&
filters.from.selfCustodialProviders &&
filters.from.selfCustodialProviders.length > 0 &&
filters.from.selfCustodialProviders.filter(
(provider) => provider.chain === this.chain
).length > 0
) {
//Check if eth is included otherwise use from block 0
const ethIncluded = (filters.from as any).selfCustodialProviders.find(
(e) => e.chain === 'eth'
);
startingBlock = ethIncluded.block;
}
const {
data: { result: ethTransactions },
} = await axios.get(
`${this._baseurl}?module=account&action=txlist&address=${address}&startblock=${startingBlock}&endblock=99999999&tag=latest&apikey=${apiKey}`
);
for (const e of ethTransactions) {
if (e.value !== '0' && e.from) {
const lowerCaseAddress = address.toLowerCase();
const lowerCaseFrom = e.from.toLowerCase();
const obj: Transaction = {
asset: 'eth',
from: e.from,
to: e.to,
amount: weiToEth(e.value),
type: lowerCaseAddress != lowerCaseFrom ? 'in' : 'out',
status: e.isError === '0' ? 'completed' : 'failed',
timestamp: parseInt(e.timeStamp) * 1000,
provider: this._provider.id as Provider,
chain: this.chain as Chain,
block: e.blockNumber,
};
if (lowerCaseAddress == lowerCaseFrom) {
Object.assign(obj, {
gas: e.gas,
gasPrice: e.gasPrice,
gasUsed: e.gasUsed,
});
}
transactions.push(obj);
}
}
for (const contract of contracts) {
const {
data: { result: ERC20Transactions },
} = await axios.get(
`${this._baseurl}?module=account&action=tokentx&contractaddress=${
getAssetDataByChain(contract, this.chain as Chain, this._provider)
.address
}&address=${address}&startblock=${startingBlock}&endblock=99999999&tag=latest&apikey=${apiKey}`
);
for (const e of ERC20Transactions) {
if (e instanceof Object && e.value !== '0') {
const lowerCaseAddress = address.toLowerCase();
const lowerCaseFrom = e.from.toLowerCase();
const obj: Transaction = {
asset: contract.token.toLowerCase() as Asset,
from: e.from,
to: e.to,
amount: convertToken(
e.value,
getAssetDataByChain(
contract,
this.chain as Chain,
this._provider
).decimalPosition
),
type: lowerCaseAddress !== lowerCaseFrom ? 'in' : 'out',
status:
lowerCaseAddress !== lowerCaseFrom ? 'in' : ('out' as any),
timestamp: parseInt(e.timeStamp) * 1000,
provider: this._provider.id as Provider,
chain: this.chain as Chain,
block: e.blockNumber,
};
if (lowerCaseAddress === lowerCaseFrom) {
Object.assign(obj, {
gas: e.gas,
gasPrice: e.gasPrice,
gasUsed: e.gasUsed,
});
}
transactions.push(obj);
}
}
}
}
return transactions;
}
}