-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from ava-labs/dev
v0.9.8
- Loading branch information
Showing
40 changed files
with
6,030 additions
and
1,097 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { MnemonicWallet } from '../dist'; | ||
|
||
const mnemonic = `state usage height lumber ski federal silly axis train sustain pizza try embark giraffe motion account loud actress blood chapter blame network blossom hat`; | ||
// or generate new mnemonic | ||
// const mnemonic = MnemonicWallet.generateMnemonicPhrase() | ||
|
||
let account0 = new MnemonicWallet(mnemonic); | ||
let account1 = new MnemonicWallet(mnemonic, 1); | ||
let account2 = new MnemonicWallet(mnemonic, 2); | ||
|
||
// Account 0 | ||
console.log('Account 0'); | ||
console.log('X:', account0.getAddressX()); | ||
console.log('C:', account0.getAddressC()); | ||
// Account 1 | ||
console.log('Account 1'); | ||
console.log('X:', account1.getAddressX()); | ||
console.log('C:', account1.getAddressC()); | ||
// Account 2 | ||
console.log('Account 2'); | ||
console.log('X:', account2.getAddressX()); | ||
console.log('C:', account2.getAddressC()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { ITransactionData, ITransactionDataEVM } from '@/History/raw_types'; | ||
import { explorer_api } from '@/Network/network'; | ||
import { NO_EXPLORER_API } from '@/errors'; | ||
import { ChainIdType } from '@/types'; | ||
|
||
/** | ||
* Returns transactions FROM and TO the address given | ||
* @param addr The address to get historic transactions for. | ||
*/ | ||
export async function getAddressHistoryEVM(addr: string): Promise<ITransactionDataEVM[]> { | ||
if (!explorer_api) { | ||
throw NO_EXPLORER_API; | ||
} | ||
|
||
let endpoint = `v2/ctransactions?address=${addr}`; | ||
let data: ITransactionDataEVM[] = (await explorer_api.get(endpoint)).data.Transactions; | ||
|
||
data.sort((a, b) => { | ||
let dateA = new Date(a.createdAt); | ||
let dateB = new Date(b.createdAt); | ||
|
||
return dateB.getTime() - dateA.getTime(); | ||
}); | ||
|
||
return data; | ||
} | ||
|
||
export async function getAddressHistory( | ||
addrs: string[], | ||
limit = 20, | ||
chainID: string, | ||
endTime?: string | ||
): Promise<ITransactionData[]> { | ||
if (!explorer_api) { | ||
throw NO_EXPLORER_API; | ||
} | ||
|
||
const ADDR_SIZE = 1024; | ||
let selection = addrs.slice(0, ADDR_SIZE); | ||
let remaining = addrs.slice(ADDR_SIZE); | ||
|
||
let addrsRaw = selection.map((addr) => { | ||
return addr.split('-')[1]; | ||
}); | ||
|
||
let rootUrl = 'v2/transactions'; | ||
|
||
let req = { | ||
address: addrsRaw, | ||
sort: ['timestamp-desc'], | ||
disableCount: ['1'], | ||
chainID: [chainID], | ||
disableGenesis: ['false'], | ||
}; | ||
|
||
if (limit > 0) { | ||
//@ts-ignore | ||
req.limit = [limit.toString()]; | ||
} | ||
|
||
if (endTime) { | ||
//@ts-ignore | ||
req.endTime = [endTime]; | ||
} | ||
|
||
let res = await explorer_api.post(rootUrl, req); | ||
let txs = res.data.transactions; | ||
let next: string | undefined = res.data.next; | ||
|
||
if (txs === null) txs = []; | ||
|
||
// If we need to fetch more for this address | ||
if (next && !limit) { | ||
let endTime = next.split('&')[0].split('=')[1]; | ||
let nextRes = await getAddressHistory(selection, limit, chainID, endTime); | ||
txs.push(...nextRes); | ||
} | ||
|
||
// If there are addresses left, fetch them too | ||
// TODO: Do this in parallel, not recursive | ||
if (remaining.length > 0) { | ||
let nextRes = await getAddressHistory(remaining, limit, chainID); | ||
txs.push(...nextRes); | ||
} | ||
|
||
return txs; | ||
} | ||
|
||
/** | ||
* Returns the ortelius data from the given tx id. | ||
* @param txID | ||
*/ | ||
export async function getTx(txID: string): Promise<ITransactionData> { | ||
if (!explorer_api) { | ||
throw NO_EXPLORER_API; | ||
} | ||
|
||
let url = `v2/transactions/${txID}`; | ||
let res = await explorer_api.get(url); | ||
return res.data; | ||
} | ||
|
||
/** | ||
* Returns ortelius data for a transaction hash on C chain EVM, | ||
* @param txHash | ||
*/ | ||
export async function getTxEvm(txHash: string): Promise<ITransactionDataEVM> { | ||
if (!explorer_api) { | ||
throw NO_EXPLORER_API; | ||
} | ||
|
||
let endpoint = `v2/ctransactions?hash=${txHash}`; | ||
let data: ITransactionDataEVM = (await explorer_api.get(endpoint)).data.Transactions[0]; | ||
|
||
return data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import { | ||
iHistoryBaseTx, | ||
iHistoryBaseTxNFTsReceived, | ||
iHistoryBaseTxNFTsReceivedRaw, | ||
iHistoryBaseTxNFTsSent, | ||
iHistoryBaseTxNFTsSentRaw, | ||
iHistoryBaseTxToken, | ||
iHistoryBaseTxTokenLossGain, | ||
iHistoryBaseTxTokenOwners, | ||
ITransactionData, | ||
UTXO, | ||
} from '@/History'; | ||
import * as Assets from '@/Asset/Assets'; | ||
import { bnToLocaleString, getTxFeeX } from '@/utils'; | ||
import { AVMConstants } from 'avalanche/dist/apis/avm'; | ||
import { BN } from 'avalanche'; | ||
import { getAssetBalanceFromUTXOs, getNFTBalanceFromUTXOs, parseMemo } from '@/History/history_helpers'; | ||
import { | ||
filterDuplicateStrings, | ||
getAssetOutputs, | ||
getNotOwnedOutputs, | ||
getOutputsAssetIDs, | ||
getOutputsAssetOwners, | ||
getOutputsOfType, | ||
getOutputTotals, | ||
getOwnedOutputs, | ||
} from '@/History/utxo_helpers'; | ||
import { getAvaxAssetID } from '@/Network'; | ||
|
||
export async function getBaseTxSummary(tx: ITransactionData, ownerAddrs: string[]): Promise<iHistoryBaseTx> { | ||
let ins = tx.inputs?.map((input) => input.output) || []; | ||
let outs = tx.outputs || []; | ||
|
||
// Calculate losses from inputs | ||
let losses = getOwnedTokens(ins, ownerAddrs); | ||
let gains = getOwnedTokens(outs, ownerAddrs); | ||
|
||
let nowOwnedIns = getNotOwnedOutputs(ins, ownerAddrs); | ||
let nowOwnedOuts = getNotOwnedOutputs(outs, ownerAddrs); | ||
|
||
let froms = getOutputsAssetOwners(nowOwnedIns); | ||
let tos = getOutputsAssetOwners(nowOwnedOuts); | ||
|
||
let tokens = await getBaseTxTokensSummary(gains, losses, froms, tos); | ||
|
||
return { | ||
id: tx.id, | ||
fee: getTxFeeX(), | ||
type: 'transaction', | ||
timestamp: new Date(tx.timestamp), | ||
memo: parseMemo(tx.memo), | ||
tokens: tokens, | ||
}; | ||
} | ||
|
||
function getBaseTxNFTLosses(tx: ITransactionData, ownerAddrs: string[]): iHistoryBaseTxNFTsSentRaw { | ||
let ins = tx.inputs || []; | ||
let inUTXOs = ins.map((input) => input.output); | ||
let nftUTXOs = inUTXOs.filter((utxo) => { | ||
return utxo.outputType === AVMConstants.NFTXFEROUTPUTID; | ||
}); | ||
|
||
let res: iHistoryBaseTxNFTsSentRaw = {}; | ||
for (let assetID in tx.inputTotals) { | ||
let nftBal = getNFTBalanceFromUTXOs(nftUTXOs, ownerAddrs, assetID); | ||
|
||
// If empty dictionary pass | ||
if (Object.keys(nftBal).length === 0) continue; | ||
|
||
res[assetID] = nftBal; | ||
} | ||
return res; | ||
} | ||
|
||
function getBaseTxNFTGains(tx: ITransactionData, ownerAddrs: string[]): iHistoryBaseTxNFTsReceivedRaw { | ||
let outs = tx.outputs || []; | ||
let nftUTXOs = outs.filter((utxo) => { | ||
return utxo.outputType === AVMConstants.NFTXFEROUTPUTID; | ||
}); | ||
let res: iHistoryBaseTxNFTsReceivedRaw = {}; | ||
|
||
for (let assetID in tx.inputTotals) { | ||
let nftBal = getNFTBalanceFromUTXOs(nftUTXOs, ownerAddrs, assetID); | ||
// If empty dictionary pass | ||
if (Object.keys(nftBal).length === 0) continue; | ||
|
||
res[assetID] = nftBal; | ||
} | ||
return res; | ||
} | ||
|
||
/** | ||
* Returns a dictionary of asset totals belonging to the owner | ||
* @param utxos | ||
* @param ownerAddrs | ||
*/ | ||
function getOwnedTokens(utxos: UTXO[], ownerAddrs: string[]): iHistoryBaseTxTokenLossGain { | ||
let tokenUTXOs = getOutputsOfType(utxos, AVMConstants.SECPXFEROUTPUTID); | ||
// Owned inputs | ||
let myUTXOs = getOwnedOutputs(tokenUTXOs, ownerAddrs); | ||
|
||
// Asset IDs received | ||
let assetIDs = getOutputsAssetIDs(myUTXOs); | ||
|
||
let res: iHistoryBaseTxTokenLossGain = {}; | ||
|
||
for (let i = 0; i < assetIDs.length; i++) { | ||
let assetID = assetIDs[i]; | ||
let assetUTXOs = getAssetOutputs(myUTXOs, assetID); | ||
let tot = getOutputTotals(assetUTXOs); | ||
res[assetID] = tot; | ||
} | ||
|
||
return res; | ||
} | ||
|
||
async function getBaseTxTokensSummary( | ||
gains: iHistoryBaseTxTokenLossGain, | ||
losses: iHistoryBaseTxTokenLossGain, | ||
froms: iHistoryBaseTxTokenOwners, | ||
tos: iHistoryBaseTxTokenOwners | ||
): Promise<iHistoryBaseTxToken[]> { | ||
let res: iHistoryBaseTxToken[] = []; | ||
|
||
let assetIDs = filterDuplicateStrings([...Object.keys(gains), ...Object.keys(losses)]); | ||
|
||
// Fetch asset descriptions | ||
let calls = assetIDs.map((id) => Assets.getAssetDescription(id)); | ||
let descs = await Promise.all(calls); | ||
let descsDict: any = {}; | ||
|
||
// Convert array to dict | ||
for (let i = 0; i < descs.length; i++) { | ||
let desc = descs[i]; | ||
descsDict[desc.assetID] = desc; | ||
} | ||
|
||
for (let i = 0; i < assetIDs.length; i++) { | ||
let id = assetIDs[i]; | ||
let tokenGain = gains[id] || new BN(0); | ||
let tokenLost = losses[id] || new BN(0); | ||
let tokenDesc = descsDict[id]; | ||
|
||
// If we sent avax, deduct the fee | ||
if (id === getAvaxAssetID() && !tokenLost.isZero()) { | ||
tokenLost = tokenLost.sub(getTxFeeX()); | ||
} | ||
|
||
// How much we gained/lost of this token | ||
let diff = tokenGain.sub(tokenLost); | ||
let diffClean = bnToLocaleString(diff, tokenDesc.denomination); | ||
|
||
// If we didnt gain or lose anything, ignore this token | ||
if (diff.isZero()) continue; | ||
|
||
if (diff.isNeg()) { | ||
res.push({ | ||
amount: diff, | ||
amountDisplayValue: diffClean, | ||
addresses: tos[id], | ||
asset: tokenDesc, | ||
}); | ||
} else { | ||
res.push({ | ||
amount: diff, | ||
amountDisplayValue: diffClean, | ||
addresses: froms[id], | ||
asset: tokenDesc, | ||
}); | ||
} | ||
} | ||
|
||
return res; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { ITransactionDataEVM } from '@/History/raw_types'; | ||
import { iHistoryEVMTx } from '@/History/parsed_types'; | ||
import { bnToAvaxC } from '@/utils'; | ||
import { BN } from 'avalanche'; | ||
|
||
export function getTransactionSummaryEVM(tx: ITransactionDataEVM, walletAddress: string): iHistoryEVMTx { | ||
let isSender = tx.fromAddr.toUpperCase() === walletAddress.toUpperCase(); | ||
|
||
let amt = new BN(tx.value); | ||
let amtClean = bnToAvaxC(amt); | ||
let date = new Date(tx.createdAt); | ||
|
||
let gasLimit = new BN(tx.gasLimit); | ||
let gasPrice = new BN(tx.gasPrice); | ||
let feeBN = gasLimit.mul(gasPrice); // in gwei | ||
|
||
return { | ||
id: tx.hash, | ||
fee: feeBN, | ||
memo: '', | ||
block: tx.block, | ||
isSender, | ||
type: 'transaction_evm', | ||
amount: amt, | ||
amountDisplayValue: amtClean, | ||
gasLimit: tx.gasLimit, | ||
gasPrice: tx.gasPrice, | ||
from: tx.fromAddr, | ||
to: tx.toAddr, | ||
timestamp: date, | ||
input: tx.input, | ||
}; | ||
} |
Oops, something went wrong.