@dojima-wallet/connection
Version:
Initialise and connection for layer 1&2 blockchain
328 lines (292 loc) • 9.71 kB
text/typescript
import { ChainClientParams, Network } from "../client";
import { validatePhrase } from "../crypto";
// import BigNumber from "bignumber.js";
import * as ethers from "ethers";
import Web3 from "web3";
import { DOJ_DECIMAL } from "./const";
import { DojTransferParams, DojTxData } from "./types";
import {
calcDoubleSwapOutput,
calcDoubleSwapSlip,
calcSwapOutput,
calcSwapSlip,
getStagenetInboundObject,
getTestnetInboundObject,
PoolData,
SwapFeeResult,
} from "../swap_utils";
import { SwapAssetList } from "@dojima-wallet/utils";
export type DojRpcParams = {
privateKey: string;
rpcUrl: string;
infuraApiKey?: string;
};
class DojimaClient {
protected network: Network;
protected web3: Web3;
protected rpcUrl: string;
protected provider: ethers.ethers.providers.JsonRpcProvider;
protected account: ethers.ethers.Wallet;
private phrase = "";
private privateKey = "";
constructor({
phrase,
privateKey,
network,
rpcUrl,
}: ChainClientParams & DojRpcParams) {
this.network = network;
this.rpcUrl = rpcUrl;
this.web3 = new Web3(new Web3.providers.HttpProvider(this.rpcUrl));
this.provider = new ethers.providers.JsonRpcProvider(this.rpcUrl);
if ((!phrase && !privateKey) || (phrase && privateKey)) {
throw new Error("Any one of phrase or privateKey should be provided");
}
if (phrase) {
if (!validatePhrase(phrase)) {
throw new Error("Invalid phrase");
}
this.phrase = phrase;
const accountData = ethers.Wallet.fromMnemonic(this.phrase);
this.account = new ethers.Wallet(accountData.privateKey).connect(
this.provider
);
}
if (privateKey) {
this.privateKey = privateKey;
this.account = new ethers.Wallet(this.privateKey).connect(this.provider);
}
}
getAddress(): string {
return this.account.address;
}
async getBalance(address: string): Promise<number> {
const gweiBalance = await this.web3.eth.getBalance(address); // Results balance in gwei, 1 doj = 10^9 gwei(1,000,000,000)
const dojBalance = this.web3.utils.fromWei(gweiBalance);
return Number(dojBalance);
}
// calculateDojFee(baseGasFee: number, multiplier: number): number {
// return new BigNumber(baseGasFee)
// .times(new BigNumber(multiplier))
// .toNumber();
// }
//
async estimateGasFee(amount: number, memo?: string): Promise<number> {
return await this.web3.eth.estimateGas({
from: this.getAddress(),
to: this.getAddress(),
value: amount * Math.pow(10, DOJ_DECIMAL),
data: memo ? this.web3.utils.toHex(memo) : undefined,
});
}
//
// async getFees(amount: number, memo?: string): Promise<GasfeeResult> {
// const estimateGas = await this.estimateGasFee(
// amount,
// memo ? memo : undefined
// );
// return {
// slow: this.calculateDojFee(estimateGas, 1) / Math.pow(10, 9),
// average: this.calculateDojFee(estimateGas, 1.5) / Math.pow(10, 9),
// fast: this.calculateDojFee(estimateGas, 2) / Math.pow(10, 9),
// };
// }
// Function to estimate gas limit
async estimateGasLimit(
tx: ethers.providers.TransactionRequest
): Promise<ethers.BigNumber> {
return await this.provider.estimateGas(tx);
}
// Function to get the current gas price
async getCurrentGasPrice(): Promise<ethers.BigNumber> {
return await this.provider.getGasPrice();
}
// Function to calculate the gas fee
async calculateGasFee(
recipient: string,
amount: number,
memo?: string
): Promise<string> {
const tx = {
to: recipient,
value: ethers.utils.parseEther(`${amount}`),
data: memo ? ethers.utils.toUtf8Bytes(memo) : undefined,
};
const gasLimit = await this.estimateGasLimit(tx);
const gasPrice = await this.getCurrentGasPrice();
return ethers.utils.formatEther(gasLimit.mul(gasPrice));
}
async transfer(params: DojTransferParams): Promise<string> {
if (this.network === Network.Testnet) {
const transaction = await this.web3.eth.accounts.signTransaction(
{
from: this.getAddress(),
to: params.recipient,
value: params.amount * Math.pow(10, DOJ_DECIMAL),
gas: params.fee
? params.fee * Math.pow(10, 9)
: await this.estimateGasFee(
params.amount,
params.memo ? params.memo : undefined
),
data: params.memo ? this.web3.utils.toHex(params.memo) : undefined,
},
this.account.privateKey
);
const transactionResult = await this.web3.eth.sendSignedTransaction(
transaction.rawTransaction as string
);
return transactionResult.transactionHash;
} else {
const tx = {
to: params.recipient,
value: ethers.utils.parseEther(`${params.amount}`),
data: params.memo ? ethers.utils.toUtf8Bytes(params.memo) : undefined,
};
// Send transaction with gas limit and gas price
const gasLimit = await this.estimateGasLimit(tx);
const gasPrice = await this.getCurrentGasPrice();
const transactionResponse = await this.account.sendTransaction({
...tx,
gasLimit,
gasPrice,
});
return transactionResponse.hash;
}
}
async dummyTx(recipient: string, amount: number): Promise<string> {
const memo = `NOOP:NOVAULT`;
const poolHash = await this.transfer({
recipient,
amount,
memo,
});
return poolHash;
}
async getTransactionData(hash: string): Promise<DojTxData> {
const tx = await this.provider.getTransaction(hash);
const receipt = await this.provider.getTransactionReceipt(hash);
if (tx && receipt) {
const amount = ethers.utils.formatEther(tx.value);
const gasUsed = ethers.utils.formatUnits(receipt.gasUsed, "gwei");
const gasPrice = ethers.utils.formatUnits(tx.gasPrice, "gwei");
return {
transaction_hash: tx.hash,
from: tx.from,
to: tx.to as string,
amount: parseFloat(amount),
gasFee: parseFloat(gasPrice) * parseFloat(gasUsed),
block_number: receipt.blockNumber as number,
block_hash: receipt.blockHash as string,
gasPrice: gasPrice,
transaction_status: receipt.status,
nonce: tx.nonce,
};
} else {
throw new Error(`Failed to get transaction data (tx-hash: ${hash})`);
}
// const data = await this.web3.eth.getTransaction(hash);
// if (data) {
// return {
// transaction_hash: data.hash,
// from: data.from,
// to: data.to as string,
// amount: Number(data.value) / Math.pow(10, 18),
// gasFee: data.gas / Math.pow(10, 9),
// block_number: data.blockNumber as number,
// block_hash: data.blockHash as string,
// gasPrice: data.gasPrice,
// nonce: data.nonce,
// };
// } else {
// throw new Error(`Failed to get transaction data (tx-hash: ${hash})`);
// }
}
getSwapOutput(inputAmount: number, pool: PoolData, toDoj: boolean): number {
const input = inputAmount * Math.pow(10, DOJ_DECIMAL);
return calcSwapOutput(input, pool, toDoj);
}
getDoubleSwapOutput(
inputAmount: number,
pool1: PoolData,
pool2: PoolData
): number {
const input = inputAmount * Math.pow(10, DOJ_DECIMAL);
return calcDoubleSwapOutput(input, pool1, pool2);
}
getSwapSlip(inputAmount: number, pool: PoolData, toDoj: boolean): number {
const input = inputAmount * Math.pow(10, DOJ_DECIMAL);
return calcSwapSlip(input, pool, toDoj);
}
getDoubleSwapSlip(
inputAmount: number,
pool1: PoolData,
pool2: PoolData
): number {
const input = inputAmount * Math.pow(10, DOJ_DECIMAL);
return calcDoubleSwapSlip(input, pool1, pool2);
}
async getSwapFeesData(): Promise<SwapFeeResult> {
return;
}
async getDojimaInboundAddress(hermesApiUrl: string): Promise<string> {
switch (this.network) {
case Network.Testnet: {
const inboundObj = await getTestnetInboundObject(hermesApiUrl, "DOJ");
return inboundObj.address;
}
case Network.Stagenet: {
const inboundObj = await getStagenetInboundObject(hermesApiUrl, "DOJ");
return inboundObj.address;
}
case Network.Mainnet: {
return "";
}
}
}
async getDefaultLiquidityPoolGasFee(hermesApiUrl: string): Promise<number> {
switch (this.network) {
case Network.Testnet: {
const inboundObj = await getTestnetInboundObject(hermesApiUrl, "DOJ");
const gasFee = Number(inboundObj.gas_rate) / Math.pow(10, DOJ_DECIMAL);
return gasFee;
}
case Network.Stagenet: {
const inboundObj = await getStagenetInboundObject(hermesApiUrl, "DOJ");
const gasFee = Number(inboundObj.gas_rate) / Math.pow(10, DOJ_DECIMAL);
return gasFee;
}
case Network.Mainnet: {
return 0;
}
}
}
async addLiquidityPool(
amount: number,
inboundAddress: string,
hermesAddress?: string
): Promise<string> {
const memo = hermesAddress ? `ADD:DOJ.DOJ:${hermesAddress}` : `ADD:DOJ.DOJ`;
const txHash = await this.transfer({
amount,
recipient: inboundAddress,
memo,
});
return txHash;
}
async swap(
amount: number,
token: SwapAssetList,
inboundAddress: string,
recipient: string
): Promise<string> {
const memo = `SWAP:${token}:${recipient}`;
const txHash = await this.transfer({
amount,
recipient: inboundAddress,
memo,
});
return txHash;
}
}
export { DojimaClient };