@humanode-hyperlane/metrics 0.0.0-stage → 0.2.8-humanode.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +193 -0
- package/dist/balance.d.ts +93 -0
- package/dist/balance.d.ts.map +1 -0
- package/dist/balance.js +235 -0
- package/dist/balance.js.map +1 -0
- package/dist/gauges.d.ts +41 -0
- package/dist/gauges.d.ts.map +1 -0
- package/dist/gauges.js +100 -0
- package/dist/gauges.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +45 -0
- package/dist/index.test.js.map +1 -0
- package/dist/pushgateway.d.ts +23 -0
- package/dist/pushgateway.d.ts.map +1 -0
- package/dist/pushgateway.js +54 -0
- package/dist/pushgateway.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +63 -0
- package/dist/server.js.map +1 -0
- package/dist/server.test.d.ts +2 -0
- package/dist/server.test.d.ts.map +1 -0
- package/dist/server.test.js +23 -0
- package/dist/server.test.js.map +1 -0
- package/dist/types.d.ts +64 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/update.d.ts +51 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +163 -0
- package/dist/update.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +9 -0
- package/dist/utils.js.map +1 -0
- package/package.json +52 -3
- package/src/balance.ts +380 -0
- package/src/gauges.ts +136 -0
- package/src/index.test.ts +52 -0
- package/src/index.ts +53 -0
- package/src/pushgateway.ts +66 -0
- package/src/server.test.ts +32 -0
- package/src/server.ts +76 -0
- package/src/types.ts +74 -0
- package/src/update.ts +239 -0
- package/src/utils.ts +10 -0
- package/README.md +0 -3
package/dist/update.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { TokenStandard, } from '@humanode-hyperlane/sdk';
|
|
2
|
+
import { isNullish } from '@humanode-hyperlane/utils';
|
|
3
|
+
/**
|
|
4
|
+
* Updates token balance metrics for a warp route token.
|
|
5
|
+
*
|
|
6
|
+
* @param gauges - The warp metrics gauges to update
|
|
7
|
+
* @param warpCore - The WarpCore instance for the route
|
|
8
|
+
* @param token - The token to update metrics for
|
|
9
|
+
* @param balanceInfo - The balance information for the token
|
|
10
|
+
* @param warpRouteId - The warp route identifier
|
|
11
|
+
* @param logger - The logger instance
|
|
12
|
+
*/
|
|
13
|
+
export function updateTokenBalanceMetrics(gauges, warpCore, token, balanceInfo, warpRouteId, logger) {
|
|
14
|
+
const allChains = warpCore.getTokenChains().sort();
|
|
15
|
+
const relatedChains = allChains.filter((chainName) => chainName !== token.chainName);
|
|
16
|
+
const metrics = {
|
|
17
|
+
chain_name: token.chainName,
|
|
18
|
+
token_address: balanceInfo.tokenAddress,
|
|
19
|
+
token_name: token.name,
|
|
20
|
+
wallet_address:
|
|
21
|
+
// the balance for an EvmHypERC20 token is returned as the total supply of the xERC20 token,
|
|
22
|
+
// therefore we set the wallet address to the token address,
|
|
23
|
+
// we follow the same pattern or synthetic tokens
|
|
24
|
+
token.standard !== TokenStandard.EvmHypXERC20
|
|
25
|
+
? token.addressOrDenom
|
|
26
|
+
: balanceInfo.tokenAddress,
|
|
27
|
+
token_standard:
|
|
28
|
+
// as we are reporting the total supply for clarity we report the standard as xERC20
|
|
29
|
+
token.standard !== TokenStandard.EvmHypXERC20 ? token.standard : 'xERC20',
|
|
30
|
+
warp_route_id: warpRouteId,
|
|
31
|
+
// TODO: consider deprecating this label given that we have the value at risk metric
|
|
32
|
+
related_chain_names: relatedChains.join(','),
|
|
33
|
+
};
|
|
34
|
+
gauges.warpRouteTokenBalance.labels({ ...metrics }).set(balanceInfo.balance);
|
|
35
|
+
logger.info({
|
|
36
|
+
labels: metrics,
|
|
37
|
+
balance: balanceInfo.balance,
|
|
38
|
+
}, 'Wallet balance updated for token');
|
|
39
|
+
if (!isNullish(balanceInfo.valueUSD)) {
|
|
40
|
+
// TODO: consider deprecating this metric in favor of the value at risk metric
|
|
41
|
+
gauges.warpRouteCollateralValue
|
|
42
|
+
.labels({ ...metrics })
|
|
43
|
+
.set(balanceInfo.valueUSD);
|
|
44
|
+
logger.info({
|
|
45
|
+
labels: metrics,
|
|
46
|
+
valueUSD: balanceInfo.valueUSD,
|
|
47
|
+
}, 'Wallet value updated for token');
|
|
48
|
+
for (const chainName of allChains) {
|
|
49
|
+
const labels = {
|
|
50
|
+
chain_name: chainName,
|
|
51
|
+
collateral_chain_name: metrics.chain_name,
|
|
52
|
+
token_address: metrics.token_address,
|
|
53
|
+
token_name: metrics.token_name,
|
|
54
|
+
collateral_token_standard: metrics.token_standard,
|
|
55
|
+
warp_route_id: metrics.warp_route_id,
|
|
56
|
+
};
|
|
57
|
+
gauges.warpRouteValueAtRisk
|
|
58
|
+
.labels({ ...labels })
|
|
59
|
+
.set(balanceInfo.valueUSD);
|
|
60
|
+
logger.info({
|
|
61
|
+
labels,
|
|
62
|
+
valueUSD: balanceInfo.valueUSD,
|
|
63
|
+
}, `Value at risk on ${chainName} updated for token`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Updates managed lockbox balance metrics.
|
|
69
|
+
*
|
|
70
|
+
* @param gauges - The warp metrics gauges to update
|
|
71
|
+
* @param warpCore - The WarpCore instance for the route
|
|
72
|
+
* @param chainName - The chain name where the lockbox is deployed
|
|
73
|
+
* @param tokenName - The name of the collateral token
|
|
74
|
+
* @param tokenAddress - The address of the collateral token
|
|
75
|
+
* @param lockBoxAddress - The address of the managed lockbox
|
|
76
|
+
* @param balanceInfo - The balance information for the lockbox
|
|
77
|
+
* @param warpRouteId - The warp route identifier
|
|
78
|
+
* @param logger - The logger instance
|
|
79
|
+
*/
|
|
80
|
+
export function updateManagedLockboxBalanceMetrics(gauges, warpCore, chainName, tokenName, tokenAddress, lockBoxAddress, balanceInfo, warpRouteId, logger) {
|
|
81
|
+
const metrics = {
|
|
82
|
+
chain_name: chainName,
|
|
83
|
+
token_address: tokenAddress,
|
|
84
|
+
token_name: tokenName,
|
|
85
|
+
wallet_address: lockBoxAddress,
|
|
86
|
+
token_standard: 'EvmManagedLockbox', // TODO: we should eventually add a new TokenStandard for this
|
|
87
|
+
warp_route_id: warpRouteId,
|
|
88
|
+
related_chain_names: warpCore
|
|
89
|
+
.getTokenChains()
|
|
90
|
+
.filter((_chainName) => _chainName !== chainName)
|
|
91
|
+
.sort()
|
|
92
|
+
.join(','),
|
|
93
|
+
};
|
|
94
|
+
gauges.warpRouteTokenBalance.labels({ ...metrics }).set(balanceInfo.balance);
|
|
95
|
+
logger.info({
|
|
96
|
+
labels: metrics,
|
|
97
|
+
balance: balanceInfo.balance,
|
|
98
|
+
}, 'ManagedLockbox collateral balance updated');
|
|
99
|
+
if (!isNullish(balanceInfo.valueUSD)) {
|
|
100
|
+
gauges.warpRouteCollateralValue
|
|
101
|
+
.labels({ ...metrics })
|
|
102
|
+
.set(balanceInfo.valueUSD);
|
|
103
|
+
logger.info({
|
|
104
|
+
labels: metrics,
|
|
105
|
+
valueUSD: balanceInfo.valueUSD,
|
|
106
|
+
}, 'ManagedLockbox value updated for token');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Updates native wallet balance metrics.
|
|
111
|
+
*
|
|
112
|
+
* @param gauges - The warp metrics gauges to update
|
|
113
|
+
* @param balance - The native wallet balance information
|
|
114
|
+
* @param logger - The logger instance
|
|
115
|
+
*/
|
|
116
|
+
export function updateNativeWalletBalanceMetrics(gauges, balance, logger) {
|
|
117
|
+
gauges.walletBalanceGauge
|
|
118
|
+
.labels({
|
|
119
|
+
chain: balance.chain,
|
|
120
|
+
wallet_address: balance.walletAddress,
|
|
121
|
+
wallet_name: balance.walletName,
|
|
122
|
+
token_address: 'native',
|
|
123
|
+
token_symbol: 'Native',
|
|
124
|
+
token_name: 'Native',
|
|
125
|
+
})
|
|
126
|
+
.set(balance.balance);
|
|
127
|
+
logger.info('Native wallet balance updated', {
|
|
128
|
+
balanceInfo: balance,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Updates xERC20 limits metrics.
|
|
133
|
+
*
|
|
134
|
+
* @param gauges - The warp metrics gauges to update
|
|
135
|
+
* @param token - The token to update metrics for
|
|
136
|
+
* @param limits - The xERC20 limits
|
|
137
|
+
* @param bridgeAddress - The address of the bridge
|
|
138
|
+
* @param bridgeLabel - A label for the bridge (e.g., token standard)
|
|
139
|
+
* @param xERC20Address - The address of the xERC20 token
|
|
140
|
+
* @param logger - The logger instance
|
|
141
|
+
*/
|
|
142
|
+
export function updateXERC20LimitsMetrics(gauges, token, limits, bridgeAddress, bridgeLabel, xERC20Address, logger) {
|
|
143
|
+
const labels = {
|
|
144
|
+
chain_name: token.chainName,
|
|
145
|
+
token_name: token.name,
|
|
146
|
+
bridge_address: bridgeAddress,
|
|
147
|
+
token_address: xERC20Address,
|
|
148
|
+
bridge_label: bridgeLabel,
|
|
149
|
+
};
|
|
150
|
+
for (const [limitType, limit] of Object.entries(limits)) {
|
|
151
|
+
gauges.xERC20LimitsGauge
|
|
152
|
+
.labels({
|
|
153
|
+
...labels,
|
|
154
|
+
limit_type: limitType,
|
|
155
|
+
})
|
|
156
|
+
.set(limit);
|
|
157
|
+
}
|
|
158
|
+
logger.info({
|
|
159
|
+
...labels,
|
|
160
|
+
limits,
|
|
161
|
+
}, 'xERC20 limits updated for bridge on token');
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Token } from '@humanode-hyperlane/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Formats a bigint value to a number using the token's decimal precision.
|
|
4
|
+
* Returns 0 if the result is non-finite (NaN, Infinity) to avoid Prometheus dropping the metric.
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatBigInt(token: Token, num: bigint): number;
|
|
7
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAErD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9D"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats a bigint value to a number using the token's decimal precision.
|
|
3
|
+
* Returns 0 if the result is non-finite (NaN, Infinity) to avoid Prometheus dropping the metric.
|
|
4
|
+
*/
|
|
5
|
+
export function formatBigInt(token, num) {
|
|
6
|
+
const result = Number(token.amount(num).getDecimalFormattedAmount());
|
|
7
|
+
return Number.isFinite(result) ? result : 0;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAY,EAAE,GAAW;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,yBAAyB,EAAE,CAAC,CAAC;IACrE,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanode-hyperlane/metrics",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.8-humanode.1",
|
|
4
|
+
"description": "Shared Prometheus metrics utilities for Hyperlane monitoring",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"hyperlane",
|
|
7
|
+
"metrics",
|
|
8
|
+
"monitoring",
|
|
9
|
+
"prometheus",
|
|
10
|
+
"warp-route"
|
|
11
|
+
],
|
|
12
|
+
"license": "Apache-2.0",
|
|
13
|
+
"author": "Abacus Works, Inc.",
|
|
14
|
+
"repository": "https://github.com/humanode-network/hyperlane-monorepo",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"ethers": "^5.8.0",
|
|
24
|
+
"pino": "^8.19.0",
|
|
25
|
+
"prom-client": "^14.0.1",
|
|
26
|
+
"@humanode-hyperlane/core": "11.2.0-humanode.1",
|
|
27
|
+
"@humanode-hyperlane/sdk": "30.0.0-humanode.1",
|
|
28
|
+
"@humanode-hyperlane/utils": "30.0.0-humanode.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/chai": "^4.3.11",
|
|
32
|
+
"@types/mocha": "^10.0.1",
|
|
33
|
+
"@types/node": "^24.10.9",
|
|
34
|
+
"@types/sinon": "^17.0.1",
|
|
35
|
+
"chai": "^4.5.0",
|
|
36
|
+
"mocha": "^11.5.0",
|
|
37
|
+
"oxfmt": "^0.42.0",
|
|
38
|
+
"sinon": "^13.0.2",
|
|
39
|
+
"tsx": "^4.19.1",
|
|
40
|
+
"typescript": "6.0.2",
|
|
41
|
+
"@humanode-hyperlane/tsconfig": "^30.0.0"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc",
|
|
48
|
+
"clean": "rm -rf dist",
|
|
49
|
+
"dev": "tsc --watch",
|
|
50
|
+
"lint": "oxlint -c ../../oxlint.json",
|
|
51
|
+
"format": "oxfmt --write ./src",
|
|
52
|
+
"test": "mocha --config .mocharc.json './src/**/*.test.ts' --exit",
|
|
53
|
+
"test:ci": "pnpm test"
|
|
54
|
+
}
|
|
6
55
|
}
|
package/src/balance.ts
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { Contract, type PopulatedTransaction } from 'ethers';
|
|
2
|
+
import type { Logger } from 'pino';
|
|
3
|
+
|
|
4
|
+
import { IXERC20VS__factory } from '@humanode-hyperlane/core';
|
|
5
|
+
import {
|
|
6
|
+
type EvmHypXERC20Adapter,
|
|
7
|
+
type EvmHypXERC20LockboxAdapter,
|
|
8
|
+
EvmTokenAdapter,
|
|
9
|
+
type IHypXERC20Adapter,
|
|
10
|
+
type MultiProtocolProvider,
|
|
11
|
+
type SealevelHypTokenAdapter,
|
|
12
|
+
Token,
|
|
13
|
+
TokenStandard,
|
|
14
|
+
type WarpCore,
|
|
15
|
+
} from '@humanode-hyperlane/sdk';
|
|
16
|
+
import { type Address, ProtocolType } from '@humanode-hyperlane/utils';
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
NativeWalletBalance,
|
|
20
|
+
WarpRouteBalance,
|
|
21
|
+
XERC20Info,
|
|
22
|
+
XERC20Limit,
|
|
23
|
+
} from './types.js';
|
|
24
|
+
import { formatBigInt } from './utils.js';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Minimal ABI for managed lockbox contracts.
|
|
28
|
+
*/
|
|
29
|
+
export const MANAGED_LOCKBOX_MINIMAL_ABI = [
|
|
30
|
+
'function XERC20() view returns (address)',
|
|
31
|
+
'function ERC20() view returns (address)',
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Interface for token price getter to allow different implementations.
|
|
36
|
+
*/
|
|
37
|
+
export interface TokenPriceGetter {
|
|
38
|
+
tryGetTokenPrice(token: Token): Promise<number | undefined>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets the bridged balance and value of a token in a warp route.
|
|
43
|
+
*
|
|
44
|
+
* @param warpCore - The WarpCore instance for the route
|
|
45
|
+
* @param token - The token to get the balance for
|
|
46
|
+
* @param tokenPriceGetter - Price getter for calculating USD value
|
|
47
|
+
* @param logger - Logger instance
|
|
48
|
+
* @param bridgedSupply - Optional pre-fetched bridged supply
|
|
49
|
+
* @returns The balance information or undefined if not available
|
|
50
|
+
*/
|
|
51
|
+
export async function getTokenBridgedBalance(
|
|
52
|
+
warpCore: WarpCore,
|
|
53
|
+
token: Token,
|
|
54
|
+
tokenPriceGetter: TokenPriceGetter,
|
|
55
|
+
logger: Logger,
|
|
56
|
+
bridgedSupply?: bigint,
|
|
57
|
+
): Promise<WarpRouteBalance | undefined> {
|
|
58
|
+
if (!token.isHypToken()) {
|
|
59
|
+
logger.warn(
|
|
60
|
+
{ token: token.symbol, chain: token.chainName },
|
|
61
|
+
'No support for bridged balance on non-Hyperlane token',
|
|
62
|
+
);
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const adapter = token.getHypAdapter(warpCore.multiProvider);
|
|
67
|
+
let tokenAddress = token.collateralAddressOrDenom ?? token.addressOrDenom;
|
|
68
|
+
|
|
69
|
+
// If bridged supply is not provided, fetch it
|
|
70
|
+
const supply = bridgedSupply ?? (await adapter.getBridgedSupply());
|
|
71
|
+
if (supply === undefined) {
|
|
72
|
+
logger.warn(
|
|
73
|
+
{ token: token.symbol, chain: token.chainName },
|
|
74
|
+
'Failed to get bridged supply',
|
|
75
|
+
);
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const balance = token.amount(supply).getDecimalFormattedAmount();
|
|
80
|
+
|
|
81
|
+
let tokenPrice;
|
|
82
|
+
// Only record value for collateralized and xERC20 lockbox tokens.
|
|
83
|
+
if (
|
|
84
|
+
token.isCollateralized() ||
|
|
85
|
+
token.standard === TokenStandard.EvmHypXERC20Lockbox ||
|
|
86
|
+
token.standard === TokenStandard.EvmHypVSXERC20Lockbox
|
|
87
|
+
) {
|
|
88
|
+
tokenPrice = await tokenPriceGetter.tryGetTokenPrice(token);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
token.standard === TokenStandard.EvmHypXERC20Lockbox ||
|
|
93
|
+
token.standard === TokenStandard.EvmHypVSXERC20Lockbox
|
|
94
|
+
) {
|
|
95
|
+
tokenAddress = (await (adapter as EvmHypXERC20LockboxAdapter).getXERC20())
|
|
96
|
+
.address;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
balance,
|
|
101
|
+
valueUSD: tokenPrice ? balance * tokenPrice : undefined,
|
|
102
|
+
tokenAddress,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets the native balance of the ATA payer for Sealevel tokens.
|
|
108
|
+
*
|
|
109
|
+
* @param warpCore - The WarpCore instance for the route
|
|
110
|
+
* @param token - The Sealevel token
|
|
111
|
+
* @param warpRouteId - The warp route identifier
|
|
112
|
+
* @returns The native wallet balance information
|
|
113
|
+
*/
|
|
114
|
+
export async function getSealevelAtaPayerBalance(
|
|
115
|
+
warpCore: WarpCore,
|
|
116
|
+
token: Token,
|
|
117
|
+
warpRouteId: string,
|
|
118
|
+
): Promise<NativeWalletBalance> {
|
|
119
|
+
if (token.protocol !== ProtocolType.Sealevel || token.isNative()) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Unsupported ATA payer protocol type ${token.protocol} or standard ${token.standard}`,
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const adapter = token.getHypAdapter(
|
|
125
|
+
warpCore.multiProvider,
|
|
126
|
+
) as SealevelHypTokenAdapter;
|
|
127
|
+
|
|
128
|
+
const ataPayer = adapter.deriveAtaPayerAccount().toString();
|
|
129
|
+
const nativeToken = Token.FromChainMetadataNativeToken(
|
|
130
|
+
warpCore.multiProvider.getChainMetadata(token.chainName),
|
|
131
|
+
);
|
|
132
|
+
const ataPayerBalance = await nativeToken.getBalance(
|
|
133
|
+
warpCore.multiProvider,
|
|
134
|
+
ataPayer,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
chain: token.chainName,
|
|
139
|
+
walletAddress: ataPayer.toString(),
|
|
140
|
+
walletName: `${warpRouteId}/ata-payer`,
|
|
141
|
+
balance: ataPayerBalance.getDecimalFormattedAmount(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Gets xERC20 information for a token.
|
|
147
|
+
*
|
|
148
|
+
* @param warpCore - The WarpCore instance for the route
|
|
149
|
+
* @param token - The xERC20 token
|
|
150
|
+
* @returns The xERC20 info including limits and address
|
|
151
|
+
*/
|
|
152
|
+
export async function getXERC20Info(
|
|
153
|
+
warpCore: WarpCore,
|
|
154
|
+
token: Token,
|
|
155
|
+
): Promise<XERC20Info> {
|
|
156
|
+
if (
|
|
157
|
+
token.protocol !== ProtocolType.Ethereum &&
|
|
158
|
+
token.protocol !== ProtocolType.Tron
|
|
159
|
+
) {
|
|
160
|
+
throw new Error(`Unsupported XERC20 protocol type ${token.protocol}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (
|
|
164
|
+
token.standard === TokenStandard.EvmHypXERC20 ||
|
|
165
|
+
token.standard === TokenStandard.EvmHypVSXERC20 ||
|
|
166
|
+
token.standard == TokenStandard.TronHypVSXERC20 ||
|
|
167
|
+
token.standard == TokenStandard.TronHypXERC20
|
|
168
|
+
) {
|
|
169
|
+
const adapter = token.getAdapter(
|
|
170
|
+
warpCore.multiProvider,
|
|
171
|
+
) as EvmHypXERC20Adapter;
|
|
172
|
+
return {
|
|
173
|
+
limits: await getXERC20Limit(token, adapter),
|
|
174
|
+
xERC20Address: (await adapter.getXERC20()).address,
|
|
175
|
+
};
|
|
176
|
+
} else if (
|
|
177
|
+
token.standard === TokenStandard.EvmHypXERC20Lockbox ||
|
|
178
|
+
token.standard === TokenStandard.EvmHypVSXERC20Lockbox ||
|
|
179
|
+
token.standard === TokenStandard.TronHypXERC20Lockbox ||
|
|
180
|
+
token.standard === TokenStandard.TronHypVSXERC20Lockbox
|
|
181
|
+
) {
|
|
182
|
+
const adapter = token.getAdapter(
|
|
183
|
+
warpCore.multiProvider,
|
|
184
|
+
) as EvmHypXERC20LockboxAdapter;
|
|
185
|
+
return {
|
|
186
|
+
limits: await getXERC20Limit(token, adapter),
|
|
187
|
+
xERC20Address: (await adapter.getXERC20()).address,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
throw new Error(`Unsupported XERC20 token standard ${token.standard}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Gets xERC20 limits from an adapter.
|
|
196
|
+
*
|
|
197
|
+
* @param token - The token to get limits for
|
|
198
|
+
* @param xerc20 - The xERC20 adapter
|
|
199
|
+
* @returns The xERC20 limits
|
|
200
|
+
*/
|
|
201
|
+
export async function getXERC20Limit(
|
|
202
|
+
token: Token,
|
|
203
|
+
xerc20: IHypXERC20Adapter<PopulatedTransaction>,
|
|
204
|
+
): Promise<XERC20Limit> {
|
|
205
|
+
const [mintCurrent, mintMax, burnCurrent, burnMax] = await Promise.all([
|
|
206
|
+
xerc20.getMintLimit(),
|
|
207
|
+
xerc20.getMintMaxLimit(),
|
|
208
|
+
xerc20.getBurnLimit(),
|
|
209
|
+
xerc20.getBurnMaxLimit(),
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
mint: formatBigInt(token, mintCurrent),
|
|
214
|
+
mintMax: formatBigInt(token, mintMax),
|
|
215
|
+
burn: formatBigInt(token, burnCurrent),
|
|
216
|
+
burnMax: formatBigInt(token, burnMax),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Gets a managed lockbox contract instance.
|
|
222
|
+
*
|
|
223
|
+
* @param multiProvider - The multi-protocol provider
|
|
224
|
+
* @param chainName - The chain name
|
|
225
|
+
* @param lockboxAddress - The lockbox contract address
|
|
226
|
+
* @returns The Contract instance
|
|
227
|
+
*/
|
|
228
|
+
export function getManagedLockBox(
|
|
229
|
+
multiProvider: MultiProtocolProvider,
|
|
230
|
+
chainName: string,
|
|
231
|
+
lockboxAddress: Address,
|
|
232
|
+
): Contract {
|
|
233
|
+
const provider = multiProvider.getEthersV5Provider(chainName);
|
|
234
|
+
return new Contract(lockboxAddress, MANAGED_LOCKBOX_MINIMAL_ABI, provider);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Gets extra lockbox information for xERC20 tokens.
|
|
239
|
+
*
|
|
240
|
+
* @param multiProvider - The multi-protocol provider
|
|
241
|
+
* @param warpToken - The warp token
|
|
242
|
+
* @param lockboxAddress - The lockbox contract address
|
|
243
|
+
* @returns The xERC20 info for the lockbox
|
|
244
|
+
*/
|
|
245
|
+
export async function getExtraLockboxInfo(
|
|
246
|
+
multiProvider: MultiProtocolProvider,
|
|
247
|
+
warpToken: Token,
|
|
248
|
+
lockboxAddress: Address,
|
|
249
|
+
): Promise<XERC20Info> {
|
|
250
|
+
const currentChainProvider = multiProvider.getEthersV5Provider(
|
|
251
|
+
warpToken.chainName,
|
|
252
|
+
);
|
|
253
|
+
const lockboxInstance = getManagedLockBox(
|
|
254
|
+
multiProvider,
|
|
255
|
+
warpToken.chainName,
|
|
256
|
+
lockboxAddress,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
const xERC20Address = await lockboxInstance['XERC20']();
|
|
260
|
+
const vsXERC20Instance = IXERC20VS__factory.connect(
|
|
261
|
+
xERC20Address,
|
|
262
|
+
currentChainProvider,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const [mintMax, burnMax, mint, burn] = await Promise.all([
|
|
266
|
+
vsXERC20Instance.mintingMaxLimitOf(lockboxAddress),
|
|
267
|
+
vsXERC20Instance.burningMaxLimitOf(lockboxAddress),
|
|
268
|
+
vsXERC20Instance.mintingCurrentLimitOf(lockboxAddress),
|
|
269
|
+
vsXERC20Instance.burningCurrentLimitOf(lockboxAddress),
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
limits: {
|
|
274
|
+
burn: formatBigInt(warpToken, burn.toBigInt()),
|
|
275
|
+
burnMax: formatBigInt(warpToken, burnMax.toBigInt()),
|
|
276
|
+
mint: formatBigInt(warpToken, mint.toBigInt()),
|
|
277
|
+
mintMax: formatBigInt(warpToken, mintMax.toBigInt()),
|
|
278
|
+
},
|
|
279
|
+
xERC20Address,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Gets the balance of an extra lockbox.
|
|
285
|
+
*
|
|
286
|
+
* @param multiProvider - The multi-protocol provider
|
|
287
|
+
* @param warpToken - The warp token
|
|
288
|
+
* @param tokenPriceGetter - Price getter for calculating USD value
|
|
289
|
+
* @param lockboxAddress - The lockbox contract address
|
|
290
|
+
* @param logger - Logger instance
|
|
291
|
+
* @returns The balance information or undefined if not available
|
|
292
|
+
*/
|
|
293
|
+
export async function getExtraLockboxBalance(
|
|
294
|
+
multiProvider: MultiProtocolProvider,
|
|
295
|
+
warpToken: Token,
|
|
296
|
+
tokenPriceGetter: TokenPriceGetter,
|
|
297
|
+
lockboxAddress: Address,
|
|
298
|
+
logger: Logger,
|
|
299
|
+
): Promise<WarpRouteBalance | undefined> {
|
|
300
|
+
if (!warpToken.isXerc20()) {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const lockboxInstance = getManagedLockBox(
|
|
305
|
+
multiProvider,
|
|
306
|
+
warpToken.chainName,
|
|
307
|
+
lockboxAddress,
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
const erc20TokenAddress = await lockboxInstance['ERC20']();
|
|
311
|
+
const erc20tokenAdapter = new EvmTokenAdapter(
|
|
312
|
+
warpToken.chainName,
|
|
313
|
+
multiProvider,
|
|
314
|
+
{
|
|
315
|
+
token: erc20TokenAddress,
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
let balance;
|
|
320
|
+
try {
|
|
321
|
+
balance = await erc20tokenAdapter.getBalance(lockboxAddress);
|
|
322
|
+
} catch (err) {
|
|
323
|
+
logger.error(
|
|
324
|
+
{
|
|
325
|
+
err,
|
|
326
|
+
chain: warpToken.chainName,
|
|
327
|
+
token: warpToken.symbol,
|
|
328
|
+
lockboxAddress,
|
|
329
|
+
erc20TokenAddress,
|
|
330
|
+
},
|
|
331
|
+
'Failed to get balance for contract at lockbox address',
|
|
332
|
+
);
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const tokenPrice = await tokenPriceGetter.tryGetTokenPrice(warpToken);
|
|
337
|
+
const balanceNumber = formatBigInt(warpToken, balance);
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
balance: balanceNumber,
|
|
341
|
+
valueUSD: tokenPrice ? balanceNumber * tokenPrice : undefined,
|
|
342
|
+
tokenAddress: erc20TokenAddress,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Gets collateral info for a managed lockbox.
|
|
348
|
+
*
|
|
349
|
+
* @param multiProvider - The multi-protocol provider
|
|
350
|
+
* @param warpToken - The warp token
|
|
351
|
+
* @param lockBoxAddress - The lockbox contract address
|
|
352
|
+
* @returns The token name and address of the collateral
|
|
353
|
+
*/
|
|
354
|
+
export async function getManagedLockBoxCollateralInfo(
|
|
355
|
+
multiProvider: MultiProtocolProvider,
|
|
356
|
+
warpToken: Token,
|
|
357
|
+
lockBoxAddress: Address,
|
|
358
|
+
): Promise<{ tokenName: string; tokenAddress: Address }> {
|
|
359
|
+
const lockBoxInstance = getManagedLockBox(
|
|
360
|
+
multiProvider,
|
|
361
|
+
warpToken.chainName,
|
|
362
|
+
lockBoxAddress,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const collateralTokenAddress = await lockBoxInstance['ERC20']();
|
|
366
|
+
const collateralTokenAdapter = new EvmTokenAdapter(
|
|
367
|
+
warpToken.chainName,
|
|
368
|
+
multiProvider,
|
|
369
|
+
{
|
|
370
|
+
token: collateralTokenAddress,
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const { name } = await collateralTokenAdapter.getMetadata();
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
tokenName: name,
|
|
378
|
+
tokenAddress: collateralTokenAddress,
|
|
379
|
+
};
|
|
380
|
+
}
|