Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

circulating supply endpoint update #7

Open
wants to merge 1 commit into
base: classic-main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 85 additions & 31 deletions src/service/treasury/circulatingSupply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,105 @@ import { getTotalSupply } from './totalSupply'
import { isToken, getCirculatingSupply as getTokenCirculatingSupply } from 'service/token'
import getLunaBalance from './getLunaBalance'
import { BOND_DENOM } from 'lib/constant'
import { getTxList, GetTxListParam } from 'service/transaction'
import { Validator } from 'koa-joi-controllers'
import { CHAIN_ID_REGEX } from 'lib/constant'
import { getActiveAccounts } from 'service/dashboard'
import axios, { AxiosResponse, AxiosError } from 'axios'
import { SigningStargateClient, SigningStargateClientOptions } from '@cosmjs/stargate'
import { OfflineDirectSigner, DirectSignResponse } from '@cosmjs/proto-signing'
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx'

import { Tendermint34Client } from '@cosmjs/tendermint-rpc'

const getLunaBalanceMemoized = memoizeCache(getLunaBalance, { promise: true, maxAge: 5 * 60 * 1000 /* 5 minutes */ })

function isDateWithinAYear(timestamp: string): boolean {
const timeDifference = new Date().getTime() - new Date(timestamp).getTime()
const millisecondsInAYear = 365 * 24 * 60 * 60 * 1000
return timeDifference < millisecondsInAYear
}

export async function getCirculatingSupply(input: string): Promise<string> {
if (isToken(input)) {
return getTokenCirculatingSupply(input)
}

const msgKeyMap: Map<string, string> = new Map()
msgKeyMap.set('bank/MsgSend', 'from_address')
msgKeyMap.set('staking/MsgUndelegate', 'delegator_address')
msgKeyMap.set('staking/MsgDelegate', 'delegator_address')

const denom = isActiveCurrency(input) ? currencyToDenom(input.toLowerCase()) : input
const [totalSupply, communityPool] = await Promise.all([getTotalSupply(denom), lcd.getCommunityPool()])
const unvested = await getRepository(UnvestedEntity).find({
where: {
denom
},
order: {
id: 'DESC'
},
take: 1
})

// Initialize circualating supply to total supply
let circulatingSupply = totalSupply

// Remove unvested amount
if (unvested.length) {
circulatingSupply = minus(circulatingSupply, unvested[0].amount)
const Joi = Validator.Joi

const query: GetTxListParam = {
chainId: config.CHAIN_ID,
limit: 100
}

// Special conditions for Luna
if (denom === BOND_DENOM) {
// Remove Luna in community pool
if (communityPool) {
circulatingSupply = minus(circulatingSupply, communityPool.find((c) => c.denom === denom)?.amount || '0')
}
const result = {
supply: 0,
valid: true
}

while (result.valid) {
const txsList = await getTxList(query)

const txPromises = txsList.txs.map(async (tx) => {
if (!isDateWithinAYear(tx.timestamp)) {
result.valid = false
return 0
} else {
return processMessages(tx.tx.value.msg, msgKeyMap, denom)
}
})

const txResults = await Promise.all(txPromises)

// Remove Luna in bank wallets
if (config.BANK_WALLETS.length !== 0) {
const total = await Bluebird.reduce(
config.BANK_WALLETS,
(acc, cur) => getLunaBalanceMemoized(cur).then((balance) => plus(acc, balance)),
'0'
)
circulatingSupply = minus(circulatingSupply, total)
txResults.forEach((supply) => {
result.supply += supply
})

if (!result.valid || !txsList.next) {
break
}

query.offset = txsList.next
}

return input !== denom ? div(circulatingSupply, 1000000) : circulatingSupply
return result.supply.toString()
}

async function processMessages(messages: any[], msgKeyMap: Map<string, string>, denom: string): Promise<number> {
const supplies = await Promise.all(
messages.map(async (msg) => {
if (msgKeyMap.has(msg.type)) {
const key = msgKeyMap.get(msg.type)
if (key) {
const address = msg.value[key]

// Parallelize balance retrieval
const balances = await lcd.getBalance(address)

// Calculate supply for this iteration
const localSupply = balances
.filter((coin) => coin.denom === denom)
.reduce((acc, coin) => acc + parseInt(coin.amount), 0)

return localSupply
}
}
})
)

// Sum up the results after all iterations are completed
const supply = supplies.reduce((acc, value) => {
if (typeof acc === 'number' && typeof value === 'number') {
return acc + value
}
}, 0)

return supply || 0
}