@ledgerhq/coin-algorand
Version:
Ledger Algorand Coin integration
171 lines (158 loc) • 4.88 kB
text/typescript
import network from "@ledgerhq/live-network";
import { getEnv } from "@ledgerhq/live-env";
import { BigNumber } from "bignumber.js";
import {
AlgoAssetTransferInfo,
AlgoPaymentInfo,
AlgoTransaction,
AlgoTransactionDetails,
} from "./indexer.types";
const LIMIT = 100; // Max nb of transactions per request
const BASE_URL = getEnv("API_ALGORAND_BLOCKCHAIN_EXPLORER_API_ENDPOINT");
const INDEXER_URL = `${BASE_URL}/idx2/v2`;
const fullUrl = (route: string): string => `${INDEXER_URL}${route}?limit=${LIMIT}`;
type ExplorerTransactions = {
"current-round": number;
"next-token": string;
transactions: ExplorerTransaction[];
};
type ExplorerTransaction = {
"application-transaction"?: {
accounts: string[];
"application-args": string[];
"application-id": number;
"foreign-apps": unknown[];
"foreign-assets": number[];
"global-state-schema": {
"num-byte-slice": number;
"num-uint": number;
};
"local-state-schema": {
"num-byte-slice": number;
"num-uint": number;
};
"on-completion": string;
};
"asset-transfer-transaction"?: {
amount: number;
"asset-id": number;
"close-amount": number;
"close-to"?: string;
receiver: string;
};
"close-rewards": number;
"closing-amount": number;
"confirmed-round": number;
fee: number;
"first-valid": number;
"genesis-hash": string;
"genesis-id": string;
"global-state-delta": {
key: string;
value: {
action: number;
uint: number;
bytes?: string;
};
}[];
id: string;
"intra-round-offset": number;
"last-valid": number;
"local-state-delta": {
address: string;
delta: {
key: string;
value: {
action: number;
uint: number;
};
}[];
}[];
note: string;
"payment-transaction"?: {
amount: number;
"close-amount": number;
"close-remainder-to"?: string;
receiver: string;
};
"receiver-rewards": number;
"round-time": number;
sender: string;
"sender-rewards": number;
signature: {
sig: string;
};
"tx-type": string;
};
export const getAccountTransactions = async (
address: string,
startAt?: number,
): Promise<AlgoTransaction[]> => {
const url = fullUrl(`/accounts/${address}/transactions`);
let nextToken: string | undefined;
let newRawTxs: any[] = [];
const mergedTxs: AlgoTransaction[] = [];
do {
let nextUrl: string = url;
if (startAt) {
nextUrl = nextUrl.concat(`&min-round=${startAt}`);
}
if (nextToken) {
nextUrl = nextUrl.concat(`&next=${nextToken}`);
}
const { data }: { data: { transactions: any[] } } = await network<ExplorerTransactions>({
url: nextUrl,
});
// FIXME: what is the correct type? Properly type response from api above (data)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
nextToken = data["next-token"];
newRawTxs = data.transactions;
newRawTxs.map(parseRawTransaction).forEach(tx => mergedTxs.push(tx));
} while (newRawTxs.length >= LIMIT);
return mergedTxs;
};
const parseRawTransaction = (tx: ExplorerTransaction): AlgoTransaction => {
let details: AlgoTransactionDetails | undefined = undefined;
if (tx["tx-type"] === "pay") {
// If "tx-type" is "pay", we know we received a "payment-transaction"
const info = tx["payment-transaction"]!;
const paymentInfo: AlgoPaymentInfo = {
amount: new BigNumber(info.amount),
recipientAddress: info.receiver,
closeAmount:
info["close-amount"] === undefined ? undefined : new BigNumber(info["close-amount"]),
closeToAddress: info["close-remainder-to"],
};
details = paymentInfo;
} else if (tx["tx-type"] === "axfer") {
// If "tx-type" is "axfer", we know we received a "asset-transfer-transaction"
const info = tx["asset-transfer-transaction"]!;
const assetTransferInfo: AlgoAssetTransferInfo = {
assetAmount: new BigNumber(info.amount),
assetId: info["asset-id"].toString(),
assetRecipientAddress: info.receiver,
assetSenderAddress: tx.sender,
assetCloseAmount:
info["close-amount"] === undefined ? undefined : new BigNumber(info["close-amount"]),
assetCloseToAddress: info["close-to"],
};
details = assetTransferInfo;
}
return {
id: tx.id,
timestamp: tx["round-time"].toString(),
round: tx["confirmed-round"],
senderAddress: tx.sender,
senderRewards: new BigNumber(tx["sender-rewards"]),
recipientRewards: new BigNumber(tx["receiver-rewards"]),
closeRewards:
tx["close-rewards"] === undefined ? undefined : new BigNumber(tx["close-rewards"]),
closeAmount:
tx["closing-amount"] === undefined ? undefined : new BigNumber(tx["closing-amount"]),
fee: new BigNumber(tx.fee),
note: tx.note,
type: tx["tx-type"],
details,
};
};