@tatumio/erc20-connector
Version:
ERC20 Connector for Tatum API
348 lines (329 loc) • 16.8 kB
text/typescript
import Web3 from 'web3';
import {PinoLogger} from 'nestjs-pino';
import {Erc20Error} from './Erc20Error';
import {
ChainBurnCeloErc20,
ChainBurnErc20,
ChainBurnKcsErc20,
ChainDeployCeloErc20,
ChainDeployErc20,
ChainDeployKcsErc20,
ChainEgldEsdtTransaction,
ChainMintCeloErc20,
ChainMintErc20,
ChainMintKcsErc20,
ChainTransferAlgoErc20,
ChainTransferBscBep20,
ChainTransferCeloErc20Token,
ChainTransferErc20,
ChainTransferEthErc20,
ChainTransferHrm20,
ChainTransferKcsErc20,
ChainTransferPolygonErc20
} from './Erc20Base';
import {HarmonyAddress} from '@harmony-js/crypto';
import {
ApproveErc20,
BurnCeloErc20,
BurnErc20,
Currency,
DeployCeloErc20,
DeployErc20,
EgldEsdtTransaction,
egldGetAccountErc20Balance,
fromXdcAddress,
getAlgoClient,
MintCeloErc20,
MintErc20,
OneBurn20,
OneMint20,
OneTransfer20,
prepareAlgoBurnFTSignedTransaction,
prepareAlgoCreateFTSignedTransaction,
prepareAlgoTransferFTSignedTransaction,
prepareAuctionApproveErc20Transfer,
prepareBurnBep20SignedTransaction,
prepareCeloBurnErc20SignedTransaction,
prepareCeloDeployErc20SignedTransaction,
prepareCeloMintErc20SignedTransaction,
prepareCeloTransferErc20SignedTransaction,
prepareCustomBep20SignedTransaction,
prepareCustomErc20SignedTransaction,
prepareDeployBep20SignedTransaction,
prepareDeployErc20SignedTransaction,
prepareEgldBurnEsdtSignedTransaction,
prepareEgldDeployEsdtSignedTransaction,
prepareEgldFreezeOrWipeOrOwvershipEsdtSignedTransaction,
prepareEgldMintEsdtSignedTransaction,
prepareEgldTransferEsdtSignedTransaction,
prepareEthBurnErc20SignedTransaction,
prepareEthMintErc20SignedTransaction,
prepareEthOrErc20SignedTransaction,
prepareMintBep20SignedTransaction,
prepareOneBurn20SignedTransaction,
prepareOneDeploy20SignedTransaction,
prepareOneMint20SignedTransaction,
prepareOneTransfer20SignedTransaction,
preparePolygonBurnErc20SignedTransaction,
preparePolygonDeployErc20SignedTransaction,
preparePolygonMintErc20SignedTransaction,
preparePolygonTransferErc20SignedTransaction,
prepareXdcBurnErc20SignedTransaction,
prepareXdcCustomErc20SignedTransaction,
prepareXdcDeployErc20SignedTransaction,
prepareXdcMintErc20SignedTransaction,
TransactionHash,
TransferCeloOrCeloErc20Token,
TransferErc20
} from '@tatumio/tatum';
import erc20_abi from '@tatumio/tatum/dist/src/contracts/erc20/token_abi';
import {
BurnErc20 as CoreBurnErc20,
DeployErc20 as CoreDeployErc20,
MintErc20 as CoreMintErc20,
TransferErc20 as CoreTransferErc20
} from '@tatumio/tatum-core'
import {
prepareBurnErc20SignedTransaction as prepareKcsBurnErc20SignedTransaction,
prepareDeployErc20SignedTransaction as prepareKcsDeployErc20SignedTransaction,
prepareMintErc20SignedTransaction as prepareKcsMintErc20SignedTransaction,
prepareTransferErc20SignedTransaction as prepareKcsTransferErc20SignedTransaction
} from '@tatumio/tatum-kcs'
export abstract class Erc20Service {
protected constructor(protected readonly logger: PinoLogger) {
}
protected abstract storeKMSTransaction(txData: string, currency: string, signatureId: string[], index?: number): Promise<string>;
protected abstract isTestnet(): Promise<boolean>;
protected abstract getNodesUrl(chain: Currency, testnet: boolean): Promise<string[]>;
protected abstract broadcast(chain: Currency, txData: string, signatureId?: string): Promise<TransactionHash>;
private async getFirstNodeUrl(chain: Currency, testnet: boolean): Promise<string> {
const nodes = await this.getNodesUrl(chain, testnet);
if (nodes.length === 0) {
new Erc20Error('Nodes url array must have at least one element.', 'erc20.nodes.url');
}
return nodes[0];
}
private async getClient(chain: Currency, testnet: boolean) {
if ([Currency.ETH, Currency.BSC, Currency.CELO, Currency.XDC, Currency.ONE, Currency.MATIC, Currency.KCS].includes(chain)) {
return new Web3((await this.getFirstNodeUrl(chain, testnet)));
} else if (chain === Currency.ALGO) {
return await getAlgoClient(await this.isTestnet(), await this.getFirstNodeUrl(chain, testnet));
}
throw new Erc20Error(`Unsupported chain ${chain}.`, 'unsupported.chain');
}
public async getErc20Balance(chain: Currency, address: string, contractAddress: string): Promise<{ balance: string }> {
let contractOrAddress;
switch (chain) {
case Currency.ETH:
case Currency.BSC:
case Currency.CELO:
case Currency.MATIC:
case Currency.KCS:
contractOrAddress = contractAddress;
break;
case Currency.ONE:
contractOrAddress = new HarmonyAddress(contractAddress).basicHex;
break;
case Currency.EGLD:
return { balance: `${await egldGetAccountErc20Balance(address, contractAddress)}` };
case Currency.XDC:
contractOrAddress = fromXdcAddress(contractAddress);
break;
default:
throw new Erc20Error(`Unsupported chain ${chain}.`, 'unsupported.chain');
}
const _address = chain === Currency.XDC ? fromXdcAddress(address) : address;
const client = await this.getClient(chain, await this.isTestnet());
// @ts-ignore
const contract = new client.eth.Contract(erc20_abi, contractOrAddress);
return {balance: await contract.methods.balanceOf(_address).call()};
}
public async transferErc20(body: ChainTransferEthErc20 | ChainTransferBscBep20 | ChainTransferCeloErc20Token |
ChainTransferErc20 | ChainTransferHrm20 | ChainTransferPolygonErc20 | ChainEgldEsdtTransaction | ChainTransferAlgoErc20 | ChainTransferKcsErc20):
Promise<TransactionHash | { signatureId: string }> {
const testnet = await this.isTestnet();
const {chain, ..._body} = body;
let txData;
switch (chain) {
case Currency.ETH:
if ((_body as TransferErc20).currency) {
txData = await prepareEthOrErc20SignedTransaction(_body as TransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
} else {
txData = await prepareCustomErc20SignedTransaction(_body as TransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
}
break;
case Currency.ONE:
txData = await prepareOneTransfer20SignedTransaction(testnet, _body as OneTransfer20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.MATIC:
txData = await preparePolygonTransferErc20SignedTransaction(testnet, _body as TransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.KCS:
txData = await prepareKcsTransferErc20SignedTransaction(_body as CoreTransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.BSC:
txData = await prepareCustomBep20SignedTransaction(_body as TransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.CELO:
txData = await prepareCeloTransferErc20SignedTransaction(testnet, _body as TransferCeloOrCeloErc20Token, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.EGLD:
txData = await prepareEgldTransferEsdtSignedTransaction(_body as EgldEsdtTransaction, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.XDC:
txData = await prepareXdcCustomErc20SignedTransaction(_body as TransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.ALGO:
txData = await prepareAlgoTransferFTSignedTransaction(testnet, _body as TransferErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
default:
throw new Erc20Error(`Unsupported chain ${chain}.`, 'unsupported.chain');
}
if (body.signatureId) {
return {signatureId: await this.storeKMSTransaction(txData, chain, [body.signatureId], body.index)};
} else {
return this.broadcast(chain, txData);
}
}
public async burnErc20(body: ChainBurnErc20 | ChainBurnCeloErc20 | ChainEgldEsdtTransaction | ChainBurnKcsErc20): Promise<TransactionHash | { signatureId: string }> {
const testnet = await this.isTestnet();
const {chain, ..._body} = body;
let txData;
switch (chain) {
case Currency.ETH:
txData = await prepareEthBurnErc20SignedTransaction(_body as BurnErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.ONE:
txData = await prepareOneBurn20SignedTransaction(testnet, _body as OneBurn20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.MATIC:
txData = await preparePolygonBurnErc20SignedTransaction(testnet, _body as BurnErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.KCS:
txData = await prepareKcsBurnErc20SignedTransaction(_body as CoreBurnErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.BSC:
txData = await prepareBurnBep20SignedTransaction(_body as BurnErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.CELO:
txData = await prepareCeloBurnErc20SignedTransaction(testnet, _body as BurnCeloErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.EGLD:
txData = await prepareEgldBurnEsdtSignedTransaction(_body as EgldEsdtTransaction, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.XDC:
txData = await prepareXdcBurnErc20SignedTransaction(_body as BurnErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.ALGO:
txData = await prepareAlgoBurnFTSignedTransaction(testnet, _body as BurnErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
default:
throw new Erc20Error(`Unsupported chain ${chain}.`, 'unsupported.chain');
}
if (body.signatureId) {
return {signatureId: await this.storeKMSTransaction(txData, chain, [body.signatureId], body.index)};
} else {
return this.broadcast(chain, txData);
}
}
public async mintErc20(body: ChainMintErc20 | ChainMintCeloErc20 | ChainEgldEsdtTransaction | ChainMintKcsErc20): Promise<TransactionHash | { signatureId: string }> {
const testnet = await this.isTestnet();
const {chain, ..._body} = body;
let txData;
switch (chain) {
case Currency.ETH:
txData = await prepareEthMintErc20SignedTransaction(_body as MintErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.ONE:
txData = await prepareOneMint20SignedTransaction(testnet, _body as OneMint20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.MATIC:
txData = await preparePolygonMintErc20SignedTransaction(testnet, _body as MintErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.KCS:
txData = await prepareKcsMintErc20SignedTransaction(_body as CoreMintErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.BSC:
txData = await prepareMintBep20SignedTransaction(_body as MintErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.CELO:
txData = await prepareCeloMintErc20SignedTransaction(testnet, _body as MintCeloErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.EGLD:
txData = await prepareEgldMintEsdtSignedTransaction(_body as EgldEsdtTransaction, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.XDC:
txData = await prepareXdcMintErc20SignedTransaction(_body as MintErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
default:
throw new Erc20Error(`Unsupported chain ${chain}.`, 'unsupported.chain');
}
if (body.signatureId) {
return {signatureId: await this.storeKMSTransaction(txData, chain, [body.signatureId], body.index)};
} else {
return this.broadcast(chain, txData);
}
}
public async deployErc20(body: ChainDeployErc20 | ChainDeployCeloErc20 | ChainEgldEsdtTransaction | ChainDeployKcsErc20): Promise<TransactionHash | { signatureId: string }> {
const testnet = await this.isTestnet();
const {chain, ..._body} = body;
let txData;
switch (chain) {
case Currency.ETH:
txData = await prepareDeployErc20SignedTransaction(_body as DeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.ONE:
txData = await prepareOneDeploy20SignedTransaction(testnet, _body as DeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.MATIC:
txData = await preparePolygonDeployErc20SignedTransaction(testnet, _body as DeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.KCS:
txData = await prepareKcsDeployErc20SignedTransaction(_body as CoreDeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.BSC:
txData = await prepareDeployBep20SignedTransaction(_body as DeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.CELO:
txData = await prepareCeloDeployErc20SignedTransaction(testnet, _body as DeployCeloErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.EGLD:
txData = await prepareEgldDeployEsdtSignedTransaction(_body as EgldEsdtTransaction, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.XDC:
const tx = {
..._body,
address: fromXdcAddress((_body as DeployErc20).address),
};
txData = await prepareXdcDeployErc20SignedTransaction(tx as DeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
case Currency.ALGO:
txData = await prepareAlgoCreateFTSignedTransaction(testnet, _body as DeployErc20, (await this.getFirstNodeUrl(chain, testnet)));
break;
default:
throw new Erc20Error(`Unsupported chain ${chain}.`, 'unsupported.chain');
}
if (body.signatureId) {
return {signatureId: await this.storeKMSTransaction(txData, chain, [body.signatureId], body.index)};
} else {
return this.broadcast(chain, txData);
}
}
public async approveErc20(body: ApproveErc20 | ChainEgldEsdtTransaction): Promise<TransactionHash | { signatureId: string }> {
const testnet = await this.isTestnet();
const {chain, ..._body} = body;
let txData;
switch (chain) {
case Currency.EGLD:
txData = await prepareEgldFreezeOrWipeOrOwvershipEsdtSignedTransaction(_body as EgldEsdtTransaction, (await this.getFirstNodeUrl(chain, testnet)));
break;
default:
txData = await prepareAuctionApproveErc20Transfer(testnet, body as ApproveErc20, await this.getFirstNodeUrl(body.chain, testnet));
}
if (body.signatureId) {
return {signatureId: await this.storeKMSTransaction(txData, body.chain, [body.signatureId], body.index)};
} else {
return this.broadcast(body.chain, txData);
}
}
}