@dojima-wallet/connection
Version:
Initialise and connection for layer 1&2 blockchain
311 lines (263 loc) • 8.94 kB
text/typescript
import { ChainClientParams, Network } from "../client";
import { validatePhrase } from "../crypto";
import { SwapAssetList } from "@dojima-wallet/utils";
import Arweave from "arweave";
import { getKeyFromMnemonic } from "arweave-mnemonic-keys";
import { ApiConfig } from "arweave/node/lib/api";
import Transaction, { Tag } from "arweave/node/lib/transaction";
import ArweaveTxClient from "./tx-client";
import {
ArTxDataResult,
ArTxParams,
ArTxs,
ArTxsHistoryParams,
GasfeeResult,
TxStatusResponse,
} from "./types";
import {
AR_DECIMAL,
// defaultArMainnetConfig,
// defaultArTestnetConfig,
} from "./utils";
import {
calcDoubleSwapOutput,
calcDoubleSwapSlip,
calcSwapOutput,
calcSwapSlip,
getStagenetInboundObject,
getTestnetInboundObject,
PoolData,
SwapFeeResult,
} from "../swap_utils";
export interface ArweaveChainClient {
getAddress(): Promise<string>;
mintArTokens(pubAddress: string): Promise<void>;
getBalance(address: string): Promise<number>;
getFees(rawTx: Transaction): GasfeeResult;
transfer({ recipient, amount }: ArTxParams): Promise<string>;
getTransactionData(txHash: string): Promise<ArTxDataResult>;
getTransactionsHistory(params: ArTxsHistoryParams): Promise<ArTxs>;
getTxStatus(txHash: string): Promise<TxStatusResponse>;
}
export type ChainConfigParams = {
config: ApiConfig;
};
class ArweaveClient extends ArweaveTxClient implements ArweaveChainClient {
protected network: Network;
protected arweave: Arweave;
private phrase = "";
protected apiConfig: ApiConfig;
constructor({
phrase,
network = Network.Mainnet,
config,
}: ChainClientParams & ChainConfigParams) {
super();
if (phrase) {
if (!validatePhrase(phrase)) {
throw new Error("Invalid phrase");
}
this.phrase = phrase;
}
this.network = network;
this.apiConfig = config;
// if (this.network === Network.Testnet || this.network === Network.Stagenet) {
// this.apiConfig = defaultArTestnetConfig;
// }
// if (
// this.network === Network.Testnet &&
// this.apiConfig === defaultArMainnetConfig
// ) {
// throw Error(`'config' params can't be empty for 'testnet'`);
// }
this.arweave = Arweave.init(this.apiConfig);
}
async getAddress(): Promise<string> {
const keyPair = await getKeyFromMnemonic(this.phrase);
const address = await this.arweave.wallets.jwkToAddress(keyPair);
return address;
}
/** testnet tokens in winston */
async mintArTokens(address: string) {
const test_ar_amount = 2000000000000;
// Mint balance in Arlocal for testing
await this.arweave.api.get(`/mint/${address}/${test_ar_amount}`);
await this.arweave.api.get("/mine");
}
async getBalance(address: string): Promise<number> {
/** Get balance */
const wnstBalance = await this.arweave.wallets.getBalance(address);
/** Convert balance from Winston to Ar. (1 Ar = 10^12) */
const arBalance = this.arweave.ar.winstonToAr(wnstBalance);
return Number(arBalance);
}
/** Calculate gasFee required for transaction */
getFees(rawTx: Transaction): GasfeeResult {
/** Gas fee generated by default during 'createTransaction' */
const arw_gasFee = Number(rawTx.reward);
return {
slow: arw_gasFee,
average: arw_gasFee,
fast: arw_gasFee,
};
}
/** Create transaction based on user inputs */
async createTransaction(
recipient: string,
amount: number,
tag?: Tag
): Promise<Transaction> {
const pvtKey = await getKeyFromMnemonic(this.phrase);
// const pubAddress = await this.arweave.wallets.jwkToAddress(pvtKey);
// Create transaction
const rawTx = await this.arweave.createTransaction(
{
target: recipient, // Receiver address
quantity: this.arweave.ar.arToWinston(amount.toString()), // Amount to transfer in Ar
tags: tag ? [tag] : [],
},
pvtKey
);
return rawTx;
}
/** Sign and Send the transaction */
async signAndSend(rawTx: Transaction): Promise<string> {
const pvtKey = await getKeyFromMnemonic(this.phrase);
// const pubAddress = await this._arweave.wallets.jwkToAddress(pvtKey);
/** Sign transaction and retrieve status */
await this.arweave.transactions.sign(rawTx, pvtKey);
const status = await this.arweave.transactions.post(rawTx);
await this.arweave.api.get("/mine");
if (status.status !== 200) throw Error(`Transaction error or invalid`);
return rawTx.id;
}
async transfer({ recipient, amount }: ArTxParams): Promise<string> {
const rawTx = await this.createTransaction(recipient, amount);
const txHash = await this.signAndSend(rawTx);
return txHash;
}
/** Get status data using transaction hash / id */
async getTxStatus(txHash: string): Promise<TxStatusResponse> {
const statusData = await this.arweave.transactions.getStatus(txHash);
return statusData;
}
async dummyTx(recipient: string, amount: number): Promise<string> {
const tag = new Tag(
this.arweave.utils.stringToB64Url("memo"),
this.arweave.utils.stringToB64Url(`NOOP:NOVAULT`)
);
const rawTx = await this.createTransaction(recipient, amount, tag);
const txHash = await this.signAndSend(rawTx);
return txHash;
}
async getTransactionData(txHash: string): Promise<ArTxDataResult> {
const txData = await this.getTxData(this.arweave, txHash);
return txData;
}
async getTransactionsHistory(params: ArTxsHistoryParams): Promise<ArTxs> {
const txs = await this.getTxsHistory(
this.arweave,
params.address,
params.limit ? params.limit : 100
);
const txsResult = {
total: txs.inner.length,
txs,
};
return txsResult;
}
getSwapOutput(inputAmount: number, pool: PoolData, toDoj: boolean): number {
const input = inputAmount * Math.pow(10, AR_DECIMAL);
return calcSwapOutput(input, pool, toDoj);
}
getDoubleSwapOutput(
inputAmount: number,
pool1: PoolData,
pool2: PoolData
): number {
const input = inputAmount * Math.pow(10, AR_DECIMAL);
return calcDoubleSwapOutput(input, pool1, pool2);
}
getSwapSlip(inputAmount: number, pool: PoolData, toDoj: boolean): number {
const input = inputAmount * Math.pow(10, AR_DECIMAL);
return calcSwapSlip(input, pool, toDoj);
}
getDoubleSwapSlip(
inputAmount: number,
pool1: PoolData,
pool2: PoolData
): number {
const input = inputAmount * Math.pow(10, AR_DECIMAL);
return calcDoubleSwapSlip(input, pool1, pool2);
}
async getSwapFeesData(): Promise<SwapFeeResult> {
return;
}
async getArweaveInboundAddress(hermesApiUrl: string): Promise<string> {
switch (this.network) {
case Network.Testnet: {
const inboundObj = await getTestnetInboundObject(hermesApiUrl, "AR");
return inboundObj.address;
}
case Network.Stagenet: {
const inboundObj = await getStagenetInboundObject(hermesApiUrl, "AR");
return inboundObj.address;
}
case Network.Mainnet: {
return "";
}
}
}
async getDefaultLiquidityPoolGasFee(hermesApiUrl: string): Promise<number> {
switch (this.network) {
case Network.Testnet: {
const inboundObj = await getTestnetInboundObject(hermesApiUrl, "AR");
/** Convert from Winston to Ar. (1 Ar = 10^12) */
const arGasFee = this.arweave.ar.winstonToAr(inboundObj.gas_rate);
return Number(arGasFee);
}
case Network.Stagenet: {
const inboundObj = await getStagenetInboundObject(hermesApiUrl, "AR");
/** Convert from Winston to Ar. (1 Ar = 10^12) */
const arGasFee = this.arweave.ar.winstonToAr(inboundObj.gas_rate);
return Number(arGasFee);
}
case Network.Mainnet: {
return 0;
}
}
}
async addLiquidityPool(
amount: number,
inboundAddress: string,
hermesAddress?: string
): Promise<string> {
const tag = hermesAddress
? new Tag(
this.arweave.utils.stringToB64Url("memo"),
this.arweave.utils.stringToB64Url(`ADD:AR.AR:${hermesAddress}`)
)
: new Tag(
this.arweave.utils.stringToB64Url("memo"),
this.arweave.utils.stringToB64Url(`ADD:AR.AR`)
);
const rawTx = await this.createTransaction(inboundAddress, amount, tag);
const txHash = await this.signAndSend(rawTx);
return txHash;
}
async swap(
amount: number,
token: SwapAssetList,
inboundAddress: string,
recipient: string
): Promise<string> {
const tag = new Tag(
this.arweave.utils.stringToB64Url("memo"),
this.arweave.utils.stringToB64Url(`SWAP:${token}:${recipient}`)
);
const rawTx = await this.createTransaction(inboundAddress, amount, tag);
const txHash = await this.signAndSend(rawTx);
return txHash;
}
}
export { ArweaveClient };