-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dashboard: ntt total supply on evm vs total locked on sol
Signed-off-by: bingyuyap <bingyu.yap.21@gmail.com>
- Loading branch information
Showing
21 changed files
with
708 additions
and
428 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { | ||
assertEnvironmentVariable, | ||
NTT_MANAGER_CONTRACT, | ||
NTT_TOKENS, | ||
NTT_TRANSCEIVER_CONTRACT, | ||
NTT_SUPPORTED_CHAINS, | ||
getEvmTokenDecimals, | ||
getSolanaTokenDecimals, | ||
NTTRateLimit, | ||
} from '@wormhole-foundation/wormhole-monitor-common'; | ||
import { EvmPlatform, EvmChains } from '@wormhole-foundation/sdk-evm'; | ||
import { SolanaPlatform } from '@wormhole-foundation/sdk-solana'; | ||
import { EvmNtt } from '@wormhole-foundation/sdk-evm-ntt'; | ||
import { SolanaNtt } from '@wormhole-foundation/sdk-solana-ntt'; | ||
import { Network, Chain, contracts, rpc, chainToChainId } from '@wormhole-foundation/sdk-base'; | ||
import { Storage } from '@google-cloud/storage'; | ||
|
||
const storage = new Storage(); | ||
let bucketName: string = 'wormhole-ntt-cache'; | ||
if (assertEnvironmentVariable('NETWORK') === 'Testnet') { | ||
bucketName = 'wormhole-ntt-cache-testnet'; | ||
} | ||
|
||
const cacheBucket = storage.bucket(bucketName); | ||
const cacheFileName = 'ntt-rate-limits-cache.json'; | ||
const cloudStorageCache = cacheBucket.file(cacheFileName); | ||
|
||
async function computeNTTRateLimits_( | ||
network: Network, | ||
token: string, | ||
chain: Chain | ||
): Promise<NTTRateLimit> { | ||
let ntt: EvmNtt<Network, EvmChains> | SolanaNtt<Network, 'Solana'>; | ||
let tokenDecimals: number; | ||
const rpcEndpoint = rpc.rpcAddress(network, chain as Chain); | ||
const tokenAddress = NTT_TOKENS[network][token][chain]!; | ||
const managerContract = NTT_MANAGER_CONTRACT[network][token][chain]!; | ||
const transceiverContract = NTT_TRANSCEIVER_CONTRACT[network][token][chain]!; | ||
|
||
if (chain === 'Solana') { | ||
const platform = new SolanaPlatform(network); | ||
ntt = new SolanaNtt(network, chain, platform.getRpc(chain), { | ||
coreBridge: contracts.coreBridge(network, chain), | ||
ntt: { | ||
token: tokenAddress, | ||
manager: managerContract, | ||
transceiver: { | ||
wormhole: transceiverContract, | ||
}, | ||
}, | ||
}); | ||
tokenDecimals = await getSolanaTokenDecimals(rpcEndpoint, tokenAddress); | ||
} else { | ||
const evmChain = chain as EvmChains; | ||
const platform = new EvmPlatform(network); | ||
ntt = new EvmNtt(network, evmChain, platform.getRpc(evmChain), { | ||
ntt: { | ||
token: tokenAddress, | ||
manager: managerContract, | ||
transceiver: { | ||
wormhole: transceiverContract, | ||
}, | ||
}, | ||
}); | ||
tokenDecimals = await getEvmTokenDecimals(rpcEndpoint, managerContract); | ||
} | ||
|
||
const outboundCapacity = await ntt.getCurrentOutboundCapacity(); | ||
const normalizedOutboundCapacity = outboundCapacity / BigInt(10 ** tokenDecimals); | ||
|
||
const inboundChains = NTT_SUPPORTED_CHAINS(network, token).filter( | ||
(inboundChain) => inboundChain !== chain | ||
); | ||
|
||
let totalInboundCapacity = BigInt(0); | ||
const inboundRateLimits = await Promise.all( | ||
inboundChains.map(async (inboundChain): Promise<NTTRateLimit> => { | ||
const inboundCapacity = await ntt.getCurrentInboundCapacity(inboundChain); | ||
const normalizedInboundCapacity = inboundCapacity / BigInt(10 ** tokenDecimals); | ||
totalInboundCapacity += normalizedInboundCapacity; | ||
|
||
return { | ||
tokenName: token, | ||
srcChain: chainToChainId(inboundChain), | ||
destChain: chainToChainId(chain), | ||
amount: normalizedInboundCapacity.toString(), | ||
}; | ||
}) | ||
); | ||
|
||
return { | ||
tokenName: token, | ||
srcChain: chainToChainId(chain), | ||
amount: normalizedOutboundCapacity.toString(), | ||
inboundCapacity: inboundRateLimits, | ||
totalInboundCapacity: totalInboundCapacity.toString(), | ||
}; | ||
} | ||
|
||
export async function computeNTTRateLimits(req: any, res: any) { | ||
res.set('Access-Control-Allow-Origin', '*'); | ||
if (req.method === 'OPTIONS') { | ||
res.set('Access-Control-Allow-Methods', 'GET'); | ||
res.set('Access-Control-Allow-Headers', 'Content-Type'); | ||
res.set('Access-Control-Max-Age', '3600'); | ||
res.sendStatus(204); | ||
return; | ||
} | ||
|
||
try { | ||
const network = assertEnvironmentVariable('NETWORK') as Network; | ||
const managerContracts = NTT_MANAGER_CONTRACT[network]; | ||
|
||
const rateLimits = await Promise.all( | ||
Object.entries(managerContracts).map(async ([token, manager]) => { | ||
const inboundCapacityPromises = Object.entries(manager) | ||
.map(([chain, contract]) => | ||
contract ? computeNTTRateLimits_(network, token, chain as Chain) : null | ||
) | ||
.filter(Boolean); | ||
|
||
const inboundCapacity = await Promise.all(inboundCapacityPromises); | ||
|
||
return { | ||
tokenName: token, | ||
inboundCapacity: inboundCapacity, | ||
}; | ||
}) | ||
); | ||
await cloudStorageCache.save(JSON.stringify(rateLimits)); | ||
res.status(200).send('Rate limits saved'); | ||
} catch (e) { | ||
console.error(e); | ||
res.sendStatus(500); | ||
} | ||
} |
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,107 @@ | ||
import { | ||
NTT_MANAGER_CONTRACT, | ||
NTT_TOKENS, | ||
assertEnvironmentVariable, | ||
derivePda, | ||
getEvmTokenDecimals, | ||
getEvmTotalSupply, | ||
} from '@wormhole-foundation/wormhole-monitor-common'; | ||
import { PublicKey } from '@solana/web3.js'; | ||
import { | ||
getCustody, | ||
getCustodyAmount, | ||
NTTTotalSupplyAndLockedData, | ||
} from '@wormhole-foundation/wormhole-monitor-common'; | ||
import { Network, rpc, Chain, chainToChainId } from '@wormhole-foundation/sdk-base'; | ||
import { Storage } from '@google-cloud/storage'; | ||
|
||
const storage = new Storage(); | ||
let bucketName: string = 'wormhole-ntt-cache'; | ||
if (assertEnvironmentVariable('NETWORK') === 'Testnet') { | ||
bucketName = 'wormhole-ntt-cache-testnet'; | ||
} | ||
|
||
const cacheBucket = storage.bucket(bucketName); | ||
const cacheFileName = 'ntt-total-supply-and-locked.json'; | ||
const cloudStorageCache = cacheBucket.file(cacheFileName); | ||
|
||
async function getEvmNormalizedTotalSupply( | ||
network: Network, | ||
token: string, | ||
chain: Chain | ||
): Promise<number> { | ||
const tokenDecimals = await getEvmTokenDecimals( | ||
rpc.rpcAddress(network, chain), | ||
NTT_MANAGER_CONTRACT[network][token][chain]! | ||
); | ||
const tokenSupply = await getEvmTotalSupply( | ||
rpc.rpcAddress(network, chain), | ||
NTT_TOKENS[network][token][chain]! | ||
); | ||
|
||
return tokenSupply / 10 ** tokenDecimals; | ||
} | ||
|
||
async function fetchTotalSupplyAndLocked(network: Network): Promise<NTTTotalSupplyAndLockedData[]> { | ||
const tokens = NTT_MANAGER_CONTRACT[network]; | ||
const totalSupplyVsLocked: NTTTotalSupplyAndLockedData[] = []; | ||
for (const token in tokens) { | ||
if (!NTT_MANAGER_CONTRACT[network][token].Solana) continue; | ||
|
||
const programId = new PublicKey(NTT_MANAGER_CONTRACT[network][token].Solana!); | ||
const pda = derivePda('config', programId); | ||
const custody = await getCustody(rpc.rpcAddress(network, 'Solana'), pda.toBase58()); | ||
const locked = await getCustodyAmount(rpc.rpcAddress(network, 'Solana'), custody); | ||
|
||
const evmTotalSupply: NTTTotalSupplyAndLockedData[] = []; | ||
let totalSupply = 0; | ||
for (const [supportedChain] of Object.entries(NTT_TOKENS[network][token])) { | ||
if (supportedChain === 'Solana') continue; | ||
const tokenSupplyNormalized = await getEvmNormalizedTotalSupply( | ||
network, | ||
token, | ||
supportedChain as Chain | ||
); | ||
|
||
evmTotalSupply.push({ | ||
tokenName: token, | ||
chain: chainToChainId(supportedChain as Chain), | ||
totalSupply: tokenSupplyNormalized, | ||
}); | ||
|
||
totalSupply += tokenSupplyNormalized; | ||
} | ||
|
||
totalSupplyVsLocked.push({ | ||
chain: chainToChainId('Solana'), | ||
tokenName: token, | ||
amountLocked: locked, | ||
totalSupply, | ||
evmTotalSupply, | ||
}); | ||
} | ||
|
||
return totalSupplyVsLocked; | ||
} | ||
|
||
export async function computeTotalSupplyAndLocked(req: any, res: any) { | ||
res.set('Access-Control-Allow-Origin', '*'); | ||
if (req.method === 'OPTIONS') { | ||
res.set('Access-Control-Allow-Methods', 'GET'); | ||
res.set('Access-Control-Allow-Headers', 'Content-Type'); | ||
res.set('Access-Control-Max-Age', '3600'); | ||
res.sendStatus(204); | ||
return; | ||
} | ||
|
||
try { | ||
const network = assertEnvironmentVariable('NETWORK') as Network; | ||
const totalSupplyAndLocked = await fetchTotalSupplyAndLocked(network); | ||
await cloudStorageCache.save(JSON.stringify(totalSupplyAndLocked)); | ||
|
||
res.status(200).send('Total supply and locked saved'); | ||
} catch (e) { | ||
console.error(e); | ||
res.sendStatus(500); | ||
} | ||
} |
Oops, something went wrong.