@talismn/balances 0.0.0-pr2075-20250703111149 → 0.0.0-pr2075-20250707042608
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/dist/declarations/src/NewBalanceModules.d.ts +99 -0
- package/dist/declarations/src/index.d.ts +2 -0
- package/dist/declarations/src/modules/EvmErc20Module.d.ts +1 -1
- package/dist/declarations/src/modules/IBalanceModule.d.ts +100 -0
- package/dist/declarations/src/modules/abis/index.d.ts +2 -0
- package/dist/declarations/src/modules/evm-erc20/config.d.ts +16 -0
- package/dist/declarations/src/modules/evm-erc20/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/evm-erc20/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/evm-erc20/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/evm-erc20/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/evm-erc20/index.d.ts +1 -0
- package/dist/declarations/src/modules/evm-erc20/module.d.ts +3 -0
- package/dist/declarations/src/modules/evm-erc20/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/evm-erc20/utils.d.ts +6 -0
- package/dist/declarations/src/modules/evm-native/config.d.ts +15 -0
- package/dist/declarations/src/modules/evm-native/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/evm-native/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/evm-native/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/evm-native/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/evm-native/index.d.ts +1 -0
- package/dist/declarations/src/modules/evm-native/module.d.ts +3 -0
- package/dist/declarations/src/modules/evm-native/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/config.d.ts +16 -0
- package/dist/declarations/src/modules/evm-uniswapv2/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/index.d.ts +1 -0
- package/dist/declarations/src/modules/evm-uniswapv2/module.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/evm-uniswapv2/utils.d.ts +12 -0
- package/dist/declarations/src/modules/index.d.ts +2 -1
- package/dist/declarations/src/modules/shared/errors.d.ts +11 -0
- package/dist/declarations/src/modules/shared/fetchRuntimeCallResult.d.ts +2 -0
- package/dist/declarations/src/modules/shared/getContantValue.d.ts +1 -0
- package/dist/declarations/src/modules/shared/hasConstantValue.d.ts +427 -0
- package/dist/declarations/src/modules/shared/index.d.ts +7 -0
- package/dist/declarations/src/modules/shared/tryGetConstantValue.d.ts +1 -0
- package/dist/declarations/src/modules/shared/types.d.ts +10 -0
- package/dist/declarations/src/modules/shared/utils.d.ts +4 -0
- package/dist/declarations/src/modules/substrate-assets/config.d.ts +16 -0
- package/dist/declarations/src/modules/substrate-assets/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-assets/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-assets/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-assets/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-assets/index.d.ts +1 -0
- package/dist/declarations/src/modules/substrate-assets/module.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-assets/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-foreignassets/config.d.ts +16 -0
- package/dist/declarations/src/modules/substrate-foreignassets/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-foreignassets/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-foreignassets/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-foreignassets/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-foreignassets/index.d.ts +1 -0
- package/dist/declarations/src/modules/substrate-foreignassets/module.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-foreignassets/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-hydration/config.d.ts +2 -0
- package/dist/declarations/src/modules/substrate-hydration/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-hydration/fetchTokens.d.ts +4 -0
- package/dist/declarations/src/modules/substrate-hydration/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-hydration/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-hydration/index.d.ts +2 -0
- package/dist/declarations/src/modules/substrate-hydration/module.d.ts +4 -0
- package/dist/declarations/src/modules/substrate-hydration/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-hydration/types.d.ts +14 -0
- package/dist/declarations/src/modules/substrate-native/bittensor/getSubtensorStakingBalances.d.ts +9 -0
- package/dist/declarations/src/modules/substrate-native/bittensor/subtensor.d.ts +20 -0
- package/dist/declarations/src/modules/substrate-native/config.d.ts +25 -0
- package/dist/declarations/src/modules/substrate-native/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-native/fetchTokens.d.ts +8 -0
- package/dist/declarations/src/modules/substrate-native/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-native/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-native/index.d.ts +1 -0
- package/dist/declarations/src/modules/substrate-native/module.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-native/queries/buildBaseQueries.d.ts +20 -0
- package/dist/declarations/src/modules/substrate-native/queries/buildNomPoolQueries.d.ts +8 -0
- package/dist/declarations/src/modules/substrate-native/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-native/util/lockTypes.d.ts +15 -0
- package/dist/declarations/src/modules/substrate-native/util/nompoolAccountId.d.ts +5 -0
- package/dist/declarations/src/modules/substrate-psp22/config.d.ts +16 -0
- package/dist/declarations/src/modules/substrate-psp22/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-psp22/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-psp22/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-psp22/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-psp22/index.d.ts +1 -0
- package/dist/declarations/src/modules/substrate-psp22/module.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-psp22/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-tokens/config.d.ts +23 -0
- package/dist/declarations/src/modules/substrate-tokens/fetchBalances.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-tokens/fetchTokens.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-tokens/getMiniMetadata.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-tokens/getTransferCallData.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-tokens/index.d.ts +1 -0
- package/dist/declarations/src/modules/substrate-tokens/module.d.ts +3 -0
- package/dist/declarations/src/modules/substrate-tokens/subscribeBalances.d.ts +3 -0
- package/dist/declarations/src/modules/util/RpcStateQueriesHelper.d.ts +12 -0
- package/dist/declarations/src/modules/util/buildStorageCoders.d.ts +2 -2
- package/dist/declarations/src/types/balances.d.ts +2 -1
- package/dist/declarations/src/types/minimetadatas.d.ts +1 -1
- package/dist/declarations/src/version.d.ts +1 -0
- package/dist/talismn-balances.cjs.dev.js +3901 -331
- package/dist/talismn-balances.cjs.prod.js +3901 -331
- package/dist/talismn-balances.esm.js +3887 -323
- package/package.json +8 -8
- package/dist/declarations/src/libVersion.d.ts +0 -1
- package/dist/declarations/src/modules/abis/erc20.d.ts +0 -227
@@ -1,27 +1,29 @@
|
|
1
1
|
import { Dexie } from 'dexie';
|
2
2
|
import anylogger from 'anylogger';
|
3
|
-
import { evmErc20TokenId, TokenBaseSchema, EvmErc20TokenSchema, networkIdFromTokenId, EvmUniswapV2TokenSchema, evmUniswapV2TokenId, getGithubTokenLogoUrl, DotNetworkBalancesConfigSchema, SubAssetsTokenSchema, parseSubAssetTokenId, subAssetTokenId, SubForeignAssetsTokenSchema, parseSubForeignAssetTokenId, subForeignAssetTokenId, parseTokenId, subNativeTokenId, SubPsp22TokenSchema, subPsp22TokenId, SubTokensTokenSchema, parseSubTokensTokenId, subTokensTokenId } from '@talismn/chaindata-provider';
|
3
|
+
import { evmErc20TokenId, MINIMETADATA_VERSION, TokenBaseSchema, EvmErc20TokenSchema, networkIdFromTokenId, EvmUniswapV2TokenSchema, evmUniswapV2TokenId, getGithubTokenLogoUrl, DotNetworkBalancesConfigSchema, SubAssetsTokenSchema, parseSubAssetTokenId, subAssetTokenId, SubForeignAssetsTokenSchema, parseSubForeignAssetTokenId, subForeignAssetTokenId, parseTokenId, subNativeTokenId, SubPsp22TokenSchema, subPsp22TokenId, SubTokensTokenSchema, parseSubTokensTokenId, subTokensTokenId, parseEvmErc20TokenId, isTokenOfType, EvmNativeTokenSchema, evmNativeTokenId, SubHydrationTokenSchema, subHydrationTokenId, SubNativeTokenSchema } from '@talismn/chaindata-provider';
|
4
|
+
export { MINIMETADATA_VERSION } from '@talismn/chaindata-provider';
|
4
5
|
import { newTokenRates } from '@talismn/token-rates';
|
5
6
|
import { isBigInt, BigMath, planckToTokens, isArrayOf, isTruthy, isEthereumAddress, hasOwnProperty, isAbortError, isNotNil, decodeAnyAddress, blake2Concat, Deferred } from '@talismn/util';
|
6
7
|
import BigNumber from 'bignumber.js';
|
7
8
|
import { u8aToHex, assert, stringCamelCase, u8aConcatStrict, arrayChunk, u8aToString, hexToNumber, hexToU8a } from '@polkadot/util';
|
8
9
|
import { xxhashAsU8a } from '@polkadot/util-crypto';
|
9
10
|
import pako from 'pako';
|
11
|
+
import { parseAbi, erc20Abi, isHex, hexToBigInt, getContract, ContractFunctionExecutionError, hexToString, erc20Abi_bytes32, encodeFunctionData, withRetry } from 'viem';
|
12
|
+
export { erc20Abi } from 'viem';
|
10
13
|
import z from 'zod/v4';
|
11
|
-
import {
|
12
|
-
import { fromPairs, toPairs, keys, groupBy as groupBy$1 } from 'lodash';
|
14
|
+
import { fromPairs, toPairs, keys, groupBy as groupBy$1, assign, isEqual as isEqual$1, keyBy, uniq, values } from 'lodash';
|
13
15
|
import isEqual from 'lodash/isEqual';
|
14
16
|
import { defineMethod } from '@substrate/txwrapper-core';
|
15
|
-
import { unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, compactMetadata, encodeMetadata, decodeScale, papiParse, getMetadataVersion, encodeStateKey } from '@talismn/scale';
|
17
|
+
import { unifyMetadata, decAnyMetadata, getDynamicBuilder, getLookupFn, compactMetadata, encodeMetadata, decodeScale, papiParse, getMetadataVersion, encodeStateKey, parseMetadataRpc, getStorageKeyPrefix, papiStringify, toHex as toHex$1 } from '@talismn/scale';
|
16
18
|
import camelCase from 'lodash/camelCase';
|
17
19
|
import PQueue from 'p-queue';
|
18
20
|
import { fetchBestMetadata, getScaleApi } from '@talismn/sapi';
|
19
21
|
import { Metadata, TypeRegistry } from '@polkadot/types';
|
20
22
|
import groupBy from 'lodash/groupBy';
|
21
23
|
import { mergeUint8, toHex } from '@polkadot-api/utils';
|
22
|
-
import { Binary, AccountId } from 'polkadot-api';
|
24
|
+
import { Binary, AccountId, Enum } from 'polkadot-api';
|
23
25
|
import { ChainConnectionError } from '@talismn/chain-connector';
|
24
|
-
import { Observable, scan, share, map, switchAll, combineLatest, from, mergeMap, toArray, interval, startWith, exhaustMap, BehaviorSubject, debounceTime, takeUntil, distinctUntilChanged, switchMap, withLatestFrom, concatMap } from 'rxjs';
|
26
|
+
import { Observable, scan, share, map, switchAll, combineLatest, from, mergeMap, toArray, interval, startWith, exhaustMap, BehaviorSubject, debounceTime, takeUntil, distinctUntilChanged, switchMap, withLatestFrom, concatMap, of, timer, firstValueFrom } from 'rxjs';
|
25
27
|
import { u32, Struct, u128 } from 'scale-ts';
|
26
28
|
import upperFirst from 'lodash/upperFirst';
|
27
29
|
import { Abi } from '@polkadot/api-contract';
|
@@ -59,11 +61,10 @@ const DefaultBalanceModule = type => ({
|
|
59
61
|
// internal
|
60
62
|
//
|
61
63
|
|
62
|
-
var
|
63
|
-
name: "@talismn/balances"
|
64
|
-
version: "0.0.0-pr2075-20250703111149"};
|
64
|
+
var packageJson = {
|
65
|
+
name: "@talismn/balances"};
|
65
66
|
|
66
|
-
var log = anylogger(
|
67
|
+
var log = anylogger(packageJson.name);
|
67
68
|
|
68
69
|
function excludeFromTransferableAmount(locks) {
|
69
70
|
if (typeof locks === "string") return BigInt(locks);
|
@@ -847,9 +848,8 @@ const getValueId = amount => {
|
|
847
848
|
const deriveMiniMetadataId = ({
|
848
849
|
source,
|
849
850
|
chainId,
|
850
|
-
specVersion
|
851
|
-
|
852
|
-
}) => u8aToHex(xxhashAsU8a(new TextEncoder().encode(`${source}${chainId}${specVersion}${libVersion}`), 64), undefined, false);
|
851
|
+
specVersion
|
852
|
+
}) => u8aToHex(xxhashAsU8a(new TextEncoder().encode(`${source}${chainId}${specVersion}${MINIMETADATA_VERSION}`), 64), undefined, false);
|
853
853
|
|
854
854
|
// for DB version 3, Wallet version 1.21.0
|
855
855
|
const upgradeRemoveSymbolFromNativeTokenId = async tx => {
|
@@ -948,234 +948,6 @@ const TokenConfigBaseSchema = TokenBaseSchema.partial().omit({
|
|
948
948
|
id: true
|
949
949
|
});
|
950
950
|
|
951
|
-
const erc20Abi = [{
|
952
|
-
constant: true,
|
953
|
-
inputs: [],
|
954
|
-
name: "name",
|
955
|
-
outputs: [{
|
956
|
-
name: "",
|
957
|
-
type: "string"
|
958
|
-
}],
|
959
|
-
payable: false,
|
960
|
-
stateMutability: "view",
|
961
|
-
type: "function"
|
962
|
-
}, {
|
963
|
-
constant: false,
|
964
|
-
inputs: [{
|
965
|
-
name: "_spender",
|
966
|
-
type: "address"
|
967
|
-
}, {
|
968
|
-
name: "_value",
|
969
|
-
type: "uint256"
|
970
|
-
}],
|
971
|
-
name: "approve",
|
972
|
-
outputs: [{
|
973
|
-
name: "",
|
974
|
-
type: "bool"
|
975
|
-
}],
|
976
|
-
payable: false,
|
977
|
-
stateMutability: "nonpayable",
|
978
|
-
type: "function"
|
979
|
-
}, {
|
980
|
-
constant: true,
|
981
|
-
inputs: [],
|
982
|
-
name: "totalSupply",
|
983
|
-
outputs: [{
|
984
|
-
name: "",
|
985
|
-
type: "uint256"
|
986
|
-
}],
|
987
|
-
payable: false,
|
988
|
-
stateMutability: "view",
|
989
|
-
type: "function"
|
990
|
-
}, {
|
991
|
-
constant: false,
|
992
|
-
inputs: [{
|
993
|
-
name: "_from",
|
994
|
-
type: "address"
|
995
|
-
}, {
|
996
|
-
name: "_to",
|
997
|
-
type: "address"
|
998
|
-
}, {
|
999
|
-
name: "_value",
|
1000
|
-
type: "uint256"
|
1001
|
-
}],
|
1002
|
-
name: "transferFrom",
|
1003
|
-
outputs: [{
|
1004
|
-
name: "",
|
1005
|
-
type: "bool"
|
1006
|
-
}],
|
1007
|
-
payable: false,
|
1008
|
-
stateMutability: "nonpayable",
|
1009
|
-
type: "function"
|
1010
|
-
}, {
|
1011
|
-
constant: true,
|
1012
|
-
inputs: [],
|
1013
|
-
name: "decimals",
|
1014
|
-
outputs: [{
|
1015
|
-
name: "",
|
1016
|
-
type: "uint8"
|
1017
|
-
}],
|
1018
|
-
payable: false,
|
1019
|
-
stateMutability: "view",
|
1020
|
-
type: "function"
|
1021
|
-
}, {
|
1022
|
-
constant: false,
|
1023
|
-
inputs: [{
|
1024
|
-
name: "_to",
|
1025
|
-
type: "address"
|
1026
|
-
}, {
|
1027
|
-
name: "_value",
|
1028
|
-
type: "uint256"
|
1029
|
-
}, {
|
1030
|
-
name: "_data",
|
1031
|
-
type: "bytes"
|
1032
|
-
}],
|
1033
|
-
name: "transferAndCall",
|
1034
|
-
outputs: [{
|
1035
|
-
name: "success",
|
1036
|
-
type: "bool"
|
1037
|
-
}],
|
1038
|
-
payable: false,
|
1039
|
-
stateMutability: "nonpayable",
|
1040
|
-
type: "function"
|
1041
|
-
}, {
|
1042
|
-
constant: false,
|
1043
|
-
inputs: [{
|
1044
|
-
name: "_spender",
|
1045
|
-
type: "address"
|
1046
|
-
}, {
|
1047
|
-
name: "_subtractedValue",
|
1048
|
-
type: "uint256"
|
1049
|
-
}],
|
1050
|
-
name: "decreaseApproval",
|
1051
|
-
outputs: [{
|
1052
|
-
name: "success",
|
1053
|
-
type: "bool"
|
1054
|
-
}],
|
1055
|
-
payable: false,
|
1056
|
-
stateMutability: "nonpayable",
|
1057
|
-
type: "function"
|
1058
|
-
}, {
|
1059
|
-
constant: true,
|
1060
|
-
inputs: [{
|
1061
|
-
name: "_owner",
|
1062
|
-
type: "address"
|
1063
|
-
}],
|
1064
|
-
name: "balanceOf",
|
1065
|
-
outputs: [{
|
1066
|
-
name: "balance",
|
1067
|
-
type: "uint256"
|
1068
|
-
}],
|
1069
|
-
payable: false,
|
1070
|
-
stateMutability: "view",
|
1071
|
-
type: "function"
|
1072
|
-
}, {
|
1073
|
-
constant: true,
|
1074
|
-
inputs: [],
|
1075
|
-
name: "symbol",
|
1076
|
-
outputs: [{
|
1077
|
-
name: "",
|
1078
|
-
type: "string"
|
1079
|
-
}],
|
1080
|
-
payable: false,
|
1081
|
-
stateMutability: "view",
|
1082
|
-
type: "function"
|
1083
|
-
}, {
|
1084
|
-
constant: false,
|
1085
|
-
inputs: [{
|
1086
|
-
name: "_to",
|
1087
|
-
type: "address"
|
1088
|
-
}, {
|
1089
|
-
name: "_value",
|
1090
|
-
type: "uint256"
|
1091
|
-
}],
|
1092
|
-
name: "transfer",
|
1093
|
-
outputs: [{
|
1094
|
-
name: "success",
|
1095
|
-
type: "bool"
|
1096
|
-
}],
|
1097
|
-
payable: false,
|
1098
|
-
stateMutability: "nonpayable",
|
1099
|
-
type: "function"
|
1100
|
-
}, {
|
1101
|
-
constant: false,
|
1102
|
-
inputs: [{
|
1103
|
-
name: "_spender",
|
1104
|
-
type: "address"
|
1105
|
-
}, {
|
1106
|
-
name: "_addedValue",
|
1107
|
-
type: "uint256"
|
1108
|
-
}],
|
1109
|
-
name: "increaseApproval",
|
1110
|
-
outputs: [{
|
1111
|
-
name: "success",
|
1112
|
-
type: "bool"
|
1113
|
-
}],
|
1114
|
-
payable: false,
|
1115
|
-
stateMutability: "nonpayable",
|
1116
|
-
type: "function"
|
1117
|
-
}, {
|
1118
|
-
constant: true,
|
1119
|
-
inputs: [{
|
1120
|
-
name: "_owner",
|
1121
|
-
type: "address"
|
1122
|
-
}, {
|
1123
|
-
name: "_spender",
|
1124
|
-
type: "address"
|
1125
|
-
}],
|
1126
|
-
name: "allowance",
|
1127
|
-
outputs: [{
|
1128
|
-
name: "remaining",
|
1129
|
-
type: "uint256"
|
1130
|
-
}],
|
1131
|
-
payable: false,
|
1132
|
-
stateMutability: "view",
|
1133
|
-
type: "function"
|
1134
|
-
}, {
|
1135
|
-
inputs: [],
|
1136
|
-
payable: false,
|
1137
|
-
stateMutability: "nonpayable",
|
1138
|
-
type: "constructor"
|
1139
|
-
}, {
|
1140
|
-
anonymous: false,
|
1141
|
-
inputs: [{
|
1142
|
-
indexed: true,
|
1143
|
-
name: "from",
|
1144
|
-
type: "address"
|
1145
|
-
}, {
|
1146
|
-
indexed: true,
|
1147
|
-
name: "to",
|
1148
|
-
type: "address"
|
1149
|
-
}, {
|
1150
|
-
indexed: false,
|
1151
|
-
name: "value",
|
1152
|
-
type: "uint256"
|
1153
|
-
}, {
|
1154
|
-
indexed: false,
|
1155
|
-
name: "data",
|
1156
|
-
type: "bytes"
|
1157
|
-
}],
|
1158
|
-
name: "Transfer",
|
1159
|
-
type: "event"
|
1160
|
-
}, {
|
1161
|
-
anonymous: false,
|
1162
|
-
inputs: [{
|
1163
|
-
indexed: true,
|
1164
|
-
name: "owner",
|
1165
|
-
type: "address"
|
1166
|
-
}, {
|
1167
|
-
indexed: true,
|
1168
|
-
name: "spender",
|
1169
|
-
type: "address"
|
1170
|
-
}, {
|
1171
|
-
indexed: false,
|
1172
|
-
name: "value",
|
1173
|
-
type: "uint256"
|
1174
|
-
}],
|
1175
|
-
name: "Approval",
|
1176
|
-
type: "event"
|
1177
|
-
}];
|
1178
|
-
|
1179
951
|
const erc20BalancesAggregatorAbi = parseAbi(["struct AccountToken {address account; address token;}", "function balances(AccountToken[] memory accountTokens) public view returns (uint256[] memory)"]);
|
1180
952
|
|
1181
953
|
const moduleType$7 = "evm-erc20";
|
@@ -1331,7 +1103,7 @@ const EvmErc20Module = hydrate => {
|
|
1331
1103
|
const {
|
1332
1104
|
errors,
|
1333
1105
|
results
|
1334
|
-
} = await fetchBalances$
|
1106
|
+
} = await fetchBalances$c(chainConnectors.evm, {
|
1335
1107
|
[evmNetworkId]: fetchesPerNetwork[evmNetworkId]
|
1336
1108
|
}, aggregators);
|
1337
1109
|
|
@@ -1389,7 +1161,7 @@ const EvmErc20Module = hydrate => {
|
|
1389
1161
|
if (!chainConnectors.evm) throw new Error(`This module requires an evm chain connector`);
|
1390
1162
|
const [aggregators, tokens] = await Promise.all([getErc20Aggregators(), getModuleTokens()]);
|
1391
1163
|
const fetchesPerNetwork = await prepareFetchParameters(addressesByToken, tokens);
|
1392
|
-
const balances = await fetchBalances$
|
1164
|
+
const balances = await fetchBalances$c(chainConnectors.evm, fetchesPerNetwork, aggregators);
|
1393
1165
|
return new Balances(balances.results);
|
1394
1166
|
}
|
1395
1167
|
};
|
@@ -1411,7 +1183,7 @@ class EvmErc20NetworkError extends Error {
|
|
1411
1183
|
this.evmNetworkId = evmNetworkId;
|
1412
1184
|
}
|
1413
1185
|
}
|
1414
|
-
const fetchBalances$
|
1186
|
+
const fetchBalances$c = async (evmChainConnector, tokenAddressesByNetwork, erc20Aggregators = {}) => {
|
1415
1187
|
const result = {
|
1416
1188
|
results: [],
|
1417
1189
|
errors: []
|
@@ -1586,7 +1358,7 @@ const EvmNativeModule = hydrate => {
|
|
1586
1358
|
if (!tokenId) throw new Error(`No native token for evm network ${evmNetworkId}`);
|
1587
1359
|
try {
|
1588
1360
|
if (!chainConnectors.evm) throw new Error(`This module requires an evm chain connector`);
|
1589
|
-
const balances = await fetchBalances$
|
1361
|
+
const balances = await fetchBalances$b(chainConnectors.evm, {
|
1590
1362
|
[tokenId]: addresses
|
1591
1363
|
}, tokens);
|
1592
1364
|
const resultBalances = [];
|
@@ -1628,7 +1400,7 @@ const EvmNativeModule = hydrate => {
|
|
1628
1400
|
async fetchBalances(addressesByToken) {
|
1629
1401
|
if (!chainConnectors.evm) throw new Error(`This module requires an evm chain connector`);
|
1630
1402
|
const tokens = await getModuleTokens();
|
1631
|
-
const balanceResults = await fetchBalances$
|
1403
|
+
const balanceResults = await fetchBalances$b(chainConnectors.evm, addressesByToken, tokens);
|
1632
1404
|
const pureBalances = balanceResults.flat().filter(b => !(b instanceof EvmNativeBalanceError) && BigInt(b.value) > 0n);
|
1633
1405
|
return new Balances(pureBalances);
|
1634
1406
|
}
|
@@ -1644,7 +1416,7 @@ class EvmNativeBalanceError extends Error {
|
|
1644
1416
|
}
|
1645
1417
|
}
|
1646
1418
|
}
|
1647
|
-
const fetchBalances$
|
1419
|
+
const fetchBalances$b = async (evmChainConnector, addressesByToken, tokens) => {
|
1648
1420
|
if (!evmChainConnector) throw new Error(`This module requires an evm chain connector`);
|
1649
1421
|
return Promise.all(Object.entries(addressesByToken).map(async ([tokenId, addresses]) => {
|
1650
1422
|
const token = tokens[tokenId];
|
@@ -2396,7 +2168,7 @@ const EvmUniswapV2Module = hydrate => {
|
|
2396
2168
|
}
|
2397
2169
|
try {
|
2398
2170
|
if (!chainConnectors.evm) throw new Error(`This module requires an evm chain connector`);
|
2399
|
-
const balances = await fetchBalances$
|
2171
|
+
const balances = await fetchBalances$a(chainConnectors.evm, evmNetworks, tokens, addressesByToken);
|
2400
2172
|
|
2401
2173
|
// Don't call callback with balances which have not changed since the last poll.
|
2402
2174
|
const json = balances.toJSON();
|
@@ -2425,11 +2197,11 @@ const EvmUniswapV2Module = hydrate => {
|
|
2425
2197
|
if (!chainConnectors.evm) throw new Error(`This module requires an evm chain connector`);
|
2426
2198
|
const evmNetworks = await chaindataProvider.getNetworksMapById("ethereum");
|
2427
2199
|
const tokens = await chaindataProvider.getTokensMapById();
|
2428
|
-
return fetchBalances$
|
2200
|
+
return fetchBalances$a(chainConnectors.evm, evmNetworks, tokens, addressesByToken);
|
2429
2201
|
}
|
2430
2202
|
};
|
2431
2203
|
};
|
2432
|
-
const fetchBalances$
|
2204
|
+
const fetchBalances$a = async (evmChainConnector, evmNetworks, tokens, addressesByToken) => {
|
2433
2205
|
const addressesByTokenGroupedByEvmNetwork = groupAddressesByTokenByEvmNetwork(addressesByToken, tokens);
|
2434
2206
|
const balances = (await Promise.allSettled(Object.entries(addressesByTokenGroupedByEvmNetwork).map(async ([networkId, addressesByToken]) => {
|
2435
2207
|
if (!evmChainConnector) throw new Error(`This module requires an evm chain connector`);
|
@@ -2569,8 +2341,6 @@ async function getPoolBalance(publicClient, contractAddress, accountAddress) {
|
|
2569
2341
|
}
|
2570
2342
|
}
|
2571
2343
|
|
2572
|
-
const libVersion = pkg.version;
|
2573
|
-
|
2574
2344
|
// cache the promise so it can be shared across multiple calls
|
2575
2345
|
const CACHE_GET_SPEC_VERSION = new Map();
|
2576
2346
|
const fetchSpecVersion = async (chainConnector, networkId) => {
|
@@ -2671,13 +2441,12 @@ const fetchMiniMetadatas = async (chainConnector, chaindataProvider, chainId, sp
|
|
2671
2441
|
id: deriveMiniMetadataId({
|
2672
2442
|
source,
|
2673
2443
|
chainId,
|
2674
|
-
specVersion
|
2675
|
-
libVersion
|
2444
|
+
specVersion
|
2676
2445
|
}),
|
2677
2446
|
source,
|
2678
2447
|
chainId,
|
2679
2448
|
specVersion,
|
2680
|
-
|
2449
|
+
version: MINIMETADATA_VERSION,
|
2681
2450
|
data: chainMeta?.miniMetadata ?? null,
|
2682
2451
|
extra: chainMeta?.extra ?? null
|
2683
2452
|
};
|
@@ -2699,14 +2468,13 @@ const getUpdatedMiniMetadatas = async (chainConnector, chaindataProvider, chainI
|
|
2699
2468
|
return miniMetadatas;
|
2700
2469
|
};
|
2701
2470
|
|
2702
|
-
const getMiniMetadata = async (chaindataProvider, chainConnector, chainId, source, signal) => {
|
2471
|
+
const getMiniMetadata$9 = async (chaindataProvider, chainConnector, chainId, source, signal) => {
|
2703
2472
|
const specVersion = await getSpecVersion(chainConnector, chainId);
|
2704
2473
|
signal?.throwIfAborted();
|
2705
2474
|
const miniMetadataId = deriveMiniMetadataId({
|
2706
2475
|
source,
|
2707
2476
|
chainId,
|
2708
|
-
specVersion
|
2709
|
-
libVersion
|
2477
|
+
specVersion
|
2710
2478
|
});
|
2711
2479
|
|
2712
2480
|
// lookup local ones
|
@@ -2724,7 +2492,7 @@ const getMiniMetadata = async (chaindataProvider, chainConnector, chainId, sourc
|
|
2724
2492
|
source,
|
2725
2493
|
chainId,
|
2726
2494
|
specVersion,
|
2727
|
-
|
2495
|
+
version: MINIMETADATA_VERSION,
|
2728
2496
|
miniMetadataId,
|
2729
2497
|
miniMetadatas
|
2730
2498
|
});
|
@@ -2780,6 +2548,8 @@ const buildStorageCoders = ({
|
|
2780
2548
|
return [];
|
2781
2549
|
}
|
2782
2550
|
}));
|
2551
|
+
// type StorageCoder<TCoders extends NetworkCoders> = ReturnType<ReturnType<typeof getDynamicBuilder>["buildStorage"]>[keyof TCoders]
|
2552
|
+
|
2783
2553
|
const buildNetworkStorageCoders = (chainId, miniMetadata, coders) => {
|
2784
2554
|
if (!miniMetadata.data) return null;
|
2785
2555
|
const metadata = unifyMetadata(decAnyMetadata(miniMetadata.data));
|
@@ -3038,8 +2808,8 @@ const SubAssetsModule = hydrate => {
|
|
3038
2808
|
for (const tokenConfig of tokens ?? []) {
|
3039
2809
|
try {
|
3040
2810
|
const assetId = String(tokenConfig.assetId);
|
3041
|
-
const assetStateKey = tryEncode(assetCoder, BigInt(assetId)) ?? tryEncode(assetCoder, assetId);
|
3042
|
-
const metadataStateKey = tryEncode(metadataCoder, BigInt(assetId)) ?? tryEncode(metadataCoder, assetId);
|
2811
|
+
const assetStateKey = tryEncode$1(assetCoder, BigInt(assetId)) ?? tryEncode$1(assetCoder, assetId);
|
2812
|
+
const metadataStateKey = tryEncode$1(metadataCoder, BigInt(assetId)) ?? tryEncode$1(metadataCoder, assetId);
|
3043
2813
|
if (assetStateKey === null || metadataStateKey === null) throw new Error(`Failed to encode stateKey for asset ${assetId} on chain ${chainId}`);
|
3044
2814
|
const [assetsAsset, assetsMetadata] = await Promise.all([chainConnector.send(chainId, "state_getStorage", [assetStateKey]).then(result => assetCoder.value.dec(result) ?? null), chainConnector.send(chainId, "state_getStorage", [metadataStateKey]).then(result => metadataCoder.value.dec(result) ?? null)]);
|
3045
2815
|
const existentialDeposit = assetsAsset?.min_balance?.toString?.() ?? "0";
|
@@ -3059,6 +2829,7 @@ const SubAssetsModule = hydrate => {
|
|
3059
2829
|
logo: tokenConfig?.logo,
|
3060
2830
|
existentialDeposit,
|
3061
2831
|
assetId,
|
2832
|
+
isSufficient: false,
|
3062
2833
|
isFrozen,
|
3063
2834
|
networkId: chainId
|
3064
2835
|
};
|
@@ -3111,7 +2882,7 @@ const SubAssetsModule = hydrate => {
|
|
3111
2882
|
},
|
3112
2883
|
async fetchBalances(addressesByToken) {
|
3113
2884
|
assert(chainConnectors.substrate, "This module requires a substrate chain connector");
|
3114
|
-
const queries = await buildQueries$
|
2885
|
+
const queries = await buildQueries$6(chainConnector, chaindataProvider, addressesByToken);
|
3115
2886
|
const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
|
3116
2887
|
const balances = result?.filter(b => b !== null) ?? [];
|
3117
2888
|
return new Balances(balances);
|
@@ -3181,7 +2952,7 @@ const SubAssetsModule = hydrate => {
|
|
3181
2952
|
};
|
3182
2953
|
};
|
3183
2954
|
async function buildNetworkQueries$2(networkId, chainConnector, chaindataProvider, addressesByToken, signal) {
|
3184
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType$4, signal);
|
2955
|
+
const miniMetadata = await getMiniMetadata$9(chaindataProvider, chainConnector, networkId, moduleType$4, signal);
|
3185
2956
|
const chain = await chaindataProvider.getNetworkById(networkId, "polkadot");
|
3186
2957
|
const tokensById = await chaindataProvider.getTokensMapById();
|
3187
2958
|
signal?.throwIfAborted();
|
@@ -3205,7 +2976,7 @@ async function buildNetworkQueries$2(networkId, chainConnector, chaindataProvide
|
|
3205
2976
|
}
|
3206
2977
|
return addresses.flatMap(address => {
|
3207
2978
|
const scaleCoder = networkStorageCoders?.storage;
|
3208
|
-
const stateKey = tryEncode(scaleCoder, Number(token.assetId), address) ?? tryEncode(scaleCoder, BigInt(token.assetId), address);
|
2979
|
+
const stateKey = tryEncode$1(scaleCoder, Number(token.assetId), address) ?? tryEncode$1(scaleCoder, BigInt(token.assetId), address);
|
3209
2980
|
if (!stateKey) {
|
3210
2981
|
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
3211
2982
|
return [];
|
@@ -3260,7 +3031,7 @@ async function buildNetworkQueries$2(networkId, chainConnector, chaindataProvide
|
|
3260
3031
|
});
|
3261
3032
|
});
|
3262
3033
|
}
|
3263
|
-
async function buildQueries$
|
3034
|
+
async function buildQueries$6(chainConnector, chaindataProvider, addressesByToken, signal) {
|
3264
3035
|
const byNetwork = keys(addressesByToken).reduce((acc, tokenId) => {
|
3265
3036
|
const networkId = parseSubAssetTokenId(tokenId).networkId;
|
3266
3037
|
if (!acc[networkId]) acc[networkId] = {};
|
@@ -3275,7 +3046,7 @@ async function buildQueries$3(chainConnector, chaindataProvider, addressesByToke
|
|
3275
3046
|
// E.g. Polkadot Asset Hub needs it to be a string, Astar needs it to be a bigint
|
3276
3047
|
//
|
3277
3048
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
3278
|
-
const tryEncode = (scaleCoder, ...args) => {
|
3049
|
+
const tryEncode$1 = (scaleCoder, ...args) => {
|
3279
3050
|
try {
|
3280
3051
|
return scaleCoder?.keys?.enc?.(...args);
|
3281
3052
|
} catch {
|
@@ -3358,6 +3129,7 @@ const SubForeignAssetsModule = hydrate => {
|
|
3358
3129
|
logo: tokenConfig?.logo,
|
3359
3130
|
existentialDeposit,
|
3360
3131
|
onChainId: tokenConfig.onChainId,
|
3132
|
+
isSufficient: assetsAsset?.is_sufficient ?? false,
|
3361
3133
|
isFrozen,
|
3362
3134
|
networkId: chainId
|
3363
3135
|
};
|
@@ -3406,7 +3178,7 @@ const SubForeignAssetsModule = hydrate => {
|
|
3406
3178
|
},
|
3407
3179
|
async fetchBalances(addressesByToken) {
|
3408
3180
|
assert(chainConnectors.substrate, "This module requires a substrate chain connector");
|
3409
|
-
const queries = await buildQueries$
|
3181
|
+
const queries = await buildQueries$5(chainConnector, chaindataProvider, addressesByToken);
|
3410
3182
|
const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
|
3411
3183
|
const balances = result?.filter(b => b !== null) ?? [];
|
3412
3184
|
return new Balances(balances);
|
@@ -3462,7 +3234,7 @@ const SubForeignAssetsModule = hydrate => {
|
|
3462
3234
|
};
|
3463
3235
|
};
|
3464
3236
|
async function buildNetworkQueries$1(networkId, chainConnector, chaindataProvider, addressesByToken, signal) {
|
3465
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType$3, signal);
|
3237
|
+
const miniMetadata = await getMiniMetadata$9(chaindataProvider, chainConnector, networkId, moduleType$3, signal);
|
3466
3238
|
const chain = await chaindataProvider.getNetworkById(networkId, "polkadot");
|
3467
3239
|
const tokensById = await chaindataProvider.getTokensMapById();
|
3468
3240
|
signal?.throwIfAborted();
|
@@ -3544,7 +3316,7 @@ async function buildNetworkQueries$1(networkId, chainConnector, chaindataProvide
|
|
3544
3316
|
});
|
3545
3317
|
});
|
3546
3318
|
}
|
3547
|
-
async function buildQueries$
|
3319
|
+
async function buildQueries$5(chainConnector, chaindataProvider, addressesByToken, signal) {
|
3548
3320
|
const byNetwork = keys(addressesByToken).reduce((acc, tokenId) => {
|
3549
3321
|
const networkId = parseSubForeignAssetTokenId(tokenId).networkId;
|
3550
3322
|
if (!acc[networkId]) acc[networkId] = {};
|
@@ -3598,7 +3370,7 @@ const asObservable = handler => (...args) => new Observable(subscriber => {
|
|
3598
3370
|
* Each nominationPool in the nominationPools pallet has access to some accountIds which have no
|
3599
3371
|
* associated private key. Instead, they are derived from this function.
|
3600
3372
|
*/
|
3601
|
-
const nompoolAccountId = (palletId, poolId, index) => {
|
3373
|
+
const nompoolAccountId$1 = (palletId, poolId, index) => {
|
3602
3374
|
const utf8Encoder = new TextEncoder();
|
3603
3375
|
const encModPrefix = utf8Encoder.encode("modl");
|
3604
3376
|
const encPalletId = utf8Encoder.encode(palletId);
|
@@ -3611,7 +3383,7 @@ const nompoolAccountId = (palletId, poolId, index) => {
|
|
3611
3383
|
return AccountId().dec(bytes);
|
3612
3384
|
};
|
3613
3385
|
/** The stash account for the nomination pool */
|
3614
|
-
const nompoolStashAccountId = (palletId, poolId) => nompoolAccountId(palletId, poolId, 0);
|
3386
|
+
const nompoolStashAccountId$1 = (palletId, poolId) => nompoolAccountId$1(palletId, poolId, 0);
|
3615
3387
|
|
3616
3388
|
// TODO make this method chain-specific
|
3617
3389
|
async function subscribeNompoolStaking(chaindataProvider, chainConnector, addressesByToken, callback, signal) {
|
@@ -3623,7 +3395,7 @@ async function subscribeNompoolStaking(chaindataProvider, chainConnector, addres
|
|
3623
3395
|
const networkIds = keys(addressesByToken).map(tokenId => parseTokenId(tokenId).networkId);
|
3624
3396
|
const miniMetadatas = new Map();
|
3625
3397
|
for (const networkId of networkIds) {
|
3626
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, "substrate-native");
|
3398
|
+
const miniMetadata = await getMiniMetadata$9(chaindataProvider, chainConnector, networkId, "substrate-native");
|
3627
3399
|
miniMetadatas.set(networkId, miniMetadata);
|
3628
3400
|
}
|
3629
3401
|
signal?.throwIfAborted();
|
@@ -3749,7 +3521,7 @@ async function subscribeNompoolStaking(chaindataProvider, chainConnector, addres
|
|
3749
3521
|
const scaleCoder = chainStorageCoders.get(chainId)?.ledger;
|
3750
3522
|
const queries = poolIds.flatMap(poolId => {
|
3751
3523
|
if (!nominationPoolsPalletId) return [];
|
3752
|
-
const stashAddress = nompoolStashAccountId(nominationPoolsPalletId, poolId);
|
3524
|
+
const stashAddress = nompoolStashAccountId$1(nominationPoolsPalletId, poolId);
|
3753
3525
|
const stateKey = encodeStateKey(scaleCoder, `Invalid address in ${chainId} ledger query ${stashAddress}`, stashAddress);
|
3754
3526
|
if (!stateKey) return [];
|
3755
3527
|
const decodeResult = change => {
|
@@ -3902,12 +3674,12 @@ async function subscribeNompoolStaking(chaindataProvider, chainConnector, addres
|
|
3902
3674
|
}
|
3903
3675
|
}
|
3904
3676
|
|
3905
|
-
const SUBTENSOR_ROOT_NETUID = 0;
|
3906
|
-
const SUBTENSOR_MIN_STAKE_AMOUNT_PLANK = 1000000n;
|
3907
|
-
const TAO_DECIMALS = 9n;
|
3908
|
-
const SCALE_FACTOR = 10n ** TAO_DECIMALS; // Equivalent to 10e9 for precision
|
3909
|
-
const ONE_ALPHA_TOKEN = SCALE_FACTOR;
|
3910
|
-
const calculateAlphaPrice = ({
|
3677
|
+
const SUBTENSOR_ROOT_NETUID$1 = 0;
|
3678
|
+
const SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 = 1000000n;
|
3679
|
+
const TAO_DECIMALS$1 = 9n;
|
3680
|
+
const SCALE_FACTOR$1 = 10n ** TAO_DECIMALS$1; // Equivalent to 10e9 for precision
|
3681
|
+
const ONE_ALPHA_TOKEN$1 = SCALE_FACTOR$1;
|
3682
|
+
const calculateAlphaPrice$1 = ({
|
3911
3683
|
dynamicInfo
|
3912
3684
|
}) => {
|
3913
3685
|
if (!dynamicInfo) return 0n;
|
@@ -3917,25 +3689,25 @@ const calculateAlphaPrice = ({
|
|
3917
3689
|
} = dynamicInfo;
|
3918
3690
|
|
3919
3691
|
// Scale taoIn before division to preserve precision
|
3920
|
-
const result = tao_in * SCALE_FACTOR / alpha_in;
|
3692
|
+
const result = tao_in * SCALE_FACTOR$1 / alpha_in;
|
3921
3693
|
return result; // Scaled price as bigint
|
3922
3694
|
};
|
3923
|
-
const calculateTaoAmountFromAlpha = ({
|
3695
|
+
const calculateTaoAmountFromAlpha$1 = ({
|
3924
3696
|
alphaPrice,
|
3925
3697
|
alphaStaked
|
3926
3698
|
}) => {
|
3927
3699
|
if (!alphaStaked || !alphaPrice) return 0n;
|
3928
3700
|
const expectedAlpha = alphaStaked * alphaPrice;
|
3929
|
-
return expectedAlpha / SCALE_FACTOR;
|
3701
|
+
return expectedAlpha / SCALE_FACTOR$1;
|
3930
3702
|
};
|
3931
|
-
const calculateTaoFromDynamicInfo = ({
|
3703
|
+
const calculateTaoFromDynamicInfo$1 = ({
|
3932
3704
|
dynamicInfo,
|
3933
3705
|
alphaStaked
|
3934
3706
|
}) => {
|
3935
|
-
const alphaPrice = calculateAlphaPrice({
|
3707
|
+
const alphaPrice = calculateAlphaPrice$1({
|
3936
3708
|
dynamicInfo
|
3937
3709
|
});
|
3938
|
-
return calculateTaoAmountFromAlpha({
|
3710
|
+
return calculateTaoAmountFromAlpha$1({
|
3939
3711
|
alphaPrice,
|
3940
3712
|
alphaStaked
|
3941
3713
|
});
|
@@ -3951,7 +3723,7 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
3951
3723
|
const networkIds = keys(addressesByToken).map(tokenId => parseTokenId(tokenId).networkId);
|
3952
3724
|
const miniMetadatas = new Map();
|
3953
3725
|
for (const networkId of networkIds) {
|
3954
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, "substrate-native", signal);
|
3726
|
+
const miniMetadata = await getMiniMetadata$9(chaindataProvider, chainConnector, networkId, "substrate-native", signal);
|
3955
3727
|
miniMetadatas.set(networkId, miniMetadata);
|
3956
3728
|
}
|
3957
3729
|
signal?.throwIfAborted();
|
@@ -4042,7 +3814,7 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
4042
3814
|
const params = [address];
|
4043
3815
|
const result = await scaleApi.getRuntimeCallValue("StakeInfoRuntimeApi", "get_stake_info_for_coldkey", params);
|
4044
3816
|
if (!Array.isArray(result)) return [];
|
4045
|
-
const uniqueNetuids = Array.from(new Set(result.map(item => Number(item.netuid)).filter(netuid => netuid !== SUBTENSOR_ROOT_NETUID)));
|
3817
|
+
const uniqueNetuids = Array.from(new Set(result.map(item => Number(item.netuid)).filter(netuid => netuid !== SUBTENSOR_ROOT_NETUID$1)));
|
4046
3818
|
await fetchDynamicInfoForNetuids(uniqueNetuids);
|
4047
3819
|
const stakes = result?.map(({
|
4048
3820
|
coldkey,
|
@@ -4059,7 +3831,7 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
4059
3831
|
};
|
4060
3832
|
}).filter(({
|
4061
3833
|
stake
|
4062
|
-
}) => stake >= SUBTENSOR_MIN_STAKE_AMOUNT_PLANK);
|
3834
|
+
}) => stake >= SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1);
|
4063
3835
|
return stakes;
|
4064
3836
|
}];
|
4065
3837
|
const errors = [];
|
@@ -4105,15 +3877,15 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
4105
3877
|
const subnetIdentity = subnet_identity ? binaryToText(subnet_identity) : undefined;
|
4106
3878
|
|
4107
3879
|
// Add 1n balance if failed to fetch dynamic info, so the position is not ignored by Balance lib and is displayed in the UI.
|
4108
|
-
const alphaStakedInTao = dynamicInfo ? calculateTaoFromDynamicInfo({
|
3880
|
+
const alphaStakedInTao = dynamicInfo ? calculateTaoFromDynamicInfo$1({
|
4109
3881
|
dynamicInfo,
|
4110
3882
|
alphaStaked: stake
|
4111
3883
|
}) : 1n;
|
4112
|
-
const alphaToTaoRate = calculateTaoFromDynamicInfo({
|
3884
|
+
const alphaToTaoRate = calculateTaoFromDynamicInfo$1({
|
4113
3885
|
dynamicInfo: dynamicInfo ?? null,
|
4114
|
-
alphaStaked: ONE_ALPHA_TOKEN
|
3886
|
+
alphaStaked: ONE_ALPHA_TOKEN$1
|
4115
3887
|
}).toString();
|
4116
|
-
const stakeByNetuid = Number(netuid) === SUBTENSOR_ROOT_NETUID ? stake : alphaStakedInTao;
|
3888
|
+
const stakeByNetuid = Number(netuid) === SUBTENSOR_ROOT_NETUID$1 ? stake : alphaStakedInTao;
|
4117
3889
|
return {
|
4118
3890
|
source: "substrate-native",
|
4119
3891
|
status: "live",
|
@@ -4173,14 +3945,14 @@ async function subscribeSubtensorStaking(chaindataProvider, chainConnector, addr
|
|
4173
3945
|
}
|
4174
3946
|
}
|
4175
3947
|
|
4176
|
-
const getOtherType = input => `other-${input}`;
|
3948
|
+
const getOtherType$1 = input => `other-${input}`;
|
4177
3949
|
|
4178
3950
|
/**
|
4179
3951
|
* For converting the value of `lock?.id?.toUtf8?.()` which is retrieved from
|
4180
3952
|
* the Balances.Locks storage key into a useful classification for our UI
|
4181
3953
|
*/
|
4182
|
-
const getLockedType = input => {
|
4183
|
-
if (typeof input !== "string") return getOtherType("unknown");
|
3954
|
+
const getLockedType$1 = input => {
|
3955
|
+
if (typeof input !== "string") return getOtherType$1("unknown");
|
4184
3956
|
if (input.includes("vesting")) return "vesting";
|
4185
3957
|
if (input.includes("calamvst")) return "vesting"; // vesting on manta network
|
4186
3958
|
if (input.includes("ormlvest")) return "vesting"; // vesting ORML tokens
|
@@ -4202,19 +3974,19 @@ const getLockedType = input => {
|
|
4202
3974
|
if (input.includes("councilo")) return "democracy"; // Councilor
|
4203
3975
|
if (input.includes("proposal")) return "democracy";
|
4204
3976
|
if (input.includes("boundsta")) return "staking"; // Bound Staking Account
|
4205
|
-
if (input.includes("invitemb")) return getOtherType(input); // Invite member
|
4206
|
-
if (input.includes("bounty")) return getOtherType(input);
|
4207
|
-
if (input.startsWith("wg-")) return getOtherType(input);
|
3977
|
+
if (input.includes("invitemb")) return getOtherType$1(input); // Invite member
|
3978
|
+
if (input.includes("bounty")) return getOtherType$1(input);
|
3979
|
+
if (input.startsWith("wg-")) return getOtherType$1(input);
|
4208
3980
|
|
4209
3981
|
// ignore technical or undocumented lock types
|
4210
|
-
if (input.includes("pdexlock")) return getOtherType(input);
|
4211
|
-
if (input.includes("phala/sp")) return getOtherType(input);
|
4212
|
-
if (input.includes("aca/earn")) return getOtherType(input);
|
4213
|
-
if (input.includes("stk_stks")) return getOtherType(input);
|
3982
|
+
if (input.includes("pdexlock")) return getOtherType$1(input);
|
3983
|
+
if (input.includes("phala/sp")) return getOtherType$1(input);
|
3984
|
+
if (input.includes("aca/earn")) return getOtherType$1(input);
|
3985
|
+
if (input.includes("stk_stks")) return getOtherType$1(input);
|
4214
3986
|
|
4215
3987
|
// eslint-disable-next-line no-console
|
4216
3988
|
console.warn(`unknown locked type: ${input}`);
|
4217
|
-
return getOtherType(input);
|
3989
|
+
return getOtherType$1(input);
|
4218
3990
|
};
|
4219
3991
|
const baseLockLabels = ["fees", "misc"];
|
4220
3992
|
const isBaseLock = lock => baseLockLabels.includes(lock.label);
|
@@ -4311,7 +4083,7 @@ const AccountInfoOverrides = {
|
|
4311
4083
|
// nftmart is not yet on metadata v14
|
4312
4084
|
"nftmart": RegularAccountInfoFallback
|
4313
4085
|
};
|
4314
|
-
async function buildQueries$
|
4086
|
+
async function buildQueries$4(chains, tokens, chainStorageCoders, miniMetadatas, addressesByToken) {
|
4315
4087
|
return Object.entries(addressesByToken).reduce((outerResult, [tokenId, addresses]) => {
|
4316
4088
|
const token = tokens[tokenId];
|
4317
4089
|
if (!token) {
|
@@ -4443,7 +4215,7 @@ async function buildQueries$1(chains, tokens, chainStorageCoders, miniMetadatas,
|
|
4443
4215
|
locksQueryLocks = decoded?.map?.(lock => ({
|
4444
4216
|
type: "locked",
|
4445
4217
|
source: "substrate-native-locks",
|
4446
|
-
label: getLockedType(lock?.id?.asText?.()),
|
4218
|
+
label: getLockedType$1(lock?.id?.asText?.()),
|
4447
4219
|
meta: {
|
4448
4220
|
id: lock?.id?.asText?.()
|
4449
4221
|
},
|
@@ -4507,7 +4279,7 @@ async function buildQueries$1(chains, tokens, chainStorageCoders, miniMetadatas,
|
|
4507
4279
|
freezesQueryLocks = decoded?.map?.(lock => ({
|
4508
4280
|
type: "locked",
|
4509
4281
|
source: "substrate-native-freezes",
|
4510
|
-
label: getLockedType(lock?.id?.type?.toLowerCase?.()),
|
4282
|
+
label: getLockedType$1(lock?.id?.type?.toLowerCase?.()),
|
4511
4283
|
amount: lock?.amount?.toString?.() ?? "0"
|
4512
4284
|
})) ?? [];
|
4513
4285
|
|
@@ -4646,7 +4418,7 @@ class QueryCache {
|
|
4646
4418
|
const byNetwork = getAddresssesByTokenByNetwork(addressesByToken);
|
4647
4419
|
for (const networkId of keys(byNetwork)) {
|
4648
4420
|
if (this.miniMetadatas.has(networkId)) continue;
|
4649
|
-
const miniMetadata = await getMiniMetadata(this.#chaindataProvider, this.#chainConnector, networkId, "substrate-native");
|
4421
|
+
const miniMetadata = await getMiniMetadata$9(this.#chaindataProvider, this.#chainConnector, networkId, "substrate-native");
|
4650
4422
|
this.miniMetadatas.set(networkId, miniMetadata);
|
4651
4423
|
}
|
4652
4424
|
|
@@ -4665,7 +4437,7 @@ class QueryCache {
|
|
4665
4437
|
freezes: ["Balances", "Freezes"]
|
4666
4438
|
}
|
4667
4439
|
});
|
4668
|
-
const queries = await buildQueries$
|
4440
|
+
const queries = await buildQueries$4(chains, tokens, chainStorageCoders, this.miniMetadatas, queryResults.newAddressesByToken);
|
4669
4441
|
// now update the cache
|
4670
4442
|
Object.entries(queries).forEach(([key, query]) => {
|
4671
4443
|
this.balanceQueryCache.set(key, query);
|
@@ -4694,21 +4466,21 @@ class SubNativeBalanceError extends Error {
|
|
4694
4466
|
}
|
4695
4467
|
}
|
4696
4468
|
|
4697
|
-
const DotNetworkPropertiesSimple = z.object({
|
4469
|
+
const DotNetworkPropertiesSimple$1 = z.object({
|
4698
4470
|
tokenDecimals: z.number().optional().default(0),
|
4699
4471
|
tokenSymbol: z.string().optional().default("Unit")
|
4700
4472
|
});
|
4701
|
-
const DotNetworkPropertiesArray = z.object({
|
4473
|
+
const DotNetworkPropertiesArray$1 = z.object({
|
4702
4474
|
tokenDecimals: z.array(z.number()).nonempty(),
|
4703
4475
|
tokenSymbol: z.array(z.string()).nonempty()
|
4704
4476
|
});
|
4705
|
-
const DotNetworkPropertiesSchema = z.union([DotNetworkPropertiesSimple, DotNetworkPropertiesArray]).transform(val => ({
|
4477
|
+
const DotNetworkPropertiesSchema$1 = z.union([DotNetworkPropertiesSimple$1, DotNetworkPropertiesArray$1]).transform(val => ({
|
4706
4478
|
tokenDecimals: Array.isArray(val.tokenDecimals) ? val.tokenDecimals[0] : val.tokenDecimals,
|
4707
4479
|
tokenSymbol: Array.isArray(val.tokenSymbol) ? val.tokenSymbol[0] : val.tokenSymbol
|
4708
4480
|
}));
|
4709
|
-
const getChainProperties = async (chainConnector, networkId) => {
|
4481
|
+
const getChainProperties$1 = async (chainConnector, networkId) => {
|
4710
4482
|
const properties = await chainConnector.send(networkId, "system_properties", [], true);
|
4711
|
-
return DotNetworkPropertiesSchema.parse(properties);
|
4483
|
+
return DotNetworkPropertiesSchema$1.parse(properties);
|
4712
4484
|
};
|
4713
4485
|
|
4714
4486
|
const POLLING_WINDOW_SIZE = 20;
|
@@ -4994,7 +4766,7 @@ const SubNativeModule = hydrate => {
|
|
4994
4766
|
const {
|
4995
4767
|
tokenSymbol: symbol,
|
4996
4768
|
tokenDecimals: decimals
|
4997
|
-
} = await getChainProperties(chainConnector, chainId);
|
4769
|
+
} = await getChainProperties$1(chainConnector, chainId);
|
4998
4770
|
const {
|
4999
4771
|
existentialDeposit
|
5000
4772
|
} = chainMeta.extra ?? {};
|
@@ -5033,7 +4805,7 @@ const SubNativeModule = hydrate => {
|
|
5033
4805
|
try {
|
5034
4806
|
// this is what we want to be done separately for each network
|
5035
4807
|
// this will update the DB so minimetadata will be available when it's used, everywhere else down the tree of subscribeChainBalances
|
5036
|
-
await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType$2, controller.signal);
|
4808
|
+
await getMiniMetadata$9(chaindataProvider, chainConnector, networkId, moduleType$2, controller.signal);
|
5037
4809
|
} catch (err) {
|
5038
4810
|
if (!isAbortError(err)) log.warn("Failed to get native token miniMetadata for network", networkId, err);
|
5039
4811
|
return () => {};
|
@@ -6350,7 +6122,7 @@ const SubPsp22Module = hydrate => {
|
|
6350
6122
|
if (!subscriptionActive) return;
|
6351
6123
|
try {
|
6352
6124
|
assert(chainConnectors.substrate, "This module requires a substrate chain connector");
|
6353
|
-
const balances = await fetchBalances(chainConnectors.substrate, tokens, addressesByToken);
|
6125
|
+
const balances = await fetchBalances$9(chainConnectors.substrate, tokens, addressesByToken);
|
6354
6126
|
|
6355
6127
|
// Don't call callback with balances which have not changed since the last poll.
|
6356
6128
|
const updatedBalances = new Balances([...balances].filter(b => {
|
@@ -6373,7 +6145,7 @@ const SubPsp22Module = hydrate => {
|
|
6373
6145
|
async fetchBalances(addressesByToken) {
|
6374
6146
|
assert(chainConnectors.substrate, "This module requires a substrate chain connector");
|
6375
6147
|
const tokens = await chaindataProvider.getTokensMapById();
|
6376
|
-
return fetchBalances(chainConnectors.substrate, tokens, addressesByToken);
|
6148
|
+
return fetchBalances$9(chainConnectors.substrate, tokens, addressesByToken);
|
6377
6149
|
},
|
6378
6150
|
async transferToken({
|
6379
6151
|
tokenId,
|
@@ -6453,7 +6225,7 @@ const SubPsp22Module = hydrate => {
|
|
6453
6225
|
}
|
6454
6226
|
};
|
6455
6227
|
};
|
6456
|
-
const fetchBalances = async (chainConnector, tokens, addressesByToken) => {
|
6228
|
+
const fetchBalances$9 = async (chainConnector, tokens, addressesByToken) => {
|
6457
6229
|
const registry = new TypeRegistry();
|
6458
6230
|
const Psp22Abi = new Abi(psp22Abi);
|
6459
6231
|
const balanceRequests = Object.entries(addressesByToken).flatMap(([tokenId, addresses]) => addresses.map(address => [tokenId, address])).flatMap(async ([tokenId, address]) => {
|
@@ -6614,7 +6386,7 @@ const SubTokensModule = hydrate => {
|
|
6614
6386
|
},
|
6615
6387
|
async fetchBalances(addressesByToken) {
|
6616
6388
|
assert(chainConnectors.substrate, "This module requires a substrate chain connector");
|
6617
|
-
const queries = await buildQueries(chainConnector, chaindataProvider, addressesByToken);
|
6389
|
+
const queries = await buildQueries$3(chainConnector, chaindataProvider, addressesByToken);
|
6618
6390
|
const result = await new RpcStateQueryHelper(chainConnectors.substrate, queries).fetch();
|
6619
6391
|
const balances = result?.filter(b => b !== null) ?? [];
|
6620
6392
|
return new Balances(balances);
|
@@ -6631,7 +6403,7 @@ const SubTokensModule = hydrate => {
|
|
6631
6403
|
const chainId = token.networkId;
|
6632
6404
|
const chain = await chaindataProvider.getNetworkById(chainId, "polkadot");
|
6633
6405
|
assert(chain?.genesisHash, `Chain ${chainId} not found in store`);
|
6634
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, chainId, moduleType);
|
6406
|
+
const miniMetadata = await getMiniMetadata$9(chaindataProvider, chainConnector, chainId, moduleType);
|
6635
6407
|
const tokensPallet = miniMetadata?.extra?.palletId ?? defaultPalletId;
|
6636
6408
|
const onChainId = (() => {
|
6637
6409
|
try {
|
@@ -6732,7 +6504,7 @@ const SubTokensModule = hydrate => {
|
|
6732
6504
|
};
|
6733
6505
|
};
|
6734
6506
|
async function buildNetworkQueries(networkId, chainConnector, chaindataProvider, addressesByToken, signal) {
|
6735
|
-
const miniMetadata = await getMiniMetadata(chaindataProvider, chainConnector, networkId, moduleType, signal);
|
6507
|
+
const miniMetadata = await getMiniMetadata$9(chaindataProvider, chainConnector, networkId, moduleType, signal);
|
6736
6508
|
const chain = await chaindataProvider.getNetworkById(networkId, "polkadot");
|
6737
6509
|
const tokens = await chaindataProvider.getTokensMapById();
|
6738
6510
|
if (!chain) return [];
|
@@ -6803,7 +6575,7 @@ async function buildNetworkQueries(networkId, chainConnector, chaindataProvider,
|
|
6803
6575
|
});
|
6804
6576
|
});
|
6805
6577
|
}
|
6806
|
-
async function buildQueries(chainConnector, chaindataProvider, addressesByToken, signal) {
|
6578
|
+
async function buildQueries$3(chainConnector, chaindataProvider, addressesByToken, signal) {
|
6807
6579
|
const byNetwork = keys(addressesByToken).reduce((acc, tokenId) => {
|
6808
6580
|
const networkId = parseSubTokensTokenId(tokenId).networkId;
|
6809
6581
|
if (!acc[networkId]) acc[networkId] = {};
|
@@ -6817,4 +6589,3796 @@ async function buildQueries(chainConnector, chaindataProvider, addressesByToken,
|
|
6817
6589
|
|
6818
6590
|
const defaultBalanceModules = [EvmErc20Module, EvmNativeModule, EvmUniswapV2Module, SubAssetsModule, SubForeignAssetsModule, SubNativeModule, SubPsp22Module, SubTokensModule];
|
6819
6591
|
|
6820
|
-
|
6592
|
+
const MODULE_TYPE$8 = EvmErc20TokenSchema.shape.type.value;
|
6593
|
+
const PLATFORM$8 = EvmErc20TokenSchema.shape.platform.value;
|
6594
|
+
|
6595
|
+
// to be used by chaindata too
|
6596
|
+
z.strictObject({
|
6597
|
+
contractAddress: EvmErc20TokenSchema.shape.contractAddress,
|
6598
|
+
...TokenConfigBaseSchema.shape
|
6599
|
+
});
|
6600
|
+
|
6601
|
+
class BalanceFetchError extends Error {
|
6602
|
+
constructor(message, tokenId, address, cause) {
|
6603
|
+
super(message);
|
6604
|
+
this.name = "BalanceFetchError";
|
6605
|
+
this.tokenId = tokenId;
|
6606
|
+
this.address = address;
|
6607
|
+
if (cause) this.cause = cause;
|
6608
|
+
}
|
6609
|
+
}
|
6610
|
+
class BalanceFetchNetworkError extends Error {
|
6611
|
+
constructor(message, evmNetworkId, cause) {
|
6612
|
+
super(message);
|
6613
|
+
this.name = "BalanceFetchNetworkError";
|
6614
|
+
this.evmNetworkId = evmNetworkId;
|
6615
|
+
if (cause) this.cause = cause;
|
6616
|
+
}
|
6617
|
+
}
|
6618
|
+
|
6619
|
+
const getBalanceDefs = addressesByToken => {
|
6620
|
+
return addressesByToken.flatMap(([token, addresses]) => addresses.map(address => ({
|
6621
|
+
token,
|
6622
|
+
address
|
6623
|
+
})));
|
6624
|
+
};
|
6625
|
+
|
6626
|
+
const fetchBalances$8 = async ({
|
6627
|
+
networkId,
|
6628
|
+
addressesByToken,
|
6629
|
+
connector
|
6630
|
+
}) => {
|
6631
|
+
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
6632
|
+
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
6633
|
+
for (const [token, addresses] of addressesByToken) {
|
6634
|
+
if (token.type !== MODULE_TYPE$8 || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for EVM ERC20 balance module: ${token.type} on ${token.networkId}`);
|
6635
|
+
for (const address of addresses) if (!isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
6636
|
+
}
|
6637
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
6638
|
+
if (client.chain?.contracts?.erc20Aggregator && balanceDefs.length > 1) {
|
6639
|
+
const erc20Aggregator = client.chain.contracts.erc20Aggregator;
|
6640
|
+
return fetchWithAggregator$1(client, balanceDefs, erc20Aggregator.address);
|
6641
|
+
}
|
6642
|
+
return fetchWithoutAggregator$1(client, balanceDefs);
|
6643
|
+
};
|
6644
|
+
const fetchWithoutAggregator$1 = async (client, balanceDefs) => {
|
6645
|
+
if (balanceDefs.length === 0) return {
|
6646
|
+
success: [],
|
6647
|
+
errors: []
|
6648
|
+
};
|
6649
|
+
log.debug("fetching %s balances without aggregator", MODULE_TYPE$8, balanceDefs.length);
|
6650
|
+
const results = await Promise.allSettled(balanceDefs.map(async ({
|
6651
|
+
token,
|
6652
|
+
address
|
6653
|
+
}) => {
|
6654
|
+
try {
|
6655
|
+
const result = await client.readContract({
|
6656
|
+
abi: erc20Abi,
|
6657
|
+
address: token.contractAddress,
|
6658
|
+
functionName: "balanceOf",
|
6659
|
+
args: [address]
|
6660
|
+
});
|
6661
|
+
const balance = {
|
6662
|
+
address,
|
6663
|
+
tokenId: token.id,
|
6664
|
+
value: result.toString(),
|
6665
|
+
source: MODULE_TYPE$8,
|
6666
|
+
networkId: parseEvmErc20TokenId(token.id).networkId,
|
6667
|
+
status: "cache"
|
6668
|
+
};
|
6669
|
+
return balance;
|
6670
|
+
} catch (err) {
|
6671
|
+
throw new BalanceFetchError(`Failed to get balance for token ${token.id} and address ${address} on chain ${client.chain?.id}`, token.id, address, err);
|
6672
|
+
}
|
6673
|
+
}));
|
6674
|
+
return results.reduce((acc, result) => {
|
6675
|
+
if (result.status === "fulfilled") acc.success.push(result.value);else {
|
6676
|
+
const error = result.reason;
|
6677
|
+
acc.errors.push({
|
6678
|
+
tokenId: error.tokenId,
|
6679
|
+
address: error.address,
|
6680
|
+
error
|
6681
|
+
});
|
6682
|
+
}
|
6683
|
+
return acc;
|
6684
|
+
}, {
|
6685
|
+
success: [],
|
6686
|
+
errors: []
|
6687
|
+
});
|
6688
|
+
};
|
6689
|
+
const fetchWithAggregator$1 = async (client, balanceDefs, erc20BalancesAggregatorAddress) => {
|
6690
|
+
if (balanceDefs.length === 0) return {
|
6691
|
+
success: [],
|
6692
|
+
errors: []
|
6693
|
+
};
|
6694
|
+
log.debug("fetching %s balances with aggregator", MODULE_TYPE$8, balanceDefs.length);
|
6695
|
+
try {
|
6696
|
+
const erc20Balances = await client.readContract({
|
6697
|
+
abi: erc20BalancesAggregatorAbi,
|
6698
|
+
address: erc20BalancesAggregatorAddress,
|
6699
|
+
functionName: "balances",
|
6700
|
+
args: [balanceDefs.map(b => ({
|
6701
|
+
account: b.address,
|
6702
|
+
token: b.token.contractAddress
|
6703
|
+
}))]
|
6704
|
+
});
|
6705
|
+
const success = balanceDefs.map((balanceDef, index) => ({
|
6706
|
+
address: balanceDef.address,
|
6707
|
+
tokenId: balanceDef.token.id,
|
6708
|
+
value: erc20Balances[index].toString(),
|
6709
|
+
source: MODULE_TYPE$8,
|
6710
|
+
networkId: parseTokenId(balanceDef.token.id).networkId,
|
6711
|
+
status: "cache"
|
6712
|
+
}));
|
6713
|
+
return {
|
6714
|
+
success,
|
6715
|
+
errors: []
|
6716
|
+
};
|
6717
|
+
} catch (err) {
|
6718
|
+
const errors = balanceDefs.map(balanceDef => ({
|
6719
|
+
tokenId: balanceDef.token.id,
|
6720
|
+
address: balanceDef.address,
|
6721
|
+
error: new BalanceFetchNetworkError(`Failed to get balances for evm-erc20 tokens on chain ${client.chain?.id}`, String(client.chain?.id), err)
|
6722
|
+
}));
|
6723
|
+
return {
|
6724
|
+
success: [],
|
6725
|
+
errors
|
6726
|
+
};
|
6727
|
+
}
|
6728
|
+
};
|
6729
|
+
|
6730
|
+
const getErc20ContractData$1 = async (client, contractAddress) => {
|
6731
|
+
try {
|
6732
|
+
const contract = getTypedContract$1(client, erc20Abi, contractAddress);
|
6733
|
+
|
6734
|
+
// eslint-disable-next-line no-var
|
6735
|
+
var [symbol, decimals, name] = await Promise.all([contract.read.symbol(), contract.read.decimals(), contract.read.name()]);
|
6736
|
+
} catch (e) {
|
6737
|
+
if (e instanceof ContractFunctionExecutionError) {
|
6738
|
+
// try to perform the contract read with bytes32 symbol
|
6739
|
+
const contract = getTypedContract$1(client, erc20Abi_bytes32, contractAddress);
|
6740
|
+
|
6741
|
+
// eslint-disable-next-line no-var
|
6742
|
+
var [bytesSymbol, decimals, nameSymbol] = await Promise.all([contract.read.symbol(), contract.read.decimals(), contract.read.name()]);
|
6743
|
+
symbol = hexToString(bytesSymbol).replace(/\0/g, "").trim(); // remove NULL characters
|
6744
|
+
name = hexToString(nameSymbol).replace(/\0/g, "").trim(); // remove NULL characters
|
6745
|
+
} else throw e;
|
6746
|
+
}
|
6747
|
+
return {
|
6748
|
+
symbol,
|
6749
|
+
decimals,
|
6750
|
+
name
|
6751
|
+
};
|
6752
|
+
};
|
6753
|
+
const getTypedContract$1 = (client, abi, contractAddress) => getContract({
|
6754
|
+
address: contractAddress,
|
6755
|
+
abi,
|
6756
|
+
client: {
|
6757
|
+
public: client
|
6758
|
+
}
|
6759
|
+
});
|
6760
|
+
|
6761
|
+
const TokenCacheSchema$1 = EvmErc20TokenSchema.pick({
|
6762
|
+
symbol: true,
|
6763
|
+
decimals: true,
|
6764
|
+
name: true
|
6765
|
+
});
|
6766
|
+
const fetchTokens$8 = async ({
|
6767
|
+
networkId,
|
6768
|
+
tokens,
|
6769
|
+
connector,
|
6770
|
+
cache
|
6771
|
+
}) => {
|
6772
|
+
const result = [];
|
6773
|
+
for (const tokenConfig of tokens) {
|
6774
|
+
const tokenId = evmErc20TokenId(networkId, tokenConfig.contractAddress);
|
6775
|
+
if (!cache[tokenId] || !TokenCacheSchema$1.safeParse(cache[tokenId]).success) {
|
6776
|
+
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
6777
|
+
if (!client) {
|
6778
|
+
log.warn(`No client found for network ${networkId} while fetching EVM ERC20 tokens`);
|
6779
|
+
continue;
|
6780
|
+
}
|
6781
|
+
try {
|
6782
|
+
const {
|
6783
|
+
name,
|
6784
|
+
decimals,
|
6785
|
+
symbol
|
6786
|
+
} = await getErc20ContractData$1(client, tokenConfig.contractAddress);
|
6787
|
+
cache[tokenId] = {
|
6788
|
+
id: tokenId,
|
6789
|
+
symbol,
|
6790
|
+
decimals,
|
6791
|
+
name
|
6792
|
+
};
|
6793
|
+
} catch (err) {
|
6794
|
+
log.warn(`Failed to fetch ERC20 token data for ${tokenConfig.contractAddress}`, err);
|
6795
|
+
continue;
|
6796
|
+
}
|
6797
|
+
}
|
6798
|
+
const base = {
|
6799
|
+
type: MODULE_TYPE$8,
|
6800
|
+
platform: PLATFORM$8,
|
6801
|
+
networkId
|
6802
|
+
};
|
6803
|
+
const token = assign(base, cache[tokenId], tokenConfig);
|
6804
|
+
const parsed = EvmErc20TokenSchema.safeParse(token);
|
6805
|
+
if (!parsed.success) {
|
6806
|
+
log.warn("Ignoring token with invalid EvmErc20TokenSchema", token);
|
6807
|
+
continue;
|
6808
|
+
}
|
6809
|
+
result.push(parsed.data);
|
6810
|
+
}
|
6811
|
+
return result;
|
6812
|
+
};
|
6813
|
+
|
6814
|
+
const getMiniMetadata$8 = () => {
|
6815
|
+
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
6816
|
+
};
|
6817
|
+
|
6818
|
+
const getTransferCallData$8 = ({
|
6819
|
+
from,
|
6820
|
+
to,
|
6821
|
+
value,
|
6822
|
+
token
|
6823
|
+
}) => {
|
6824
|
+
if (!isTokenOfType(token, MODULE_TYPE$8)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$8}.`);
|
6825
|
+
if (!isEthereumAddress(from)) throw new Error("Invalid from address");
|
6826
|
+
if (!isEthereumAddress(to)) throw new Error("Invalid to address");
|
6827
|
+
const data = encodeFunctionData({
|
6828
|
+
abi: erc20Abi,
|
6829
|
+
functionName: "transfer",
|
6830
|
+
args: [to, BigInt(value)]
|
6831
|
+
});
|
6832
|
+
return {
|
6833
|
+
from,
|
6834
|
+
to: token.contractAddress,
|
6835
|
+
data
|
6836
|
+
};
|
6837
|
+
};
|
6838
|
+
|
6839
|
+
const SUBSCRIPTION_INTERVAL$8 = 6_000;
|
6840
|
+
const subscribeBalances$8 = ({
|
6841
|
+
networkId,
|
6842
|
+
addressesByToken,
|
6843
|
+
connector
|
6844
|
+
}) => {
|
6845
|
+
return new Observable(subscriber => {
|
6846
|
+
const abortController = new AbortController();
|
6847
|
+
const poll = async () => {
|
6848
|
+
try {
|
6849
|
+
if (abortController.signal.aborted) return;
|
6850
|
+
const balances = await fetchBalances$8({
|
6851
|
+
networkId,
|
6852
|
+
addressesByToken,
|
6853
|
+
connector
|
6854
|
+
});
|
6855
|
+
if (abortController.signal.aborted) return;
|
6856
|
+
subscriber.next(balances);
|
6857
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$8);
|
6858
|
+
} catch (error) {
|
6859
|
+
log.error("Error", {
|
6860
|
+
module: MODULE_TYPE$8,
|
6861
|
+
networkId,
|
6862
|
+
addressesByToken,
|
6863
|
+
error
|
6864
|
+
});
|
6865
|
+
subscriber.error(error);
|
6866
|
+
}
|
6867
|
+
};
|
6868
|
+
poll();
|
6869
|
+
return () => {
|
6870
|
+
abortController.abort();
|
6871
|
+
};
|
6872
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
6873
|
+
};
|
6874
|
+
|
6875
|
+
const EvmErc20BalanceModule = {
|
6876
|
+
type: MODULE_TYPE$8,
|
6877
|
+
platform: PLATFORM$8,
|
6878
|
+
getMiniMetadata: getMiniMetadata$8,
|
6879
|
+
fetchTokens: fetchTokens$8,
|
6880
|
+
fetchBalances: fetchBalances$8,
|
6881
|
+
subscribeBalances: subscribeBalances$8,
|
6882
|
+
getTransferCallData: getTransferCallData$8
|
6883
|
+
};
|
6884
|
+
|
6885
|
+
const MODULE_TYPE$7 = EvmNativeTokenSchema.shape.type.value;
|
6886
|
+
const PLATFORM$7 = EvmNativeTokenSchema.shape.platform.value;
|
6887
|
+
|
6888
|
+
// to be used by chaindata too
|
6889
|
+
z.strictObject({
|
6890
|
+
...TokenConfigBaseSchema.shape
|
6891
|
+
});
|
6892
|
+
|
6893
|
+
const fetchBalances$7 = async ({
|
6894
|
+
networkId,
|
6895
|
+
addressesByToken,
|
6896
|
+
connector
|
6897
|
+
}) => {
|
6898
|
+
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
6899
|
+
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
6900
|
+
for (const [token, addresses] of addressesByToken) {
|
6901
|
+
if (token.type !== MODULE_TYPE$7 || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for EVM ERC20 balance module: ${token.type} on ${token.networkId}`);
|
6902
|
+
for (const address of addresses) if (!isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
6903
|
+
}
|
6904
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
6905
|
+
if (client.chain?.contracts?.multicall3 && balanceDefs.length > 1) {
|
6906
|
+
const multicall3 = client.chain.contracts.multicall3;
|
6907
|
+
return fetchWithMulticall(client, balanceDefs, multicall3.address);
|
6908
|
+
}
|
6909
|
+
return fetchWithoutMulticall(client, balanceDefs);
|
6910
|
+
};
|
6911
|
+
const fetchWithoutMulticall = async (client, balanceDefs) => {
|
6912
|
+
if (balanceDefs.length === 0) return {
|
6913
|
+
success: [],
|
6914
|
+
errors: []
|
6915
|
+
};
|
6916
|
+
log.debug("fetching %s balances without multicall3", MODULE_TYPE$7, balanceDefs.length);
|
6917
|
+
const results = await Promise.allSettled(balanceDefs.map(async ({
|
6918
|
+
token,
|
6919
|
+
address
|
6920
|
+
}) => {
|
6921
|
+
try {
|
6922
|
+
const result = await client.getBalance({
|
6923
|
+
address
|
6924
|
+
});
|
6925
|
+
const balance = {
|
6926
|
+
address,
|
6927
|
+
tokenId: token.id,
|
6928
|
+
value: result.toString(),
|
6929
|
+
source: MODULE_TYPE$7,
|
6930
|
+
networkId: parseTokenId(token.id).networkId,
|
6931
|
+
status: "live"
|
6932
|
+
};
|
6933
|
+
return balance;
|
6934
|
+
} catch (err) {
|
6935
|
+
throw new BalanceFetchError(`Failed to get balance for token ${token.id} and address ${address} on chain ${client.chain?.id}`, token.id, address, err);
|
6936
|
+
}
|
6937
|
+
}));
|
6938
|
+
return results.reduce((acc, result) => {
|
6939
|
+
if (result.status === "fulfilled") acc.success.push(result.value);else {
|
6940
|
+
const error = result.reason;
|
6941
|
+
acc.errors.push({
|
6942
|
+
tokenId: error.tokenId,
|
6943
|
+
address: error.address,
|
6944
|
+
error
|
6945
|
+
});
|
6946
|
+
}
|
6947
|
+
return acc;
|
6948
|
+
}, {
|
6949
|
+
success: [],
|
6950
|
+
errors: []
|
6951
|
+
});
|
6952
|
+
};
|
6953
|
+
const fetchWithMulticall = async (client, balanceDefs, multicall3Address) => {
|
6954
|
+
if (balanceDefs.length === 0) return {
|
6955
|
+
success: [],
|
6956
|
+
errors: []
|
6957
|
+
};
|
6958
|
+
log.debug("fetching %s balances with multicall3", MODULE_TYPE$7, balanceDefs.length);
|
6959
|
+
try {
|
6960
|
+
const callResults = await client.multicall({
|
6961
|
+
contracts: balanceDefs.map(({
|
6962
|
+
address
|
6963
|
+
}) => ({
|
6964
|
+
address: multicall3Address,
|
6965
|
+
abi: abiMulticall,
|
6966
|
+
functionName: "getEthBalance",
|
6967
|
+
args: [address]
|
6968
|
+
}))
|
6969
|
+
});
|
6970
|
+
return callResults.reduce((acc, result, index) => {
|
6971
|
+
if (result.status === "success") {
|
6972
|
+
acc.success.push({
|
6973
|
+
address: balanceDefs[index].address,
|
6974
|
+
tokenId: balanceDefs[index].token.id,
|
6975
|
+
value: result.result.toString(),
|
6976
|
+
source: MODULE_TYPE$7,
|
6977
|
+
networkId: parseTokenId(balanceDefs[index].token.id).networkId,
|
6978
|
+
status: "live"
|
6979
|
+
});
|
6980
|
+
}
|
6981
|
+
if (result.status === "failure") {
|
6982
|
+
acc.errors.push({
|
6983
|
+
tokenId: balanceDefs[index].token.id,
|
6984
|
+
address: balanceDefs[index].address,
|
6985
|
+
error: new BalanceFetchError(`Failed to get balance for token ${balanceDefs[index].token.id} and address ${balanceDefs[index].address} on chain ${client.chain?.id}`, balanceDefs[index].token.id, balanceDefs[index].address, result.error)
|
6986
|
+
});
|
6987
|
+
}
|
6988
|
+
return acc;
|
6989
|
+
}, {
|
6990
|
+
success: [],
|
6991
|
+
errors: []
|
6992
|
+
});
|
6993
|
+
} catch (err) {
|
6994
|
+
const errors = balanceDefs.map(balanceDef => ({
|
6995
|
+
tokenId: balanceDef.token.id,
|
6996
|
+
address: balanceDef.address,
|
6997
|
+
error: new BalanceFetchNetworkError(`Failed to get balances for evm-erc20 tokens on chain ${client.chain?.id}`, String(client.chain?.id), err)
|
6998
|
+
}));
|
6999
|
+
return {
|
7000
|
+
success: [],
|
7001
|
+
errors
|
7002
|
+
};
|
7003
|
+
}
|
7004
|
+
};
|
7005
|
+
|
7006
|
+
const fetchTokens$7 = async ({
|
7007
|
+
networkId,
|
7008
|
+
tokens
|
7009
|
+
}) => {
|
7010
|
+
// assume there is one and only one token in the array
|
7011
|
+
if (tokens.length !== 1) throw new Error("EVM Native module expects the nativeCurrency to be passed as a single token in the array");
|
7012
|
+
const token = assign({
|
7013
|
+
id: evmNativeTokenId(networkId),
|
7014
|
+
type: MODULE_TYPE$7,
|
7015
|
+
platform: PLATFORM$7,
|
7016
|
+
networkId,
|
7017
|
+
isDefault: true
|
7018
|
+
}, tokens[0]);
|
7019
|
+
const parsed = EvmNativeTokenSchema.safeParse(token);
|
7020
|
+
if (!parsed.success) {
|
7021
|
+
log.warn("Ignoring token with invalid EvmErc20TokenSchema", token);
|
7022
|
+
return [];
|
7023
|
+
}
|
7024
|
+
return [parsed.data];
|
7025
|
+
};
|
7026
|
+
|
7027
|
+
const getMiniMetadata$7 = () => {
|
7028
|
+
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
7029
|
+
};
|
7030
|
+
|
7031
|
+
const getTransferCallData$7 = ({
|
7032
|
+
from,
|
7033
|
+
to,
|
7034
|
+
value,
|
7035
|
+
token
|
7036
|
+
}) => {
|
7037
|
+
if (!isTokenOfType(token, MODULE_TYPE$7)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$7}.`);
|
7038
|
+
if (!isEthereumAddress(from)) throw new Error("Invalid from address");
|
7039
|
+
if (!isEthereumAddress(to)) throw new Error("Invalid to address");
|
7040
|
+
return {
|
7041
|
+
from,
|
7042
|
+
to,
|
7043
|
+
value,
|
7044
|
+
data: "0x"
|
7045
|
+
};
|
7046
|
+
};
|
7047
|
+
|
7048
|
+
const SUBSCRIPTION_INTERVAL$7 = 6_000;
|
7049
|
+
const subscribeBalances$7 = ({
|
7050
|
+
networkId,
|
7051
|
+
addressesByToken,
|
7052
|
+
connector
|
7053
|
+
}) => {
|
7054
|
+
return new Observable(subscriber => {
|
7055
|
+
const abortController = new AbortController();
|
7056
|
+
const poll = async () => {
|
7057
|
+
try {
|
7058
|
+
if (abortController.signal.aborted) return;
|
7059
|
+
const balances = await fetchBalances$7({
|
7060
|
+
networkId,
|
7061
|
+
addressesByToken,
|
7062
|
+
connector
|
7063
|
+
});
|
7064
|
+
if (abortController.signal.aborted) return;
|
7065
|
+
subscriber.next(balances);
|
7066
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$7);
|
7067
|
+
} catch (error) {
|
7068
|
+
log.error("Error", {
|
7069
|
+
module: MODULE_TYPE$7,
|
7070
|
+
networkId,
|
7071
|
+
addressesByToken,
|
7072
|
+
error
|
7073
|
+
});
|
7074
|
+
subscriber.error(error);
|
7075
|
+
}
|
7076
|
+
};
|
7077
|
+
poll();
|
7078
|
+
return () => {
|
7079
|
+
abortController.abort();
|
7080
|
+
};
|
7081
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
7082
|
+
};
|
7083
|
+
|
7084
|
+
const EvmNativeBalanceModule = {
|
7085
|
+
type: MODULE_TYPE$7,
|
7086
|
+
platform: PLATFORM$7,
|
7087
|
+
getMiniMetadata: getMiniMetadata$7,
|
7088
|
+
fetchTokens: fetchTokens$7,
|
7089
|
+
fetchBalances: fetchBalances$7,
|
7090
|
+
subscribeBalances: subscribeBalances$7,
|
7091
|
+
getTransferCallData: getTransferCallData$7
|
7092
|
+
};
|
7093
|
+
|
7094
|
+
const MODULE_TYPE$6 = EvmUniswapV2TokenSchema.shape.type.value;
|
7095
|
+
const PLATFORM$6 = EvmUniswapV2TokenSchema.shape.platform.value;
|
7096
|
+
|
7097
|
+
// to be used by chaindata too
|
7098
|
+
z.strictObject({
|
7099
|
+
contractAddress: EvmUniswapV2TokenSchema.shape.contractAddress,
|
7100
|
+
...TokenConfigBaseSchema.shape
|
7101
|
+
});
|
7102
|
+
|
7103
|
+
const fetchBalances$6 = async ({
|
7104
|
+
networkId,
|
7105
|
+
addressesByToken,
|
7106
|
+
connector
|
7107
|
+
}) => {
|
7108
|
+
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
7109
|
+
if (!client) throw new Error(`Could not get rpc provider for evm network ${networkId}`);
|
7110
|
+
for (const [token, addresses] of addressesByToken) {
|
7111
|
+
if (token.type !== MODULE_TYPE$6 || token.networkId !== networkId) throw new Error(`Invalid token type or networkId for EVM ERC20 balance module: ${token.type} on ${token.networkId}`);
|
7112
|
+
for (const address of addresses) if (!isEthereumAddress(address)) throw new Error(`Invalid ethereum address for EVM ERC20 balance module: ${address} for token ${token.id}`);
|
7113
|
+
}
|
7114
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
7115
|
+
if (client.chain?.contracts?.erc20Aggregator && balanceDefs.length > 1) {
|
7116
|
+
const erc20Aggregator = client.chain.contracts.erc20Aggregator;
|
7117
|
+
return fetchWithAggregator(client, balanceDefs, erc20Aggregator.address);
|
7118
|
+
}
|
7119
|
+
return fetchWithoutAggregator(client, balanceDefs);
|
7120
|
+
};
|
7121
|
+
const fetchWithoutAggregator = async (client, balanceDefs) => {
|
7122
|
+
if (balanceDefs.length === 0) return {
|
7123
|
+
success: [],
|
7124
|
+
errors: []
|
7125
|
+
};
|
7126
|
+
log.debug("fetching %s balances without aggregator", MODULE_TYPE$6, balanceDefs.length);
|
7127
|
+
const results = await Promise.allSettled(balanceDefs.map(async ({
|
7128
|
+
token,
|
7129
|
+
address
|
7130
|
+
}) => {
|
7131
|
+
try {
|
7132
|
+
const result = await client.readContract({
|
7133
|
+
abi: erc20Abi,
|
7134
|
+
address: token.contractAddress,
|
7135
|
+
functionName: "balanceOf",
|
7136
|
+
args: [address]
|
7137
|
+
});
|
7138
|
+
const balance = {
|
7139
|
+
address,
|
7140
|
+
tokenId: token.id,
|
7141
|
+
value: result.toString(),
|
7142
|
+
source: MODULE_TYPE$6,
|
7143
|
+
networkId: parseTokenId(token.id).networkId,
|
7144
|
+
status: "cache"
|
7145
|
+
};
|
7146
|
+
return balance;
|
7147
|
+
} catch (err) {
|
7148
|
+
throw new BalanceFetchError(`Failed to get balance for token ${token.id} and address ${address} on chain ${client.chain?.id}`, token.id, address, err);
|
7149
|
+
}
|
7150
|
+
}));
|
7151
|
+
return results.reduce((acc, result) => {
|
7152
|
+
if (result.status === "fulfilled") acc.success.push(result.value);else {
|
7153
|
+
const error = result.reason;
|
7154
|
+
acc.errors.push({
|
7155
|
+
tokenId: error.tokenId,
|
7156
|
+
address: error.address,
|
7157
|
+
error
|
7158
|
+
});
|
7159
|
+
}
|
7160
|
+
return acc;
|
7161
|
+
}, {
|
7162
|
+
success: [],
|
7163
|
+
errors: []
|
7164
|
+
});
|
7165
|
+
};
|
7166
|
+
const fetchWithAggregator = async (client, balanceDefs, erc20BalancesAggregatorAddress) => {
|
7167
|
+
if (balanceDefs.length === 0) return {
|
7168
|
+
success: [],
|
7169
|
+
errors: []
|
7170
|
+
};
|
7171
|
+
log.debug("fetching %s balances with aggregator", MODULE_TYPE$6, balanceDefs.length);
|
7172
|
+
try {
|
7173
|
+
const erc20Balances = await client.readContract({
|
7174
|
+
abi: erc20BalancesAggregatorAbi,
|
7175
|
+
address: erc20BalancesAggregatorAddress,
|
7176
|
+
functionName: "balances",
|
7177
|
+
args: [balanceDefs.map(b => ({
|
7178
|
+
account: b.address,
|
7179
|
+
token: b.token.contractAddress
|
7180
|
+
}))]
|
7181
|
+
});
|
7182
|
+
const success = balanceDefs.map((balanceDef, index) => ({
|
7183
|
+
address: balanceDef.address,
|
7184
|
+
tokenId: balanceDef.token.id,
|
7185
|
+
value: erc20Balances[index].toString(),
|
7186
|
+
source: MODULE_TYPE$6,
|
7187
|
+
networkId: parseTokenId(balanceDef.token.id).networkId,
|
7188
|
+
status: "cache"
|
7189
|
+
}));
|
7190
|
+
return {
|
7191
|
+
success,
|
7192
|
+
errors: []
|
7193
|
+
};
|
7194
|
+
} catch (err) {
|
7195
|
+
const errors = balanceDefs.map(balanceDef => ({
|
7196
|
+
tokenId: balanceDef.token.id,
|
7197
|
+
address: balanceDef.address,
|
7198
|
+
error: new BalanceFetchNetworkError(`Failed to get balances for evm-erc20 tokens on chain ${client.chain?.id}`, String(client.chain?.id), err)
|
7199
|
+
}));
|
7200
|
+
return {
|
7201
|
+
success: [],
|
7202
|
+
errors
|
7203
|
+
};
|
7204
|
+
}
|
7205
|
+
};
|
7206
|
+
|
7207
|
+
const getErc20ContractData = async (client, contractAddress) => {
|
7208
|
+
try {
|
7209
|
+
const contract = getTypedContract(client, erc20Abi, contractAddress);
|
7210
|
+
|
7211
|
+
// eslint-disable-next-line no-var
|
7212
|
+
var [symbol, decimals, name] = await Promise.all([contract.read.symbol(), contract.read.decimals(), contract.read.name()]);
|
7213
|
+
} catch (e) {
|
7214
|
+
if (e instanceof ContractFunctionExecutionError) {
|
7215
|
+
// try to perform the contract read with bytes32 symbol
|
7216
|
+
const contract = getTypedContract(client, erc20Abi_bytes32, contractAddress);
|
7217
|
+
|
7218
|
+
// eslint-disable-next-line no-var
|
7219
|
+
var [bytesSymbol, decimals, nameSymbol] = await Promise.all([contract.read.symbol(), contract.read.decimals(), contract.read.name()]);
|
7220
|
+
symbol = hexToString(bytesSymbol).replace(/\0/g, "").trim(); // remove NULL characters
|
7221
|
+
name = hexToString(nameSymbol).replace(/\0/g, "").trim(); // remove NULL characters
|
7222
|
+
} else throw e;
|
7223
|
+
}
|
7224
|
+
return {
|
7225
|
+
symbol,
|
7226
|
+
decimals,
|
7227
|
+
name
|
7228
|
+
};
|
7229
|
+
};
|
7230
|
+
const getTypedContract = (client, abi, contractAddress) => getContract({
|
7231
|
+
address: contractAddress,
|
7232
|
+
abi,
|
7233
|
+
client: {
|
7234
|
+
public: client
|
7235
|
+
}
|
7236
|
+
});
|
7237
|
+
const getUniswapV2PairContractData = async (client, contractAddress) => {
|
7238
|
+
const contract = getTypedContract(client, uniswapV2PairAbi, contractAddress);
|
7239
|
+
|
7240
|
+
// eslint-disable-next-line no-var
|
7241
|
+
var [token0, token1, decimals, name] = await Promise.all([contract.read.token0(), contract.read.token1(), contract.read.decimals(), contract.read.name()]);
|
7242
|
+
return {
|
7243
|
+
token0,
|
7244
|
+
token1,
|
7245
|
+
decimals,
|
7246
|
+
name
|
7247
|
+
};
|
7248
|
+
};
|
7249
|
+
|
7250
|
+
const TokenCacheSchema = EvmUniswapV2TokenSchema.pick({
|
7251
|
+
symbol: true,
|
7252
|
+
decimals: true,
|
7253
|
+
name: true,
|
7254
|
+
tokenAddress0: true,
|
7255
|
+
tokenAddress1: true,
|
7256
|
+
decimals0: true,
|
7257
|
+
decimals1: true,
|
7258
|
+
symbol0: true,
|
7259
|
+
symbol1: true
|
7260
|
+
});
|
7261
|
+
const fetchTokens$6 = async ({
|
7262
|
+
networkId,
|
7263
|
+
tokens,
|
7264
|
+
connector,
|
7265
|
+
cache
|
7266
|
+
}) => {
|
7267
|
+
const result = [];
|
7268
|
+
for (const tokenConfig of tokens) {
|
7269
|
+
const tokenId = evmUniswapV2TokenId(networkId, tokenConfig.contractAddress);
|
7270
|
+
if (!cache[tokenId] || !TokenCacheSchema.safeParse(cache[tokenId]).success) {
|
7271
|
+
const client = await connector.getPublicClientForEvmNetwork(networkId);
|
7272
|
+
if (!client) {
|
7273
|
+
log.warn(`No client found for network ${networkId} while fetching EVM ERC20 tokens`);
|
7274
|
+
continue;
|
7275
|
+
}
|
7276
|
+
try {
|
7277
|
+
const {
|
7278
|
+
token0,
|
7279
|
+
token1,
|
7280
|
+
name,
|
7281
|
+
decimals
|
7282
|
+
} = await getUniswapV2PairContractData(client, tokenConfig.contractAddress);
|
7283
|
+
const {
|
7284
|
+
symbol: symbol0,
|
7285
|
+
decimals: decimals0
|
7286
|
+
} = await getErc20ContractData(client, token0);
|
7287
|
+
const {
|
7288
|
+
symbol: symbol1,
|
7289
|
+
decimals: decimals1
|
7290
|
+
} = await getErc20ContractData(client, token1);
|
7291
|
+
cache[tokenId] = {
|
7292
|
+
id: tokenId,
|
7293
|
+
symbol: `${symbol0}/${symbol1}`,
|
7294
|
+
decimals,
|
7295
|
+
name,
|
7296
|
+
tokenAddress0: token0,
|
7297
|
+
tokenAddress1: token1,
|
7298
|
+
decimals0,
|
7299
|
+
decimals1,
|
7300
|
+
symbol0,
|
7301
|
+
symbol1
|
7302
|
+
};
|
7303
|
+
} catch (err) {
|
7304
|
+
log.warn(`Failed to fetch UniswapV2 token data for ${tokenConfig.contractAddress}`, err.shortMessage);
|
7305
|
+
continue;
|
7306
|
+
}
|
7307
|
+
}
|
7308
|
+
const base = {
|
7309
|
+
type: MODULE_TYPE$6,
|
7310
|
+
platform: PLATFORM$6,
|
7311
|
+
networkId
|
7312
|
+
};
|
7313
|
+
const token = assign(base, cache[tokenId], tokenConfig);
|
7314
|
+
const parsed = EvmUniswapV2TokenSchema.safeParse(token);
|
7315
|
+
if (!parsed.success) {
|
7316
|
+
log.warn("Ignoring token with invalid EvmErc20TokenSchema", token);
|
7317
|
+
continue;
|
7318
|
+
}
|
7319
|
+
result.push(parsed.data);
|
7320
|
+
}
|
7321
|
+
return result;
|
7322
|
+
};
|
7323
|
+
|
7324
|
+
const getMiniMetadata$6 = () => {
|
7325
|
+
throw new Error("MiniMetadata is not supported for ethereum tokens");
|
7326
|
+
};
|
7327
|
+
|
7328
|
+
const getTransferCallData$6 = ({
|
7329
|
+
from,
|
7330
|
+
to,
|
7331
|
+
value,
|
7332
|
+
token
|
7333
|
+
}) => {
|
7334
|
+
if (!isTokenOfType(token, MODULE_TYPE$6)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$6}.`);
|
7335
|
+
if (!isEthereumAddress(from)) throw new Error("Invalid from address");
|
7336
|
+
if (!isEthereumAddress(to)) throw new Error("Invalid to address");
|
7337
|
+
const data = encodeFunctionData({
|
7338
|
+
abi: erc20Abi,
|
7339
|
+
functionName: "transfer",
|
7340
|
+
args: [to, BigInt(value)]
|
7341
|
+
});
|
7342
|
+
return {
|
7343
|
+
from,
|
7344
|
+
to: token.contractAddress,
|
7345
|
+
data
|
7346
|
+
};
|
7347
|
+
};
|
7348
|
+
|
7349
|
+
const SUBSCRIPTION_INTERVAL$6 = 6_000;
|
7350
|
+
const subscribeBalances$6 = ({
|
7351
|
+
networkId,
|
7352
|
+
addressesByToken,
|
7353
|
+
connector
|
7354
|
+
}) => {
|
7355
|
+
return new Observable(subscriber => {
|
7356
|
+
const abortController = new AbortController();
|
7357
|
+
const poll = async () => {
|
7358
|
+
try {
|
7359
|
+
if (abortController.signal.aborted) return;
|
7360
|
+
const balances = await fetchBalances$6({
|
7361
|
+
networkId,
|
7362
|
+
addressesByToken,
|
7363
|
+
connector
|
7364
|
+
});
|
7365
|
+
if (abortController.signal.aborted) return;
|
7366
|
+
subscriber.next(balances);
|
7367
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$6);
|
7368
|
+
} catch (error) {
|
7369
|
+
log.error("Error", {
|
7370
|
+
module: MODULE_TYPE$6,
|
7371
|
+
networkId,
|
7372
|
+
addressesByToken,
|
7373
|
+
error
|
7374
|
+
});
|
7375
|
+
subscriber.error(error);
|
7376
|
+
}
|
7377
|
+
};
|
7378
|
+
poll();
|
7379
|
+
return () => {
|
7380
|
+
abortController.abort();
|
7381
|
+
};
|
7382
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
7383
|
+
};
|
7384
|
+
|
7385
|
+
const EvmUniswapV2BalanceModule = {
|
7386
|
+
type: MODULE_TYPE$6,
|
7387
|
+
platform: PLATFORM$6,
|
7388
|
+
getMiniMetadata: getMiniMetadata$6,
|
7389
|
+
fetchTokens: fetchTokens$6,
|
7390
|
+
fetchBalances: fetchBalances$6,
|
7391
|
+
subscribeBalances: subscribeBalances$6,
|
7392
|
+
getTransferCallData: getTransferCallData$6
|
7393
|
+
};
|
7394
|
+
|
7395
|
+
const MODULE_TYPE$5 = SubAssetsTokenSchema.shape.type.value;
|
7396
|
+
const PLATFORM$5 = SubAssetsTokenSchema.shape.platform.value;
|
7397
|
+
|
7398
|
+
// to be used by chaindata too
|
7399
|
+
z.strictObject({
|
7400
|
+
assetId: SubAssetsTokenSchema.shape.assetId,
|
7401
|
+
...TokenConfigBaseSchema.shape
|
7402
|
+
});
|
7403
|
+
|
7404
|
+
const fetchBalances$5 = async ({
|
7405
|
+
networkId,
|
7406
|
+
addressesByToken,
|
7407
|
+
connector,
|
7408
|
+
miniMetadata
|
7409
|
+
}) => {
|
7410
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
7411
|
+
if (!miniMetadata?.data) {
|
7412
|
+
log.warn("MiniMetadata is required for fetching balances");
|
7413
|
+
return {
|
7414
|
+
success: [],
|
7415
|
+
errors: balanceDefs.map(def => ({
|
7416
|
+
tokenId: def.token.id,
|
7417
|
+
address: def.address,
|
7418
|
+
error: new Error("Minimetadata is required for fetching balances")
|
7419
|
+
}))
|
7420
|
+
};
|
7421
|
+
}
|
7422
|
+
if (miniMetadata.source !== MODULE_TYPE$5) {
|
7423
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE$5}.`);
|
7424
|
+
return {
|
7425
|
+
success: [],
|
7426
|
+
errors: balanceDefs.map(def => ({
|
7427
|
+
tokenId: def.token.id,
|
7428
|
+
address: def.address,
|
7429
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE$5}`)
|
7430
|
+
}))
|
7431
|
+
};
|
7432
|
+
}
|
7433
|
+
if (miniMetadata.chainId !== networkId) {
|
7434
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE$5}. Expected chainId is ${networkId}`);
|
7435
|
+
return {
|
7436
|
+
success: [],
|
7437
|
+
errors: balanceDefs.map(def => ({
|
7438
|
+
tokenId: def.token.id,
|
7439
|
+
address: def.address,
|
7440
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
7441
|
+
}))
|
7442
|
+
};
|
7443
|
+
}
|
7444
|
+
const queries = buildQueries$2(networkId, balanceDefs, miniMetadata);
|
7445
|
+
const balances = await new RpcStateQueryHelper(connector, queries).fetch();
|
7446
|
+
return balanceDefs.reduce((acc, def) => {
|
7447
|
+
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
7448
|
+
if (balance) acc.success.push(balance);
|
7449
|
+
//if no entry consider empty balance
|
7450
|
+
else acc.success.push({
|
7451
|
+
address: def.address,
|
7452
|
+
networkId,
|
7453
|
+
tokenId: def.token.id,
|
7454
|
+
source: MODULE_TYPE$5,
|
7455
|
+
status: "live",
|
7456
|
+
values: [{
|
7457
|
+
type: "free",
|
7458
|
+
label: "free",
|
7459
|
+
amount: "0"
|
7460
|
+
}, {
|
7461
|
+
type: "locked",
|
7462
|
+
label: "frozen",
|
7463
|
+
amount: "0"
|
7464
|
+
}]
|
7465
|
+
});
|
7466
|
+
return acc;
|
7467
|
+
}, {
|
7468
|
+
success: [],
|
7469
|
+
errors: []
|
7470
|
+
});
|
7471
|
+
};
|
7472
|
+
const buildQueries$2 = (networkId, balanceDefs, miniMetadata) => {
|
7473
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7474
|
+
storage: ["Assets", "Account"]
|
7475
|
+
});
|
7476
|
+
return balanceDefs.map(({
|
7477
|
+
token,
|
7478
|
+
address
|
7479
|
+
}) => {
|
7480
|
+
const scaleCoder = networkStorageCoders?.storage;
|
7481
|
+
const stateKey = tryEncode(scaleCoder, Number(token.assetId), address) ??
|
7482
|
+
// Asset Hub
|
7483
|
+
tryEncode(scaleCoder, BigInt(token.assetId), address); // Astar
|
7484
|
+
|
7485
|
+
if (!stateKey) {
|
7486
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.assetId} / ${address}`);
|
7487
|
+
return null;
|
7488
|
+
}
|
7489
|
+
const decodeResult = change => {
|
7490
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7491
|
+
|
7492
|
+
const decoded = decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
7493
|
+
balance: 0n,
|
7494
|
+
status: {
|
7495
|
+
type: "Liquid"
|
7496
|
+
}};
|
7497
|
+
const isFrozen = decoded?.status?.type === "Frozen";
|
7498
|
+
const amount = (decoded?.balance ?? 0n).toString();
|
7499
|
+
|
7500
|
+
// due to the following balance calculations, which are made in the `Balance` type:
|
7501
|
+
//
|
7502
|
+
// total balance = (free balance) + (reserved balance)
|
7503
|
+
// transferable balance = (free balance) - (frozen balance)
|
7504
|
+
//
|
7505
|
+
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
7506
|
+
// of this balance to the value we received from the RPC.
|
7507
|
+
//
|
7508
|
+
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
7509
|
+
const free = amount;
|
7510
|
+
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
7511
|
+
|
7512
|
+
// include balance values even if zero, so that newly-zero values overwrite old values
|
7513
|
+
const balanceValues = [{
|
7514
|
+
type: "free",
|
7515
|
+
label: "free",
|
7516
|
+
amount: free.toString()
|
7517
|
+
}, {
|
7518
|
+
type: "locked",
|
7519
|
+
label: "frozen",
|
7520
|
+
amount: frozen.toString()
|
7521
|
+
}];
|
7522
|
+
const balance = {
|
7523
|
+
source: "substrate-assets",
|
7524
|
+
status: "live",
|
7525
|
+
address,
|
7526
|
+
networkId,
|
7527
|
+
tokenId: token.id,
|
7528
|
+
values: balanceValues
|
7529
|
+
};
|
7530
|
+
return balance;
|
7531
|
+
};
|
7532
|
+
return {
|
7533
|
+
chainId: networkId,
|
7534
|
+
stateKey,
|
7535
|
+
decodeResult
|
7536
|
+
};
|
7537
|
+
}).filter(isNotNil);
|
7538
|
+
};
|
7539
|
+
const tryEncode = (scaleCoder, ...args) => {
|
7540
|
+
try {
|
7541
|
+
return scaleCoder?.keys?.enc?.(...args);
|
7542
|
+
} catch {
|
7543
|
+
return null;
|
7544
|
+
}
|
7545
|
+
};
|
7546
|
+
|
7547
|
+
const fetchTokens$5 = async ({
|
7548
|
+
networkId,
|
7549
|
+
tokens,
|
7550
|
+
connector,
|
7551
|
+
miniMetadata
|
7552
|
+
}) => {
|
7553
|
+
const anyMiniMetadata = miniMetadata;
|
7554
|
+
if (!anyMiniMetadata?.data) return [];
|
7555
|
+
const {
|
7556
|
+
builder
|
7557
|
+
} = parseMetadataRpc(anyMiniMetadata.data);
|
7558
|
+
const assetCodec = builder.buildStorage("Assets", "Asset");
|
7559
|
+
const metadataCodec = builder.buildStorage("Assets", "Metadata");
|
7560
|
+
const [allAssetStorageKeys, allMetadataStorageKeys] = await Promise.all([connector.send(networkId, "state_getKeys", [getStorageKeyPrefix("Assets", "Asset")]), connector.send(networkId, "state_getKeys", [getStorageKeyPrefix("Assets", "Metadata")])]);
|
7561
|
+
const [assetStorageResults, metadataStorageResults] = await Promise.all([connector.send(networkId, "state_queryStorageAt", [allAssetStorageKeys]), connector.send(networkId, "state_queryStorageAt", [allMetadataStorageKeys])]);
|
7562
|
+
const assetStorageEntries = assetStorageResults[0].changes;
|
7563
|
+
const metadataStorageEntries = metadataStorageResults[0].changes;
|
7564
|
+
const assetByAssetId = keyBy(assetStorageEntries.map(([key, value]) => {
|
7565
|
+
const [assetId] = assetCodec.keys.dec(key);
|
7566
|
+
const asset = assetCodec.value.dec(value);
|
7567
|
+
return {
|
7568
|
+
assetId,
|
7569
|
+
existentialDeposit: asset.min_balance,
|
7570
|
+
isSufficient: asset.is_sufficient
|
7571
|
+
};
|
7572
|
+
}), a => a.assetId);
|
7573
|
+
const metadataByAssetId = keyBy(metadataStorageEntries.map(([key, value]) => {
|
7574
|
+
const [assetId] = metadataCodec.keys.dec(key);
|
7575
|
+
const metadata = metadataCodec.value.dec(value);
|
7576
|
+
return {
|
7577
|
+
assetId,
|
7578
|
+
decimals: metadata.decimals,
|
7579
|
+
isFrozen: metadata.is_frozen,
|
7580
|
+
name: metadata.name?.asText(),
|
7581
|
+
symbol: metadata.symbol?.asText()
|
7582
|
+
};
|
7583
|
+
}), a => a.assetId);
|
7584
|
+
const allTokens = keys(assetByAssetId).map(assetId => assign({}, assetByAssetId[assetId], metadataByAssetId[assetId] ?? undefined));
|
7585
|
+
const configTokenByAssetId = keyBy(tokens, t => t.assetId);
|
7586
|
+
return allTokens.map(asset => ({
|
7587
|
+
id: subAssetTokenId(networkId, String(asset.assetId)),
|
7588
|
+
type: MODULE_TYPE$5,
|
7589
|
+
platform: "polkadot",
|
7590
|
+
networkId,
|
7591
|
+
assetId: String(asset.assetId),
|
7592
|
+
isSufficient: asset.isSufficient,
|
7593
|
+
isFrozen: asset.isFrozen,
|
7594
|
+
name: asset.name,
|
7595
|
+
symbol: asset.symbol,
|
7596
|
+
decimals: asset.decimals ?? 0,
|
7597
|
+
existentialDeposit: String(asset.existentialDeposit),
|
7598
|
+
isDefault: true
|
7599
|
+
}))
|
7600
|
+
// keep all tokens listed in the config + all tokens marked as sufficient
|
7601
|
+
.filter(token => {
|
7602
|
+
const configToken = configTokenByAssetId[token.assetId];
|
7603
|
+
return configToken || token.isSufficient;
|
7604
|
+
})
|
7605
|
+
// apply config overrides
|
7606
|
+
.map(token => {
|
7607
|
+
const configToken = configTokenByAssetId[token.assetId];
|
7608
|
+
return configToken ? assign({}, token, configToken) : token;
|
7609
|
+
})
|
7610
|
+
// validate results
|
7611
|
+
.filter(t => {
|
7612
|
+
const parsed = SubAssetsTokenSchema.safeParse(t);
|
7613
|
+
if (!parsed.success) log.warn(`Ignoring invalid token ${MODULE_TYPE$5}`, t);
|
7614
|
+
return parsed.success;
|
7615
|
+
});
|
7616
|
+
};
|
7617
|
+
|
7618
|
+
const getConstantValue = (metadataRpc, pallet, constant) => {
|
7619
|
+
const {
|
7620
|
+
unifiedMetadata,
|
7621
|
+
builder
|
7622
|
+
} = parseMetadataRpc(metadataRpc);
|
7623
|
+
const codec = builder.buildConstant(pallet, constant);
|
7624
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
7625
|
+
name
|
7626
|
+
}) => name === pallet)?.constants.find(({
|
7627
|
+
name
|
7628
|
+
}) => name === constant)?.value;
|
7629
|
+
if (!encodedValue) throw new Error(`Constant ${pallet}.${constant} not found`);
|
7630
|
+
return codec.dec(encodedValue);
|
7631
|
+
};
|
7632
|
+
|
7633
|
+
const hasStorageItem = (metadata, palletName, itemName) => {
|
7634
|
+
const pallet = metadata.pallets.find(p => p.name === palletName);
|
7635
|
+
if (!pallet || !pallet.storage) return false;
|
7636
|
+
return pallet.storage.items.some(item => item.name === itemName);
|
7637
|
+
};
|
7638
|
+
const hasStorageItems = (metadata, palletName, itemNames) => {
|
7639
|
+
const pallet = metadata.pallets.find(p => p.name === palletName);
|
7640
|
+
if (!pallet || !pallet.storage) return false;
|
7641
|
+
return itemNames.every(itemName => pallet.storage?.items.some(item => item.name === itemName));
|
7642
|
+
};
|
7643
|
+
const hasRuntimeApi = (metadata, apiName, method) => {
|
7644
|
+
const api = metadata.apis.find(api => api.name === apiName);
|
7645
|
+
if (!api || !api.methods) return false;
|
7646
|
+
return api.methods.some(m => m.name === method);
|
7647
|
+
};
|
7648
|
+
|
7649
|
+
const getMiniMetadata$5 = ({
|
7650
|
+
networkId,
|
7651
|
+
specVersion,
|
7652
|
+
metadataRpc
|
7653
|
+
}) => {
|
7654
|
+
const source = MODULE_TYPE$5;
|
7655
|
+
const chainId = networkId;
|
7656
|
+
const systemVersion = getConstantValue(metadataRpc, "System", "Version");
|
7657
|
+
if (specVersion !== systemVersion.spec_version) log.warn("specVersion mismatch", {
|
7658
|
+
networkId,
|
7659
|
+
specVersion,
|
7660
|
+
systemVersion
|
7661
|
+
});
|
7662
|
+
const id = deriveMiniMetadataId({
|
7663
|
+
source,
|
7664
|
+
chainId,
|
7665
|
+
specVersion
|
7666
|
+
});
|
7667
|
+
const {
|
7668
|
+
unifiedMetadata
|
7669
|
+
} = parseMetadataRpc(metadataRpc);
|
7670
|
+
if (unifiedMetadata.version < 14) throw new Error(`Unsupported metadata version: ${unifiedMetadata.version}. Minimum required is 14.`);
|
7671
|
+
return {
|
7672
|
+
id,
|
7673
|
+
source,
|
7674
|
+
chainId,
|
7675
|
+
specVersion,
|
7676
|
+
version: MINIMETADATA_VERSION,
|
7677
|
+
data: getData$3(metadataRpc),
|
7678
|
+
extra: null
|
7679
|
+
};
|
7680
|
+
};
|
7681
|
+
const getData$3 = metadataRpc => {
|
7682
|
+
const {
|
7683
|
+
metadata,
|
7684
|
+
unifiedMetadata
|
7685
|
+
} = parseMetadataRpc(metadataRpc);
|
7686
|
+
|
7687
|
+
// ensure the network has all the required bits
|
7688
|
+
if (!hasStorageItems(unifiedMetadata, "Assets", ["Account", "Asset", "Metadata"])) return null;
|
7689
|
+
compactMetadata(metadata, [{
|
7690
|
+
pallet: "Assets",
|
7691
|
+
items: ["Account", "Asset", "Metadata"]
|
7692
|
+
}]);
|
7693
|
+
return encodeMetadata(metadata);
|
7694
|
+
};
|
7695
|
+
|
7696
|
+
const getTransferCallData$5 = ({
|
7697
|
+
from,
|
7698
|
+
to,
|
7699
|
+
value,
|
7700
|
+
token,
|
7701
|
+
type,
|
7702
|
+
metadataRpc
|
7703
|
+
}) => {
|
7704
|
+
if (!isTokenOfType(token, MODULE_TYPE$5)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$5}.`);
|
7705
|
+
const {
|
7706
|
+
builder
|
7707
|
+
} = parseMetadataRpc(metadataRpc);
|
7708
|
+
const method = getTransferMethod$3(type);
|
7709
|
+
const {
|
7710
|
+
codec,
|
7711
|
+
location
|
7712
|
+
} = builder.buildCall("Assets", method);
|
7713
|
+
const args = getEncodedArgs$2(method, token.assetId, to, value, codec);
|
7714
|
+
const callData = Binary.fromBytes(mergeUint8([new Uint8Array(location), args]));
|
7715
|
+
return {
|
7716
|
+
address: from,
|
7717
|
+
method: callData.asHex()
|
7718
|
+
};
|
7719
|
+
};
|
7720
|
+
const getTransferMethod$3 = type => {
|
7721
|
+
switch (type) {
|
7722
|
+
case "keep-alive":
|
7723
|
+
return "transfer_keep_alive";
|
7724
|
+
case "all":
|
7725
|
+
return "transfer_all";
|
7726
|
+
case "allow-death":
|
7727
|
+
return "transfer";
|
7728
|
+
}
|
7729
|
+
};
|
7730
|
+
const getEncodedArgs$2 = (method, assetId, to, value, argsCodec) => {
|
7731
|
+
try {
|
7732
|
+
switch (method) {
|
7733
|
+
case "transfer_keep_alive":
|
7734
|
+
case "transfer":
|
7735
|
+
return getTransferEncodedArgs$2(assetId, to, value, argsCodec);
|
7736
|
+
case "transfer_all":
|
7737
|
+
return getTransferAllEncodedArgs$2(assetId, to, argsCodec);
|
7738
|
+
}
|
7739
|
+
} catch {
|
7740
|
+
throw new Error(`Failed to encode arguments for method ${method}: ${assetId}, ${to}, ${value}`);
|
7741
|
+
}
|
7742
|
+
};
|
7743
|
+
const getEncodedValue$2 = (codec, possibleValue) => {
|
7744
|
+
for (const getArgs of possibleValue) {
|
7745
|
+
try {
|
7746
|
+
return codec.enc(getArgs());
|
7747
|
+
} catch (error) {
|
7748
|
+
// wrong inputs, ignore and try the next one
|
7749
|
+
}
|
7750
|
+
}
|
7751
|
+
throw new Error("Failed to encode");
|
7752
|
+
};
|
7753
|
+
|
7754
|
+
// same inputs for both KeepAlive and allowDeath
|
7755
|
+
const getTransferEncodedArgs$2 = (assetId, to, value, codec) => {
|
7756
|
+
return getEncodedValue$2(codec, [() => ({
|
7757
|
+
id: Number(assetId),
|
7758
|
+
// for most networks
|
7759
|
+
target: Enum("Id", to),
|
7760
|
+
amount: BigInt(value)
|
7761
|
+
}), () => ({
|
7762
|
+
id: BigInt(assetId),
|
7763
|
+
// for Astar
|
7764
|
+
target: Enum("Id", to),
|
7765
|
+
amount: BigInt(value)
|
7766
|
+
})]);
|
7767
|
+
};
|
7768
|
+
const getTransferAllEncodedArgs$2 = (assetId, to, codec) => {
|
7769
|
+
return getEncodedValue$2(codec, [() => ({
|
7770
|
+
id: Number(assetId),
|
7771
|
+
// for most networks
|
7772
|
+
target: Enum("Id", to),
|
7773
|
+
keep_alive: false
|
7774
|
+
}), () => ({
|
7775
|
+
id: BigInt(assetId),
|
7776
|
+
// for Astar
|
7777
|
+
target: Enum("Id", to),
|
7778
|
+
keep_alive: false
|
7779
|
+
})]);
|
7780
|
+
};
|
7781
|
+
|
7782
|
+
const SUBSCRIPTION_INTERVAL$5 = 6_000;
|
7783
|
+
const subscribeBalances$5 = ({
|
7784
|
+
networkId,
|
7785
|
+
addressesByToken,
|
7786
|
+
connector,
|
7787
|
+
miniMetadata
|
7788
|
+
}) => {
|
7789
|
+
return new Observable(subscriber => {
|
7790
|
+
const abortController = new AbortController();
|
7791
|
+
|
7792
|
+
// on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
|
7793
|
+
// => poll values for each block
|
7794
|
+
const poll = async () => {
|
7795
|
+
try {
|
7796
|
+
if (abortController.signal.aborted) return;
|
7797
|
+
const balances = await fetchBalances$5({
|
7798
|
+
networkId,
|
7799
|
+
addressesByToken,
|
7800
|
+
connector,
|
7801
|
+
miniMetadata
|
7802
|
+
});
|
7803
|
+
if (abortController.signal.aborted) return;
|
7804
|
+
subscriber.next(balances);
|
7805
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$5);
|
7806
|
+
} catch (error) {
|
7807
|
+
log.error("Error", {
|
7808
|
+
module: MODULE_TYPE$5,
|
7809
|
+
networkId,
|
7810
|
+
miniMetadata,
|
7811
|
+
addressesByToken,
|
7812
|
+
error
|
7813
|
+
});
|
7814
|
+
subscriber.error(error);
|
7815
|
+
}
|
7816
|
+
};
|
7817
|
+
poll();
|
7818
|
+
return () => {
|
7819
|
+
abortController.abort();
|
7820
|
+
};
|
7821
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
7822
|
+
};
|
7823
|
+
|
7824
|
+
const SubAssetsBalanceModule = {
|
7825
|
+
type: MODULE_TYPE$5,
|
7826
|
+
platform: PLATFORM$5,
|
7827
|
+
getMiniMetadata: getMiniMetadata$5,
|
7828
|
+
fetchTokens: fetchTokens$5,
|
7829
|
+
fetchBalances: fetchBalances$5,
|
7830
|
+
subscribeBalances: subscribeBalances$5,
|
7831
|
+
getTransferCallData: getTransferCallData$5
|
7832
|
+
};
|
7833
|
+
|
7834
|
+
const MODULE_TYPE$4 = SubForeignAssetsTokenSchema.shape.type.value;
|
7835
|
+
const PLATFORM$4 = SubForeignAssetsTokenSchema.shape.platform.value;
|
7836
|
+
|
7837
|
+
// to be used by chaindata too
|
7838
|
+
z.strictObject({
|
7839
|
+
onChainId: SubForeignAssetsTokenSchema.shape.onChainId,
|
7840
|
+
...TokenConfigBaseSchema.shape
|
7841
|
+
});
|
7842
|
+
|
7843
|
+
const fetchBalances$4 = async ({
|
7844
|
+
networkId,
|
7845
|
+
addressesByToken,
|
7846
|
+
connector,
|
7847
|
+
miniMetadata
|
7848
|
+
}) => {
|
7849
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
7850
|
+
if (!miniMetadata?.data) {
|
7851
|
+
log.warn("MiniMetadata is required for fetching balances");
|
7852
|
+
return {
|
7853
|
+
success: [],
|
7854
|
+
errors: balanceDefs.map(def => ({
|
7855
|
+
tokenId: def.token.id,
|
7856
|
+
address: def.address,
|
7857
|
+
error: new Error("Minimetadata is required for fetching balances")
|
7858
|
+
}))
|
7859
|
+
};
|
7860
|
+
}
|
7861
|
+
if (miniMetadata.source !== MODULE_TYPE$4) {
|
7862
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE$4}.`);
|
7863
|
+
return {
|
7864
|
+
success: [],
|
7865
|
+
errors: balanceDefs.map(def => ({
|
7866
|
+
tokenId: def.token.id,
|
7867
|
+
address: def.address,
|
7868
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE$4}`)
|
7869
|
+
}))
|
7870
|
+
};
|
7871
|
+
}
|
7872
|
+
if (miniMetadata.chainId !== networkId) {
|
7873
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE$4}. Expected chainId is ${networkId}`);
|
7874
|
+
return {
|
7875
|
+
success: [],
|
7876
|
+
errors: balanceDefs.map(def => ({
|
7877
|
+
tokenId: def.token.id,
|
7878
|
+
address: def.address,
|
7879
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
7880
|
+
}))
|
7881
|
+
};
|
7882
|
+
}
|
7883
|
+
const queries = buildQueries$1(networkId, balanceDefs, miniMetadata);
|
7884
|
+
const balances = await new RpcStateQueryHelper(connector, queries).fetch();
|
7885
|
+
return balanceDefs.reduce((acc, def) => {
|
7886
|
+
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
7887
|
+
if (balance) acc.success.push(balance);
|
7888
|
+
//if no entry consider empty balance
|
7889
|
+
else acc.success.push({
|
7890
|
+
address: def.address,
|
7891
|
+
networkId,
|
7892
|
+
tokenId: def.token.id,
|
7893
|
+
source: MODULE_TYPE$4,
|
7894
|
+
status: "live",
|
7895
|
+
values: [{
|
7896
|
+
type: "free",
|
7897
|
+
label: "free",
|
7898
|
+
amount: "0"
|
7899
|
+
}, {
|
7900
|
+
type: "locked",
|
7901
|
+
label: "frozen",
|
7902
|
+
amount: "0"
|
7903
|
+
}]
|
7904
|
+
});
|
7905
|
+
return acc;
|
7906
|
+
}, {
|
7907
|
+
success: [],
|
7908
|
+
errors: []
|
7909
|
+
});
|
7910
|
+
};
|
7911
|
+
const buildQueries$1 = (networkId, balanceDefs, miniMetadata) => {
|
7912
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
7913
|
+
storage: ["ForeignAssets", "Account"]
|
7914
|
+
});
|
7915
|
+
return balanceDefs.map(({
|
7916
|
+
token,
|
7917
|
+
address
|
7918
|
+
}) => {
|
7919
|
+
const scaleCoder = networkStorageCoders?.storage;
|
7920
|
+
const getStateKey = onChainId => {
|
7921
|
+
try {
|
7922
|
+
return scaleCoder?.keys?.enc?.(papiParse(onChainId), address);
|
7923
|
+
} catch {
|
7924
|
+
return null;
|
7925
|
+
}
|
7926
|
+
};
|
7927
|
+
const stateKey = getStateKey(token.onChainId);
|
7928
|
+
if (!stateKey) {
|
7929
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
7930
|
+
return null;
|
7931
|
+
}
|
7932
|
+
const decodeResult = change => {
|
7933
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
7934
|
+
|
7935
|
+
const decoded = decodeScale(scaleCoder, change, `Failed to decode substrate-assets balance on chain ${networkId}`) ?? {
|
7936
|
+
balance: 0n,
|
7937
|
+
is_frozen: false,
|
7938
|
+
status: {
|
7939
|
+
type: "Liquid"
|
7940
|
+
}};
|
7941
|
+
const isFrozen = decoded.is_frozen ?? decoded?.status?.type === "Frozen";
|
7942
|
+
const amount = (decoded?.balance ?? 0n).toString();
|
7943
|
+
|
7944
|
+
// due to the following balance calculations, which are made in the `Balance` type:
|
7945
|
+
//
|
7946
|
+
// total balance = (free balance) + (reserved balance)
|
7947
|
+
// transferable balance = (free balance) - (frozen balance)
|
7948
|
+
//
|
7949
|
+
// when `isFrozen` is true we need to set **both** the `free` and `frozen` amounts
|
7950
|
+
// of this balance to the value we received from the RPC.
|
7951
|
+
//
|
7952
|
+
// if we only set the `frozen` amount, then the `total` calculation will be incorrect!
|
7953
|
+
const free = amount;
|
7954
|
+
const frozen = token.isFrozen || isFrozen ? amount : "0";
|
7955
|
+
|
7956
|
+
// include balance values even if zero, so that newly-zero values overwrite old values
|
7957
|
+
const balanceValues = [{
|
7958
|
+
type: "free",
|
7959
|
+
label: "free",
|
7960
|
+
amount: free.toString()
|
7961
|
+
}, {
|
7962
|
+
type: "locked",
|
7963
|
+
label: "frozen",
|
7964
|
+
amount: frozen.toString()
|
7965
|
+
}];
|
7966
|
+
const balance = {
|
7967
|
+
source: "substrate-assets",
|
7968
|
+
status: "live",
|
7969
|
+
address,
|
7970
|
+
networkId,
|
7971
|
+
tokenId: token.id,
|
7972
|
+
values: balanceValues
|
7973
|
+
};
|
7974
|
+
return balance;
|
7975
|
+
};
|
7976
|
+
return {
|
7977
|
+
chainId: networkId,
|
7978
|
+
stateKey,
|
7979
|
+
decodeResult
|
7980
|
+
};
|
7981
|
+
}).filter(isNotNil);
|
7982
|
+
};
|
7983
|
+
|
7984
|
+
const fetchTokens$4 = async ({
|
7985
|
+
networkId,
|
7986
|
+
tokens,
|
7987
|
+
connector,
|
7988
|
+
miniMetadata
|
7989
|
+
}) => {
|
7990
|
+
const anyMiniMetadata = miniMetadata;
|
7991
|
+
if (!anyMiniMetadata?.data) return [];
|
7992
|
+
const {
|
7993
|
+
builder
|
7994
|
+
} = parseMetadataRpc(anyMiniMetadata.data);
|
7995
|
+
const assetCodec = builder.buildStorage("ForeignAssets", "Asset");
|
7996
|
+
const metadataCodec = builder.buildStorage("ForeignAssets", "Metadata");
|
7997
|
+
const [allAssetStorageKeys, allMetadataStorageKeys] = await Promise.all([connector.send(networkId, "state_getKeys", [getStorageKeyPrefix("ForeignAssets", "Asset")]), connector.send(networkId, "state_getKeys", [getStorageKeyPrefix("ForeignAssets", "Metadata")])]);
|
7998
|
+
const [assetStorageResults, metadataStorageResults] = await Promise.all([connector.send(networkId, "state_queryStorageAt", [allAssetStorageKeys]), connector.send(networkId, "state_queryStorageAt", [allMetadataStorageKeys])]);
|
7999
|
+
const assetStorageEntries = assetStorageResults[0].changes;
|
8000
|
+
const metadataStorageEntries = metadataStorageResults[0].changes;
|
8001
|
+
const assetByOnChainId = keyBy(assetStorageEntries.map(([key, value]) => {
|
8002
|
+
const [decodedKey] = assetCodec.keys.dec(key);
|
8003
|
+
const onChainId = papiStringify(decodedKey);
|
8004
|
+
const asset = assetCodec.value.dec(value);
|
8005
|
+
return {
|
8006
|
+
onChainId,
|
8007
|
+
existentialDeposit: asset.min_balance,
|
8008
|
+
isSufficient: asset.is_sufficient
|
8009
|
+
};
|
8010
|
+
}), a => a.onChainId);
|
8011
|
+
const metadataByOnChainId = keyBy(metadataStorageEntries.map(([key, value]) => {
|
8012
|
+
const [decodedKey] = metadataCodec.keys.dec(key);
|
8013
|
+
const onChainId = papiStringify(decodedKey);
|
8014
|
+
const metadata = metadataCodec.value.dec(value);
|
8015
|
+
return {
|
8016
|
+
onChainId,
|
8017
|
+
decimals: metadata.decimals,
|
8018
|
+
isFrozen: metadata.is_frozen,
|
8019
|
+
name: metadata.name?.asText(),
|
8020
|
+
symbol: metadata.symbol?.asText()
|
8021
|
+
};
|
8022
|
+
}), a => a.onChainId);
|
8023
|
+
const allTokens = keys(assetByOnChainId).map(onChainId => assign({}, assetByOnChainId[onChainId], metadataByOnChainId[onChainId] ?? undefined));
|
8024
|
+
const configTokenByOnChainId = keyBy(tokens, t => t.onChainId);
|
8025
|
+
return allTokens.map(asset => ({
|
8026
|
+
id: subForeignAssetTokenId(networkId, asset.onChainId),
|
8027
|
+
type: MODULE_TYPE$4,
|
8028
|
+
platform: "polkadot",
|
8029
|
+
networkId,
|
8030
|
+
onChainId: String(asset.onChainId),
|
8031
|
+
isSufficient: asset.isSufficient,
|
8032
|
+
isFrozen: asset.isFrozen,
|
8033
|
+
name: asset.name,
|
8034
|
+
symbol: asset.symbol,
|
8035
|
+
decimals: asset.decimals ?? 0,
|
8036
|
+
existentialDeposit: String(asset.existentialDeposit),
|
8037
|
+
isDefault: true
|
8038
|
+
}))
|
8039
|
+
// keep all tokens listed in the config + all tokens marked as sufficient
|
8040
|
+
.filter(token => {
|
8041
|
+
const configToken = configTokenByOnChainId[token.onChainId];
|
8042
|
+
return configToken || token.isSufficient;
|
8043
|
+
})
|
8044
|
+
// apply config overrides
|
8045
|
+
.map(token => {
|
8046
|
+
const configToken = configTokenByOnChainId[token.onChainId];
|
8047
|
+
return configToken ? assign({}, token, configToken) : token;
|
8048
|
+
})
|
8049
|
+
// validate results
|
8050
|
+
.filter(t => {
|
8051
|
+
const parsed = SubForeignAssetsTokenSchema.safeParse(t);
|
8052
|
+
if (!parsed.success) log.warn(`Ignoring invalid token ${MODULE_TYPE$4}`, t);
|
8053
|
+
return parsed.success;
|
8054
|
+
});
|
8055
|
+
};
|
8056
|
+
|
8057
|
+
const getMiniMetadata$4 = ({
|
8058
|
+
networkId,
|
8059
|
+
specVersion,
|
8060
|
+
metadataRpc
|
8061
|
+
}) => {
|
8062
|
+
const source = MODULE_TYPE$4;
|
8063
|
+
const chainId = networkId;
|
8064
|
+
const systemVersion = getConstantValue(metadataRpc, "System", "Version");
|
8065
|
+
if (specVersion !== systemVersion.spec_version) log.warn("specVersion mismatch", {
|
8066
|
+
networkId,
|
8067
|
+
specVersion,
|
8068
|
+
systemVersion
|
8069
|
+
});
|
8070
|
+
const id = deriveMiniMetadataId({
|
8071
|
+
source,
|
8072
|
+
chainId,
|
8073
|
+
specVersion
|
8074
|
+
});
|
8075
|
+
const {
|
8076
|
+
unifiedMetadata
|
8077
|
+
} = parseMetadataRpc(metadataRpc);
|
8078
|
+
if (unifiedMetadata.version < 14) throw new Error(`Unsupported metadata version: ${unifiedMetadata.version}. Minimum required is 14.`);
|
8079
|
+
return {
|
8080
|
+
id,
|
8081
|
+
source,
|
8082
|
+
chainId,
|
8083
|
+
specVersion,
|
8084
|
+
version: MINIMETADATA_VERSION,
|
8085
|
+
data: getData$2(metadataRpc),
|
8086
|
+
extra: null
|
8087
|
+
};
|
8088
|
+
};
|
8089
|
+
const getData$2 = metadataRpc => {
|
8090
|
+
const {
|
8091
|
+
metadata,
|
8092
|
+
unifiedMetadata
|
8093
|
+
} = parseMetadataRpc(metadataRpc);
|
8094
|
+
|
8095
|
+
// ensure the network has all the required bits
|
8096
|
+
if (!hasStorageItems(unifiedMetadata, "ForeignAssets", ["Account", "Asset", "Metadata"])) return null;
|
8097
|
+
compactMetadata(metadata, [{
|
8098
|
+
pallet: "ForeignAssets",
|
8099
|
+
items: ["Account", "Asset", "Metadata"]
|
8100
|
+
}]);
|
8101
|
+
return encodeMetadata(metadata);
|
8102
|
+
};
|
8103
|
+
|
8104
|
+
const getTransferCallData$4 = ({
|
8105
|
+
from,
|
8106
|
+
to,
|
8107
|
+
value,
|
8108
|
+
token,
|
8109
|
+
type,
|
8110
|
+
metadataRpc
|
8111
|
+
}) => {
|
8112
|
+
if (!isTokenOfType(token, MODULE_TYPE$4)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$4}.`);
|
8113
|
+
const {
|
8114
|
+
builder
|
8115
|
+
} = parseMetadataRpc(metadataRpc);
|
8116
|
+
const method = getTransferMethod$2(type);
|
8117
|
+
const {
|
8118
|
+
codec,
|
8119
|
+
location
|
8120
|
+
} = builder.buildCall("ForeignAssets", method);
|
8121
|
+
const args = getEncodedArgs$1(method, token.onChainId, to, value, codec);
|
8122
|
+
const callData = Binary.fromBytes(mergeUint8([new Uint8Array(location), args]));
|
8123
|
+
return {
|
8124
|
+
address: from,
|
8125
|
+
method: callData.asHex()
|
8126
|
+
};
|
8127
|
+
};
|
8128
|
+
const getTransferMethod$2 = type => {
|
8129
|
+
switch (type) {
|
8130
|
+
case "keep-alive":
|
8131
|
+
return "transfer_keep_alive";
|
8132
|
+
case "all":
|
8133
|
+
return "transfer_all";
|
8134
|
+
case "allow-death":
|
8135
|
+
return "transfer";
|
8136
|
+
}
|
8137
|
+
};
|
8138
|
+
const getEncodedArgs$1 = (method, onChainId, to, value, argsCodec) => {
|
8139
|
+
try {
|
8140
|
+
switch (method) {
|
8141
|
+
case "transfer_keep_alive":
|
8142
|
+
case "transfer":
|
8143
|
+
return getTransferEncodedArgs$1(onChainId, to, value, argsCodec);
|
8144
|
+
case "transfer_all":
|
8145
|
+
return getTransferAllEncodedArgs$1(onChainId, to, argsCodec);
|
8146
|
+
}
|
8147
|
+
} catch {
|
8148
|
+
throw new Error(`Failed to encode arguments for method ${method}: ${onChainId}, ${to}, ${value}`);
|
8149
|
+
}
|
8150
|
+
};
|
8151
|
+
const getEncodedValue$1 = (codec, possibleValue) => {
|
8152
|
+
for (const getArgs of possibleValue) {
|
8153
|
+
try {
|
8154
|
+
return codec.enc(getArgs());
|
8155
|
+
} catch (error) {
|
8156
|
+
// wrong inputs, ignore and try the next one
|
8157
|
+
}
|
8158
|
+
}
|
8159
|
+
throw new Error("Failed to encode");
|
8160
|
+
};
|
8161
|
+
|
8162
|
+
// same inputs for both KeepAlive and allowDeath
|
8163
|
+
const getTransferEncodedArgs$1 = (onChainId, to, value, codec) => {
|
8164
|
+
return getEncodedValue$1(codec, [() => ({
|
8165
|
+
id: papiParse(onChainId),
|
8166
|
+
// for most networks
|
8167
|
+
target: Enum("Id", to),
|
8168
|
+
amount: BigInt(value)
|
8169
|
+
})]);
|
8170
|
+
};
|
8171
|
+
const getTransferAllEncodedArgs$1 = (onChainId, to, codec) => {
|
8172
|
+
return getEncodedValue$1(codec, [() => ({
|
8173
|
+
id: papiParse(onChainId),
|
8174
|
+
// for most networks
|
8175
|
+
target: Enum("Id", to),
|
8176
|
+
keep_alive: false
|
8177
|
+
})]);
|
8178
|
+
};
|
8179
|
+
|
8180
|
+
const SUBSCRIPTION_INTERVAL$4 = 6_000;
|
8181
|
+
const subscribeBalances$4 = ({
|
8182
|
+
networkId,
|
8183
|
+
addressesByToken,
|
8184
|
+
connector,
|
8185
|
+
miniMetadata
|
8186
|
+
}) => {
|
8187
|
+
return new Observable(subscriber => {
|
8188
|
+
const abortController = new AbortController();
|
8189
|
+
|
8190
|
+
// on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
|
8191
|
+
// => poll values for each block
|
8192
|
+
const poll = async () => {
|
8193
|
+
try {
|
8194
|
+
if (abortController.signal.aborted) return;
|
8195
|
+
const balances = await fetchBalances$4({
|
8196
|
+
networkId,
|
8197
|
+
addressesByToken,
|
8198
|
+
connector,
|
8199
|
+
miniMetadata
|
8200
|
+
});
|
8201
|
+
if (abortController.signal.aborted) return;
|
8202
|
+
subscriber.next(balances);
|
8203
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$4);
|
8204
|
+
} catch (error) {
|
8205
|
+
log.error("Error", {
|
8206
|
+
module: MODULE_TYPE$4,
|
8207
|
+
networkId,
|
8208
|
+
miniMetadata,
|
8209
|
+
addressesByToken,
|
8210
|
+
error
|
8211
|
+
});
|
8212
|
+
subscriber.error(error);
|
8213
|
+
}
|
8214
|
+
};
|
8215
|
+
poll();
|
8216
|
+
return () => {
|
8217
|
+
abortController.abort();
|
8218
|
+
};
|
8219
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
8220
|
+
};
|
8221
|
+
|
8222
|
+
const SubForeignAssetsBalanceModule = {
|
8223
|
+
type: MODULE_TYPE$4,
|
8224
|
+
platform: PLATFORM$4,
|
8225
|
+
getMiniMetadata: getMiniMetadata$4,
|
8226
|
+
fetchTokens: fetchTokens$4,
|
8227
|
+
fetchBalances: fetchBalances$4,
|
8228
|
+
subscribeBalances: subscribeBalances$4,
|
8229
|
+
getTransferCallData: getTransferCallData$4
|
8230
|
+
};
|
8231
|
+
|
8232
|
+
// to be used by chaindata too
|
8233
|
+
z.strictObject({
|
8234
|
+
onChainId: SubHydrationTokenSchema.shape.onChainId,
|
8235
|
+
...TokenConfigBaseSchema.shape
|
8236
|
+
});
|
8237
|
+
|
8238
|
+
const MODULE_TYPE$3 = SubHydrationTokenSchema.shape.type.value;
|
8239
|
+
const PLATFORM$3 = SubHydrationTokenSchema.shape.platform.value;
|
8240
|
+
|
8241
|
+
const fetchRuntimeCallResult = async (connector, networkId, metadataRpc, apiName, method, args) => {
|
8242
|
+
const {
|
8243
|
+
builder
|
8244
|
+
} = parseMetadataRpc(metadataRpc);
|
8245
|
+
const call = builder.buildRuntimeCall(apiName, method);
|
8246
|
+
const hex = await connector.send(networkId, "state_call", [`${apiName}_${method}`, toHex$1(call.args.enc(args))]);
|
8247
|
+
return call.value.dec(hex);
|
8248
|
+
};
|
8249
|
+
|
8250
|
+
const tryGetConstantValue = (metadataRpc, pallet, constant) => {
|
8251
|
+
const {
|
8252
|
+
unifiedMetadata,
|
8253
|
+
builder
|
8254
|
+
} = parseMetadataRpc(metadataRpc);
|
8255
|
+
const encodedValue = unifiedMetadata.pallets.find(({
|
8256
|
+
name
|
8257
|
+
}) => name === pallet)?.constants.find(({
|
8258
|
+
name
|
8259
|
+
}) => name === constant)?.value;
|
8260
|
+
if (!encodedValue) return null;
|
8261
|
+
const codec = builder.buildConstant(pallet, constant);
|
8262
|
+
return codec.dec(encodedValue);
|
8263
|
+
};
|
8264
|
+
|
8265
|
+
const fetchBalances$3 = async ({
|
8266
|
+
networkId,
|
8267
|
+
addressesByToken,
|
8268
|
+
connector,
|
8269
|
+
miniMetadata
|
8270
|
+
}) => {
|
8271
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
8272
|
+
if (!miniMetadata?.data) {
|
8273
|
+
log.warn("MiniMetadata is required for fetching balances");
|
8274
|
+
return {
|
8275
|
+
success: [],
|
8276
|
+
errors: balanceDefs.map(def => ({
|
8277
|
+
tokenId: def.token.id,
|
8278
|
+
address: def.address,
|
8279
|
+
error: new Error("Minimetadata is required for fetching balances")
|
8280
|
+
}))
|
8281
|
+
};
|
8282
|
+
}
|
8283
|
+
if (miniMetadata.source !== MODULE_TYPE$3) {
|
8284
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE$3}.`);
|
8285
|
+
return {
|
8286
|
+
success: [],
|
8287
|
+
errors: balanceDefs.map(def => ({
|
8288
|
+
tokenId: def.token.id,
|
8289
|
+
address: def.address,
|
8290
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE$3}`)
|
8291
|
+
}))
|
8292
|
+
};
|
8293
|
+
}
|
8294
|
+
if (miniMetadata.chainId !== networkId) {
|
8295
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE$3}. Expected chainId is ${networkId}`);
|
8296
|
+
return {
|
8297
|
+
success: [],
|
8298
|
+
errors: balanceDefs.map(def => ({
|
8299
|
+
tokenId: def.token.id,
|
8300
|
+
address: def.address,
|
8301
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
8302
|
+
}))
|
8303
|
+
};
|
8304
|
+
}
|
8305
|
+
const addresses = uniq(balanceDefs.map(def => def.address));
|
8306
|
+
try {
|
8307
|
+
const res = await Promise.all(addresses.map(address => fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "CurrenciesApi", "accounts", [address])));
|
8308
|
+
const fetchedBalances = addresses.flatMap((address, index) => {
|
8309
|
+
return res[index].map(([onChainId, balance]) => ({
|
8310
|
+
address,
|
8311
|
+
onChainId,
|
8312
|
+
free: balance.free.toString(),
|
8313
|
+
reserved: balance.reserved.toString(),
|
8314
|
+
frozen: balance.frozen.toString()
|
8315
|
+
})).filter(b => b.onChainId !== undefined);
|
8316
|
+
});
|
8317
|
+
const balancesByKey = keyBy(fetchedBalances, b => `${b.address}:${b.onChainId}`);
|
8318
|
+
const success = addressesByToken.reduce((acc, [token, addresses]) => {
|
8319
|
+
if (token.type === MODULE_TYPE$3) for (const address of addresses) {
|
8320
|
+
const rawBalance = balancesByKey[`${address}:${token.onChainId}`];
|
8321
|
+
|
8322
|
+
// the endpoint only returns entries for which the address has a non-zero balance
|
8323
|
+
// => generate an zero balance object if not found
|
8324
|
+
const balance = {
|
8325
|
+
address,
|
8326
|
+
networkId,
|
8327
|
+
tokenId: token.id,
|
8328
|
+
source: MODULE_TYPE$3,
|
8329
|
+
status: "cache",
|
8330
|
+
values: [{
|
8331
|
+
type: "free",
|
8332
|
+
label: "free",
|
8333
|
+
amount: rawBalance?.free.toString() ?? "0"
|
8334
|
+
}, {
|
8335
|
+
type: "reserved",
|
8336
|
+
label: "reserved",
|
8337
|
+
amount: rawBalance?.reserved.toString() ?? "0"
|
8338
|
+
}, {
|
8339
|
+
type: "locked",
|
8340
|
+
label: "frozen",
|
8341
|
+
amount: rawBalance?.frozen.toString() ?? "0"
|
8342
|
+
}]
|
8343
|
+
};
|
8344
|
+
acc.push(balance);
|
8345
|
+
}
|
8346
|
+
return acc;
|
8347
|
+
}, []);
|
8348
|
+
return {
|
8349
|
+
success,
|
8350
|
+
errors: []
|
8351
|
+
};
|
8352
|
+
} catch (err) {
|
8353
|
+
log.warn("Failed to fetch balances for substrate-hydration", err);
|
8354
|
+
const errors = balanceDefs.map(def => ({
|
8355
|
+
tokenId: def.token.id,
|
8356
|
+
address: def.address,
|
8357
|
+
error: new Error(`Failed to fetch balance for ${def.address} on ${networkId}`)
|
8358
|
+
}));
|
8359
|
+
return {
|
8360
|
+
success: [],
|
8361
|
+
errors
|
8362
|
+
};
|
8363
|
+
}
|
8364
|
+
};
|
8365
|
+
|
8366
|
+
const fetchTokens$3 = async ({
|
8367
|
+
networkId,
|
8368
|
+
tokens,
|
8369
|
+
connector,
|
8370
|
+
miniMetadata
|
8371
|
+
}) => {
|
8372
|
+
const anyMiniMetadata = miniMetadata;
|
8373
|
+
if (!anyMiniMetadata?.data) return [];
|
8374
|
+
const {
|
8375
|
+
builder
|
8376
|
+
} = parseMetadataRpc(anyMiniMetadata.data);
|
8377
|
+
const assetsCodec = builder.buildStorage("AssetRegistry", "Assets");
|
8378
|
+
const allAssetStorageKeys = await connector.send(networkId, "state_getKeys", [getStorageKeyPrefix("AssetRegistry", "Assets")]);
|
8379
|
+
const assetStorageResults = await connector.send(networkId, "state_queryStorageAt", [allAssetStorageKeys]);
|
8380
|
+
const assetStorageEntries = assetStorageResults[0].changes;
|
8381
|
+
const configTokenByAssetId = keyBy(tokens, t => t.onChainId);
|
8382
|
+
return assetStorageEntries.map(([key, value]) => {
|
8383
|
+
// parse results
|
8384
|
+
const [onChainId] = assetsCodec.keys.dec(key);
|
8385
|
+
const asset = assetsCodec.value.dec(value);
|
8386
|
+
return {
|
8387
|
+
onChainId,
|
8388
|
+
assetType: asset.asset_type.type,
|
8389
|
+
isSufficient: asset.is_sufficient,
|
8390
|
+
name: asset.name?.asText(),
|
8391
|
+
symbol: asset.symbol?.asText(),
|
8392
|
+
decimals: asset.decimals,
|
8393
|
+
existentialDeposit: asset.existential_deposit.toString()
|
8394
|
+
};
|
8395
|
+
})
|
8396
|
+
// exclude unsupported asset types
|
8397
|
+
.filter(({
|
8398
|
+
assetType
|
8399
|
+
}) => ["Erc20", "Token"].includes(assetType))
|
8400
|
+
// convert asset to a SubHydrationToken
|
8401
|
+
.map(asset => ({
|
8402
|
+
id: subHydrationTokenId(networkId, asset.onChainId),
|
8403
|
+
type: MODULE_TYPE$3,
|
8404
|
+
platform: "polkadot",
|
8405
|
+
networkId,
|
8406
|
+
onChainId: asset.onChainId,
|
8407
|
+
assetType: asset.assetType,
|
8408
|
+
// Erc20 or Token,
|
8409
|
+
isSufficient: asset.isSufficient,
|
8410
|
+
name: asset.name,
|
8411
|
+
symbol: asset.symbol,
|
8412
|
+
decimals: asset.decimals ?? 0,
|
8413
|
+
existentialDeposit: asset.existentialDeposit,
|
8414
|
+
isDefault: true
|
8415
|
+
}))
|
8416
|
+
// keep all tokens listed in the config + all tokens marked as sufficient
|
8417
|
+
.filter(token => {
|
8418
|
+
const configToken = configTokenByAssetId[token.onChainId];
|
8419
|
+
return configToken || token.isSufficient;
|
8420
|
+
})
|
8421
|
+
// apply config overrides
|
8422
|
+
.map(token => {
|
8423
|
+
const configToken = configTokenByAssetId[token.onChainId];
|
8424
|
+
return configToken ? assign({}, token, configToken) : token;
|
8425
|
+
})
|
8426
|
+
// validate results
|
8427
|
+
.filter(t => {
|
8428
|
+
const parsed = SubHydrationTokenSchema.safeParse(t);
|
8429
|
+
if (!parsed.success) log.warn(`Ignoring invalid token ${MODULE_TYPE$3}`, t);
|
8430
|
+
return parsed.success;
|
8431
|
+
});
|
8432
|
+
};
|
8433
|
+
|
8434
|
+
const getMiniMetadata$3 = ({
|
8435
|
+
networkId,
|
8436
|
+
specVersion,
|
8437
|
+
metadataRpc
|
8438
|
+
}) => {
|
8439
|
+
const source = MODULE_TYPE$3;
|
8440
|
+
const chainId = networkId;
|
8441
|
+
const systemVersion = getConstantValue(metadataRpc, "System", "Version");
|
8442
|
+
if (specVersion !== systemVersion.spec_version) log.warn("specVersion mismatch", {
|
8443
|
+
networkId,
|
8444
|
+
specVersion,
|
8445
|
+
systemVersion
|
8446
|
+
});
|
8447
|
+
const id = deriveMiniMetadataId({
|
8448
|
+
source,
|
8449
|
+
chainId,
|
8450
|
+
specVersion
|
8451
|
+
});
|
8452
|
+
const {
|
8453
|
+
unifiedMetadata
|
8454
|
+
} = parseMetadataRpc(metadataRpc);
|
8455
|
+
if (unifiedMetadata.version < 14) throw new Error(`Unsupported metadata version: ${unifiedMetadata.version}. Minimum required is 14.`);
|
8456
|
+
return {
|
8457
|
+
id,
|
8458
|
+
source,
|
8459
|
+
chainId,
|
8460
|
+
specVersion,
|
8461
|
+
version: MINIMETADATA_VERSION,
|
8462
|
+
data: getData$1(metadataRpc),
|
8463
|
+
extra: null
|
8464
|
+
};
|
8465
|
+
};
|
8466
|
+
const getData$1 = metadataRpc => {
|
8467
|
+
const {
|
8468
|
+
metadata,
|
8469
|
+
unifiedMetadata
|
8470
|
+
} = parseMetadataRpc(metadataRpc);
|
8471
|
+
|
8472
|
+
// ensure the network has all the required bits
|
8473
|
+
if (!hasStorageItem(unifiedMetadata, "AssetRegistry", "Assets") || !hasStorageItem(unifiedMetadata, "Tokens", "Accounts") || !hasRuntimeApi(unifiedMetadata, "CurrenciesApi", "accounts")) return null;
|
8474
|
+
compactMetadata(metadata, [{
|
8475
|
+
pallet: "AssetRegistry",
|
8476
|
+
items: ["Assets"]
|
8477
|
+
},
|
8478
|
+
// token specs
|
8479
|
+
{
|
8480
|
+
pallet: "Tokens",
|
8481
|
+
items: ["Accounts"]
|
8482
|
+
} // balances for tokens
|
8483
|
+
], [{
|
8484
|
+
runtimeApi: "CurrenciesApi",
|
8485
|
+
methods: ["accounts"]
|
8486
|
+
}]);
|
8487
|
+
return encodeMetadata(metadata);
|
8488
|
+
};
|
8489
|
+
|
8490
|
+
const getTransferCallData$3 = ({
|
8491
|
+
from,
|
8492
|
+
to,
|
8493
|
+
value,
|
8494
|
+
token,
|
8495
|
+
metadataRpc
|
8496
|
+
}) => {
|
8497
|
+
if (!isTokenOfType(token, MODULE_TYPE$3)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$3}.`);
|
8498
|
+
|
8499
|
+
// there is only one transfer method, no existential deposit handling.
|
8500
|
+
// => leave this to the frontend and dry runs
|
8501
|
+
const {
|
8502
|
+
builder
|
8503
|
+
} = parseMetadataRpc(metadataRpc);
|
8504
|
+
const {
|
8505
|
+
codec,
|
8506
|
+
location
|
8507
|
+
} = builder.buildCall("Currencies", "transfer");
|
8508
|
+
const args = {
|
8509
|
+
dest: to,
|
8510
|
+
currency_id: token.onChainId,
|
8511
|
+
amount: BigInt(value)
|
8512
|
+
};
|
8513
|
+
const callData = Binary.fromBytes(mergeUint8([new Uint8Array(location), codec.enc(args)]));
|
8514
|
+
return {
|
8515
|
+
address: from,
|
8516
|
+
method: callData.asHex()
|
8517
|
+
};
|
8518
|
+
};
|
8519
|
+
|
8520
|
+
const SUBSCRIPTION_INTERVAL$3 = 6_000;
|
8521
|
+
const subscribeBalances$3 = ({
|
8522
|
+
networkId,
|
8523
|
+
addressesByToken,
|
8524
|
+
connector,
|
8525
|
+
miniMetadata
|
8526
|
+
}) => {
|
8527
|
+
return new Observable(subscriber => {
|
8528
|
+
const abortController = new AbortController();
|
8529
|
+
|
8530
|
+
// on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
|
8531
|
+
// => poll values for each block
|
8532
|
+
const poll = async () => {
|
8533
|
+
try {
|
8534
|
+
if (abortController.signal.aborted) return;
|
8535
|
+
const balances = await fetchBalances$3({
|
8536
|
+
networkId,
|
8537
|
+
addressesByToken,
|
8538
|
+
connector,
|
8539
|
+
miniMetadata
|
8540
|
+
});
|
8541
|
+
if (abortController.signal.aborted) return;
|
8542
|
+
subscriber.next(balances);
|
8543
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$3);
|
8544
|
+
} catch (error) {
|
8545
|
+
log.error("Error", {
|
8546
|
+
module: MODULE_TYPE$3,
|
8547
|
+
networkId,
|
8548
|
+
miniMetadata,
|
8549
|
+
addressesByToken,
|
8550
|
+
error
|
8551
|
+
});
|
8552
|
+
subscriber.error(error);
|
8553
|
+
}
|
8554
|
+
};
|
8555
|
+
poll();
|
8556
|
+
return () => {
|
8557
|
+
abortController.abort();
|
8558
|
+
};
|
8559
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
8560
|
+
};
|
8561
|
+
|
8562
|
+
const SubHydrationBalanceModule = {
|
8563
|
+
type: MODULE_TYPE$3,
|
8564
|
+
platform: PLATFORM$3,
|
8565
|
+
getMiniMetadata: getMiniMetadata$3,
|
8566
|
+
fetchTokens: fetchTokens$3,
|
8567
|
+
fetchBalances: fetchBalances$3,
|
8568
|
+
subscribeBalances: subscribeBalances$3,
|
8569
|
+
getTransferCallData: getTransferCallData$3
|
8570
|
+
};
|
8571
|
+
|
8572
|
+
const MODULE_TYPE$2 = SubNativeTokenSchema.shape.type.value;
|
8573
|
+
const PLATFORM$2 = SubNativeTokenSchema.shape.platform.value;
|
8574
|
+
|
8575
|
+
// to be used by chaindata too
|
8576
|
+
z.strictObject({
|
8577
|
+
...TokenConfigBaseSchema.shape
|
8578
|
+
});
|
8579
|
+
|
8580
|
+
// Do not use this type outside of this module
|
8581
|
+
|
8582
|
+
// Do not use this type outside of this module
|
8583
|
+
|
8584
|
+
// Do not use this type outside of this module
|
8585
|
+
|
8586
|
+
/**
|
8587
|
+
* Pass some these into an `RpcStateQueryHelper` in order to easily batch multiple state queries into the one rpc call.
|
8588
|
+
*/
|
8589
|
+
|
8590
|
+
const fetchQueriesPack = async (connector, networkId, queries) => {
|
8591
|
+
const allStateKeys = queries.flatMap(({
|
8592
|
+
stateKeys
|
8593
|
+
}) => stateKeys).filter(isNotNil);
|
8594
|
+
|
8595
|
+
// doing a query with only null keys would throw an error => return early
|
8596
|
+
if (!allStateKeys.length) return queries.map(({
|
8597
|
+
stateKeys,
|
8598
|
+
decodeResult
|
8599
|
+
}) => decodeResult(stateKeys.map(() => null)));
|
8600
|
+
const response = await connector.send(networkId, "state_queryStorageAt", [allStateKeys]);
|
8601
|
+
const results = queries.reduce((acc, {
|
8602
|
+
stateKeys,
|
8603
|
+
decodeResult
|
8604
|
+
}) => {
|
8605
|
+
const changes = stateKeys.map(stateKey => {
|
8606
|
+
if (!stateKey) return null;
|
8607
|
+
const change = response[0].changes.find(([key]) => key === stateKey);
|
8608
|
+
if (!change) return null;
|
8609
|
+
return change[1];
|
8610
|
+
});
|
8611
|
+
acc.push(decodeResult(changes));
|
8612
|
+
return acc;
|
8613
|
+
}, []);
|
8614
|
+
return results;
|
8615
|
+
};
|
8616
|
+
|
8617
|
+
/**
|
8618
|
+
* Used by a variety of balance modules to help batch multiple state queries into the one rpc call.
|
8619
|
+
*/
|
8620
|
+
// export class RpcStateQueriesHelper<T> {
|
8621
|
+
// #connector: ChainConnector
|
8622
|
+
// #queries: Array<RpcStateQueries<T>>
|
8623
|
+
|
8624
|
+
// constructor(connector: ChainConnector, queries: Array<RpcStateQueries<T>>) {
|
8625
|
+
// this.#connector = connector
|
8626
|
+
// this.#queries = queries
|
8627
|
+
// }
|
8628
|
+
|
8629
|
+
// async subscribe(
|
8630
|
+
// networkId: DotNetworkId,
|
8631
|
+
// callback: SubscriptionCallback<T[]>,
|
8632
|
+
// timeout: number | false = false,
|
8633
|
+
// subscribeMethod = "state_subscribeStorage",
|
8634
|
+
// responseMethod = "state_storage",
|
8635
|
+
// unsubscribeMethod = "state_unsubscribeStorage",
|
8636
|
+
// ): Promise<UnsubscribeFn> {
|
8637
|
+
// const params = [this.#queries.flatMap(({ stateKeys }) => stateKeys)]
|
8638
|
+
|
8639
|
+
// const unsub = this.#connector.subscribe(
|
8640
|
+
// networkId,
|
8641
|
+
// subscribeMethod,
|
8642
|
+
// responseMethod,
|
8643
|
+
// params,
|
8644
|
+
// (error, result) => {
|
8645
|
+
// error
|
8646
|
+
// ? callback(error)
|
8647
|
+
// : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result))
|
8648
|
+
// },
|
8649
|
+
// timeout,
|
8650
|
+
// )
|
8651
|
+
|
8652
|
+
// const subscriptions = queries.map(([chainId, queries]) => {
|
8653
|
+
// const params = [queries.map(({ stateKey }) => stateKey)]
|
8654
|
+
|
8655
|
+
// const unsub = this.#connector.subscribe(
|
8656
|
+
// networkId,
|
8657
|
+
// subscribeMethod,
|
8658
|
+
// responseMethod,
|
8659
|
+
// params,
|
8660
|
+
// (error, result) => {
|
8661
|
+
// error
|
8662
|
+
// ? callback(error)
|
8663
|
+
// : callback(null, this.#distributeChangesToQueryDecoders.call(this, chainId, result))
|
8664
|
+
// },
|
8665
|
+
// timeout,
|
8666
|
+
// )
|
8667
|
+
|
8668
|
+
// return () => unsub.then((unsubscribe) => unsubscribe(unsubscribeMethod))
|
8669
|
+
// })
|
8670
|
+
|
8671
|
+
// return () => subscriptions.forEach((unsubscribe) => unsubscribe())
|
8672
|
+
// }
|
8673
|
+
|
8674
|
+
// async fetch(method = "state_queryStorageAt"): Promise<T[]> {
|
8675
|
+
// const queriesByChain = groupBy(this.#queries, "chainId")
|
8676
|
+
|
8677
|
+
// const resultsByChain = await Promise.all(
|
8678
|
+
// Object.entries(queriesByChain).map(async ([chainId, queries]) => {
|
8679
|
+
// const params = [queries.map(({ stateKey }) => stateKey)]
|
8680
|
+
|
8681
|
+
// const result = (await this.#connector.send(chainId, method, params))[0]
|
8682
|
+
// return this.#distributeChangesToQueryDecoders.call(this, chainId, result)
|
8683
|
+
// }),
|
8684
|
+
// )
|
8685
|
+
|
8686
|
+
// return resultsByChain.flatMap((result) => result)
|
8687
|
+
// }
|
8688
|
+
|
8689
|
+
// #distributeChangesToQueryDecoders(chainId: DotNetworkId, result: unknown): T[] {
|
8690
|
+
// if (typeof result !== "object" || result === null) return []
|
8691
|
+
// if (!hasOwnProperty(result, "changes") || typeof result.changes !== "object") return []
|
8692
|
+
// if (!Array.isArray(result.changes)) return []
|
8693
|
+
|
8694
|
+
// return result.changes.flatMap(([reference, change]: [unknown, unknown]): [T] | [] => {
|
8695
|
+
// if (typeof reference !== "string") {
|
8696
|
+
// log.warn(`Received non-string reference in RPC result: ${reference}`)
|
8697
|
+
// return []
|
8698
|
+
// }
|
8699
|
+
|
8700
|
+
// if (typeof change !== "string" && change !== null) {
|
8701
|
+
// log.warn(`Received non-string and non-null change in RPC result: ${reference} | ${change}`)
|
8702
|
+
// return []
|
8703
|
+
// }
|
8704
|
+
|
8705
|
+
// const query = this.#queries.find(
|
8706
|
+
// ({ chainId: cId, stateKey }) => cId === chainId && stateKey === reference,
|
8707
|
+
// )
|
8708
|
+
// if (!query) {
|
8709
|
+
// log.warn(
|
8710
|
+
// `Failed to find query:\n${reference} in\n${this.#queries.map(({ stateKey }) => stateKey)}`,
|
8711
|
+
// )
|
8712
|
+
// return []
|
8713
|
+
// }
|
8714
|
+
|
8715
|
+
// return [query.decodeResult(change)]
|
8716
|
+
// })
|
8717
|
+
// }
|
8718
|
+
// }
|
8719
|
+
|
8720
|
+
const SUBTENSOR_ROOT_NETUID = 0;
|
8721
|
+
const SUBTENSOR_MIN_STAKE_AMOUNT_PLANK = 1000000n;
|
8722
|
+
const TAO_DECIMALS = 9n;
|
8723
|
+
const SCALE_FACTOR = 10n ** TAO_DECIMALS; // Equivalent to 10e9 for precision
|
8724
|
+
const ONE_ALPHA_TOKEN = SCALE_FACTOR;
|
8725
|
+
const calculateAlphaPrice = ({
|
8726
|
+
dynamicInfo
|
8727
|
+
}) => {
|
8728
|
+
if (!dynamicInfo) return 0n;
|
8729
|
+
const {
|
8730
|
+
alpha_in,
|
8731
|
+
tao_in
|
8732
|
+
} = dynamicInfo;
|
8733
|
+
|
8734
|
+
// Scale taoIn before division to preserve precision
|
8735
|
+
const result = tao_in * SCALE_FACTOR / alpha_in;
|
8736
|
+
return result; // Scaled price as bigint
|
8737
|
+
};
|
8738
|
+
const calculateTaoAmountFromAlpha = ({
|
8739
|
+
alphaPrice,
|
8740
|
+
alphaStaked
|
8741
|
+
}) => {
|
8742
|
+
if (!alphaStaked || !alphaPrice) return 0n;
|
8743
|
+
const expectedAlpha = alphaStaked * alphaPrice;
|
8744
|
+
return expectedAlpha / SCALE_FACTOR;
|
8745
|
+
};
|
8746
|
+
const calculateTaoFromDynamicInfo = ({
|
8747
|
+
dynamicInfo,
|
8748
|
+
alphaStaked
|
8749
|
+
}) => {
|
8750
|
+
const alphaPrice = calculateAlphaPrice({
|
8751
|
+
dynamicInfo
|
8752
|
+
});
|
8753
|
+
return calculateTaoAmountFromAlpha({
|
8754
|
+
alphaPrice,
|
8755
|
+
alphaStaked
|
8756
|
+
});
|
8757
|
+
};
|
8758
|
+
|
8759
|
+
// per address, lists of values to add to the native balance
|
8760
|
+
|
8761
|
+
const getSubtensorStakingBalances$ = (connector, networkId, balanceDefs, miniMetadata) => {
|
8762
|
+
const addresses = balanceDefs.map(def => def.address);
|
8763
|
+
const token = balanceDefs[0].token;
|
8764
|
+
if (!addresses.length || !token || !miniMetadata.extra.hasSubtensorPallet || !miniMetadata.data) return of({});
|
8765
|
+
|
8766
|
+
// we are only doing runtime calls, there is no way to subscribe to changes, except polling
|
8767
|
+
// => start immediately, then repeat every 30 seconds
|
8768
|
+
return timer(0, 30000).pipe(switchMap(() => from(fetchStakeInfoByAddress(connector, networkId, miniMetadata, addresses)).pipe(switchMap(stakeInfoByAddress => fetchStakingBalanceValuesByAddress(connector, networkId, miniMetadata, stakeInfoByAddress)))));
|
8769
|
+
};
|
8770
|
+
const fetchStakingBalanceValuesByAddress = async (connector, networkId, miniMetadata, stakeInfoByAddress) => {
|
8771
|
+
const uniqueNetuids = uniq(values(stakeInfoByAddress).flatMap(infos => infos.map(info => info.netuid))).filter(netuid => netuid !== SUBTENSOR_ROOT_NETUID);
|
8772
|
+
const dynamicInfoByNetuid = await fetchDynamicInfoByNetuid(connector, networkId, miniMetadata, uniqueNetuids);
|
8773
|
+
return fromPairs(toPairs(stakeInfoByAddress).map(([address, stakeInfos]) => {
|
8774
|
+
const stakesBalances = stakeInfos.filter(({
|
8775
|
+
stake
|
8776
|
+
}) => stake >= SUBTENSOR_MIN_STAKE_AMOUNT_PLANK).map(({
|
8777
|
+
hotkey,
|
8778
|
+
netuid,
|
8779
|
+
stake
|
8780
|
+
}) => ({
|
8781
|
+
hotkey,
|
8782
|
+
netuid: Number(netuid),
|
8783
|
+
stake: BigInt(stake),
|
8784
|
+
dynamicInfo: dynamicInfoByNetuid[Number(netuid)]
|
8785
|
+
})).map(({
|
8786
|
+
hotkey,
|
8787
|
+
stake,
|
8788
|
+
netuid,
|
8789
|
+
dynamicInfo
|
8790
|
+
}) => {
|
8791
|
+
const {
|
8792
|
+
token_symbol,
|
8793
|
+
subnet_name,
|
8794
|
+
subnet_identity
|
8795
|
+
} = dynamicInfo ?? {};
|
8796
|
+
const tokenSymbol = new TextDecoder().decode(Uint8Array.from(token_symbol ?? []));
|
8797
|
+
const subnetName = new TextDecoder().decode(Uint8Array.from(subnet_name ?? []));
|
8798
|
+
const subnetIdentity = subnet_identity ? fromPairs(toPairs(subnet_identity).map(([key, binary]) => [key, binary.asText()])) : undefined;
|
8799
|
+
|
8800
|
+
// Add 1n balance if failed to fetch dynamic info, so the position is not ignored by Balance lib and is displayed in the UI.
|
8801
|
+
const alphaStakedInTao = dynamicInfo ? calculateTaoFromDynamicInfo({
|
8802
|
+
dynamicInfo,
|
8803
|
+
alphaStaked: stake
|
8804
|
+
}) : 1n;
|
8805
|
+
const alphaToTaoRate = calculateTaoFromDynamicInfo({
|
8806
|
+
dynamicInfo: dynamicInfo ?? null,
|
8807
|
+
alphaStaked: ONE_ALPHA_TOKEN
|
8808
|
+
}).toString();
|
8809
|
+
const stakeByNetuid = Number(netuid) === SUBTENSOR_ROOT_NETUID ? stake : alphaStakedInTao;
|
8810
|
+
const balanceValue = {
|
8811
|
+
source: "subtensor-staking",
|
8812
|
+
type: "subtensor",
|
8813
|
+
label: "subtensor-staking",
|
8814
|
+
amount: stakeByNetuid.toString(),
|
8815
|
+
meta: {
|
8816
|
+
type: "subtensor-staking",
|
8817
|
+
hotkey,
|
8818
|
+
netuid,
|
8819
|
+
amountStaked: stake.toString(),
|
8820
|
+
alphaToTaoRate,
|
8821
|
+
dynamicInfo: {
|
8822
|
+
tokenSymbol,
|
8823
|
+
subnetName,
|
8824
|
+
subnetIdentity: {
|
8825
|
+
...subnetIdentity,
|
8826
|
+
subnetName: subnetIdentity?.subnet_name || subnetName
|
8827
|
+
}
|
8828
|
+
}
|
8829
|
+
}
|
8830
|
+
};
|
8831
|
+
return balanceValue;
|
8832
|
+
});
|
8833
|
+
return [address, stakesBalances];
|
8834
|
+
}));
|
8835
|
+
};
|
8836
|
+
const fetchStakeInfoByAddress = async (connector, networkId, miniMetadata, addresses) => {
|
8837
|
+
const pairs = await Promise.all(addresses.map(async address => [address, await withRetry(() => fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "StakeInfoRuntimeApi", "get_stake_info_for_coldkey", [address]), {
|
8838
|
+
delay: 500,
|
8839
|
+
retryCount: 3
|
8840
|
+
})]));
|
8841
|
+
return fromPairs(pairs);
|
8842
|
+
};
|
8843
|
+
|
8844
|
+
// assume dynamic info doesnt change over the course of a browser session
|
8845
|
+
const dynamicInfoCache = new Map();
|
8846
|
+
const getCacheKey = (networkId, netuid) => `${networkId}:${netuid}`;
|
8847
|
+
const fetchDynamicInfoByNetuid = async (connector, networkId, miniMetadata, uniqueNetuids) => {
|
8848
|
+
const fetchInfo = async netuid => {
|
8849
|
+
if (netuid === SUBTENSOR_ROOT_NETUID) return null;
|
8850
|
+
const cacheKey = getCacheKey(networkId, netuid);
|
8851
|
+
if (!dynamicInfoCache.has(cacheKey)) {
|
8852
|
+
await withRetry(async () => {
|
8853
|
+
const result = await fetchRuntimeCallResult(connector, networkId, miniMetadata.data, "SubnetInfoRuntimeApi", "get_dynamic_info", [netuid]);
|
8854
|
+
dynamicInfoCache.set(cacheKey, result); // Cache successful response
|
8855
|
+
|
8856
|
+
return result;
|
8857
|
+
}, {
|
8858
|
+
delay: 500,
|
8859
|
+
retryCount: 3
|
8860
|
+
});
|
8861
|
+
}
|
8862
|
+
return dynamicInfoCache.get(cacheKey) ?? null;
|
8863
|
+
};
|
8864
|
+
const results = await Promise.all(uniqueNetuids.map(async netuid => [netuid, await fetchInfo(netuid)]));
|
8865
|
+
return fromPairs(results);
|
8866
|
+
};
|
8867
|
+
|
8868
|
+
const getOtherType = input => `other-${input}`;
|
8869
|
+
|
8870
|
+
/**
|
8871
|
+
* For converting the value of `lock?.id?.toUtf8?.()` which is retrieved from
|
8872
|
+
* the Balances.Locks storage key into a useful classification for our UI
|
8873
|
+
*/
|
8874
|
+
const getLockedType = input => {
|
8875
|
+
if (typeof input !== "string") return getOtherType("unknown");
|
8876
|
+
if (input.includes("vesting")) return "vesting";
|
8877
|
+
if (input.includes("calamvst")) return "vesting"; // vesting on manta network
|
8878
|
+
if (input.includes("ormlvest")) return "vesting"; // vesting ORML tokens
|
8879
|
+
if (input.includes("pyconvot")) return "democracy";
|
8880
|
+
if (input.includes("democrac")) return "democracy";
|
8881
|
+
if (input.includes("democracy")) return "democracy";
|
8882
|
+
if (input.includes("phrelect")) return "democracy"; // specific to council
|
8883
|
+
if (input.includes("staking")) return "staking";
|
8884
|
+
if (input.includes("stkngdel")) return "staking"; // staking delegator
|
8885
|
+
if (input.includes("stkngcol")) return "staking"; // staking collator
|
8886
|
+
if (input.includes("kiltpstk")) return "staking"; // Kilt specific staking
|
8887
|
+
if (input.includes("dapstake")) return "dapp-staking"; // Astar specific
|
8888
|
+
if (input.includes("appstake")) return "dapp-staking"; // Quartz (unique) specific
|
8889
|
+
if (input.includes("dappstaking")) return "dapp-staking";
|
8890
|
+
|
8891
|
+
// Joystream specifics https://github.com/Joystream/pioneer/blob/dev/packages/ui/src/accounts/model/lockTypes.ts
|
8892
|
+
if (input.includes("voting")) return "democracy";
|
8893
|
+
if (input.includes("candidac")) return "democracy"; // Council Candidate
|
8894
|
+
if (input.includes("councilo")) return "democracy"; // Councilor
|
8895
|
+
if (input.includes("proposal")) return "democracy";
|
8896
|
+
if (input.includes("boundsta")) return "staking"; // Bound Staking Account
|
8897
|
+
if (input.includes("invitemb")) return getOtherType(input); // Invite member
|
8898
|
+
if (input.includes("bounty")) return getOtherType(input);
|
8899
|
+
if (input.startsWith("wg-")) return getOtherType(input);
|
8900
|
+
|
8901
|
+
// ignore technical or undocumented lock types
|
8902
|
+
if (input.includes("pdexlock")) return getOtherType(input);
|
8903
|
+
if (input.includes("phala/sp")) return getOtherType(input);
|
8904
|
+
if (input.includes("aca/earn")) return getOtherType(input);
|
8905
|
+
if (input.includes("stk_stks")) return getOtherType(input);
|
8906
|
+
|
8907
|
+
// eslint-disable-next-line no-console
|
8908
|
+
console.warn(`unknown locked type: ${input}`);
|
8909
|
+
return getOtherType(input);
|
8910
|
+
};
|
8911
|
+
|
8912
|
+
const buildBaseQueries = (networkId, balanceDefs, miniMetadata) => {
|
8913
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
8914
|
+
account: ["System", "Account"],
|
8915
|
+
stakingLedger: ["Staking", "Ledger"],
|
8916
|
+
reserves: ["Balances", "Reserves"],
|
8917
|
+
// unused ??
|
8918
|
+
holds: ["Balances", "Holds"],
|
8919
|
+
locks: ["Balances", "Locks"],
|
8920
|
+
freezes: ["Balances", "Freezes"],
|
8921
|
+
poolMembers: ["NominationPools", "PoolMembers"]
|
8922
|
+
});
|
8923
|
+
if (!networkStorageCoders) throw new Error(`No network storage coders found for networkId: ${networkId}`);
|
8924
|
+
return balanceDefs.map(({
|
8925
|
+
token,
|
8926
|
+
address
|
8927
|
+
}) => {
|
8928
|
+
const accountStateKey = networkStorageCoders.account ? networkStorageCoders.account.keys.enc(address) : null;
|
8929
|
+
const locksStateKey = networkStorageCoders.locks ? networkStorageCoders.locks.keys.enc(address) : null;
|
8930
|
+
const freezesStateKey = networkStorageCoders.freezes ? networkStorageCoders.freezes.keys.enc(address) : null;
|
8931
|
+
const holdsStateKey = networkStorageCoders.holds ? networkStorageCoders.holds.keys.enc(address) : null;
|
8932
|
+
const stakingLedgerStateKey = networkStorageCoders.stakingLedger ? networkStorageCoders.stakingLedger.keys.enc(address) : null;
|
8933
|
+
const poolMemberStateKey = networkStorageCoders.poolMembers ? networkStorageCoders.poolMembers.keys.enc(address) : null;
|
8934
|
+
const stateKeys = [accountStateKey, locksStateKey, freezesStateKey, holdsStateKey, stakingLedgerStateKey, poolMemberStateKey];
|
8935
|
+
return {
|
8936
|
+
stateKeys,
|
8937
|
+
decodeResult: changes => {
|
8938
|
+
const balance = {
|
8939
|
+
source: "substrate-native",
|
8940
|
+
status: "live",
|
8941
|
+
address,
|
8942
|
+
networkId,
|
8943
|
+
tokenId: token.id,
|
8944
|
+
values: [],
|
8945
|
+
useLegacyTransferableCalculation: miniMetadata.extra.useLegacyTransferableCalculation
|
8946
|
+
};
|
8947
|
+
let nomPoolMemberInfo = null;
|
8948
|
+
const [accountChange, lockChange, freezesChange, holdsChange, stakingLedgerChange, nomPoolMemberChange] = changes;
|
8949
|
+
if (networkStorageCoders.account) {
|
8950
|
+
// for account balance we decode even empty values
|
8951
|
+
const baseValues = decodeBaseResult(networkStorageCoders.account, accountChange, networkId);
|
8952
|
+
balance.values.push(...baseValues);
|
8953
|
+
}
|
8954
|
+
if (networkStorageCoders.locks && lockChange) {
|
8955
|
+
const lockValues = decodeLocksResult(networkStorageCoders.locks, lockChange, networkId);
|
8956
|
+
balance.values.push(...lockValues);
|
8957
|
+
}
|
8958
|
+
if (networkStorageCoders.freezes && freezesChange) {
|
8959
|
+
const freezesValues = decodeFreezesResult(networkStorageCoders.freezes, freezesChange, networkId);
|
8960
|
+
balance.values.push(...freezesValues);
|
8961
|
+
}
|
8962
|
+
if (networkStorageCoders.holds && holdsChange) {
|
8963
|
+
const holdsValues = decodeHoldsResult(networkStorageCoders.holds, holdsChange, networkId);
|
8964
|
+
balance.values.push(...holdsValues);
|
8965
|
+
}
|
8966
|
+
if (networkStorageCoders.stakingLedger && stakingLedgerChange) {
|
8967
|
+
const stakingLedgerValues = decodeStakingLedgerResult(networkStorageCoders.stakingLedger, stakingLedgerChange, networkId);
|
8968
|
+
balance.values.push(...stakingLedgerValues);
|
8969
|
+
}
|
8970
|
+
if (networkStorageCoders.poolMembers && nomPoolMemberChange) {
|
8971
|
+
const nomPoolMemberValue = decodePoolMemberResult(networkStorageCoders.poolMembers, nomPoolMemberChange, networkId);
|
8972
|
+
if (nomPoolMemberValue) nomPoolMemberInfo = nomPoolMemberValue;
|
8973
|
+
}
|
8974
|
+
return {
|
8975
|
+
balance,
|
8976
|
+
nomPoolMemberInfo
|
8977
|
+
};
|
8978
|
+
}
|
8979
|
+
};
|
8980
|
+
}).filter(isNotNil);
|
8981
|
+
};
|
8982
|
+
|
8983
|
+
// AccountInfo is the state_storage data format for nativeToken balances
|
8984
|
+
// Theory: new chains will be at least on metadata v14, and so we won't need to hardcode their AccountInfo type.
|
8985
|
+
// But for chains we want to support which aren't on metadata v14, hardcode them here:
|
8986
|
+
// If the chain upgrades to metadata v14, this override will be ignored :)
|
8987
|
+
// const RegularAccountInfoFallback = Struct({
|
8988
|
+
// nonce: u32,
|
8989
|
+
// consumers: u32,
|
8990
|
+
// providers: u32,
|
8991
|
+
// sufficients: u32,
|
8992
|
+
// data: Struct({ free: u128, reserved: u128, miscFrozen: u128, feeFrozen: u128 }),
|
8993
|
+
// })
|
8994
|
+
// const NoSufficientsAccountInfoFallback = Struct({
|
8995
|
+
// nonce: u32,
|
8996
|
+
// consumers: u32,
|
8997
|
+
// providers: u32,
|
8998
|
+
// data: Struct({ free: u128, reserved: u128, miscFrozen: u128, feeFrozen: u128 }),
|
8999
|
+
// })
|
9000
|
+
// const AccountInfoOverrides: Record<
|
9001
|
+
// string,
|
9002
|
+
// typeof RegularAccountInfoFallback | typeof NoSufficientsAccountInfoFallback | undefined
|
9003
|
+
// > = {
|
9004
|
+
// // crown-sterlin is not yet on metadata v14
|
9005
|
+
// "crown-sterling": NoSufficientsAccountInfoFallback,
|
9006
|
+
|
9007
|
+
// // crust is not yet on metadata v14
|
9008
|
+
// "crust": NoSufficientsAccountInfoFallback,
|
9009
|
+
|
9010
|
+
// // kulupu is not yet on metadata v14
|
9011
|
+
// "kulupu": RegularAccountInfoFallback,
|
9012
|
+
|
9013
|
+
// // nftmart is not yet on metadata v14
|
9014
|
+
// "nftmart": RegularAccountInfoFallback,
|
9015
|
+
// }
|
9016
|
+
|
9017
|
+
const decodeBaseResult = (coder, value, networkId) => {
|
9018
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9019
|
+
|
9020
|
+
const decoded = decodeScale(coder, value, `Failed to decode base native balance on chain ${networkId}`);
|
9021
|
+
const free = (decoded?.data?.free ?? 0n).toString();
|
9022
|
+
const reserved = (decoded?.data?.reserved ?? 0n).toString();
|
9023
|
+
const miscLock = ((decoded?.data?.miscFrozen ?? 0n) + (
|
9024
|
+
// new chains don't split their `frozen` amount into `feeFrozen` and `miscFrozen`.
|
9025
|
+
// for these chains, we'll use the `frozen` amount as `miscFrozen`.
|
9026
|
+
decoded?.data?.frozen ?? 0n)).toString();
|
9027
|
+
const feesLock = (decoded?.data?.feeFrozen ?? 0n).toString();
|
9028
|
+
|
9029
|
+
// even if these values are 0, we still need to add them to the balanceJson.values array
|
9030
|
+
// so that the balance pool can handle newly zeroed balances
|
9031
|
+
// const existingValues = Object.fromEntries(
|
9032
|
+
// balanceJson.values.map((v) => [getValueId(v), v]),
|
9033
|
+
// )
|
9034
|
+
const newValues = [{
|
9035
|
+
type: "free",
|
9036
|
+
label: "free",
|
9037
|
+
amount: free.toString()
|
9038
|
+
}, {
|
9039
|
+
type: "reserved",
|
9040
|
+
label: "reserved",
|
9041
|
+
amount: reserved.toString()
|
9042
|
+
}, {
|
9043
|
+
type: "locked",
|
9044
|
+
label: "misc",
|
9045
|
+
amount: miscLock.toString()
|
9046
|
+
}, {
|
9047
|
+
type: "locked",
|
9048
|
+
label: "fees",
|
9049
|
+
amount: feesLock.toString()
|
9050
|
+
}];
|
9051
|
+
return newValues;
|
9052
|
+
};
|
9053
|
+
const decodeLocksResult = (coder, value, networkId) => {
|
9054
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9055
|
+
|
9056
|
+
const decoded = decodeScale(coder, value, `Failed to decode lock on chain ${networkId}`);
|
9057
|
+
const locksQueryLocks = decoded?.map?.(lock => ({
|
9058
|
+
type: "locked",
|
9059
|
+
source: "substrate-native-locks",
|
9060
|
+
label: getLockedType(lock?.id?.asText?.()),
|
9061
|
+
meta: {
|
9062
|
+
id: lock?.id?.asText?.()
|
9063
|
+
},
|
9064
|
+
amount: (lock?.amount ?? 0n).toString()
|
9065
|
+
})) ?? [];
|
9066
|
+
return locksQueryLocks;
|
9067
|
+
|
9068
|
+
// // locked values should be replaced entirely, not merged or appended
|
9069
|
+
// const nonLockValues = balanceJson.values.filter(
|
9070
|
+
// (v) => v.source !== "substrate-native-locks",
|
9071
|
+
// )
|
9072
|
+
// balanceJson.values = nonLockValues.concat(locksQueryLocks)
|
9073
|
+
|
9074
|
+
// // fix any double-counting between Balances.Locks (for staking locks) and Staking.Ledger (for unbonding locks)
|
9075
|
+
// balanceJson.values = updateStakingLocksUsingUnbondingLocks(balanceJson.values)
|
9076
|
+
};
|
9077
|
+
const decodeFreezesResult = (coder, value, networkId) => {
|
9078
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9079
|
+
|
9080
|
+
const decoded = decodeScale(coder, value, `Failed to decode freeze on chain ${networkId}`);
|
9081
|
+
const freezesValues = decoded?.map?.(lock => ({
|
9082
|
+
type: "locked",
|
9083
|
+
source: "substrate-native-freezes",
|
9084
|
+
label: getLockedType(lock?.id?.type?.toLowerCase?.()),
|
9085
|
+
amount: lock?.amount?.toString?.() ?? "0"
|
9086
|
+
})) ?? [];
|
9087
|
+
return freezesValues;
|
9088
|
+
|
9089
|
+
// // freezes values should be replaced entirely, not merged or appended
|
9090
|
+
// const nonFreezesValues = balanceJson.values.filter(
|
9091
|
+
// (v) => v.source !== "substrate-native-freezes",
|
9092
|
+
// )
|
9093
|
+
// balanceJson.values = nonFreezesValues.concat(freezesQueryLocks)
|
9094
|
+
|
9095
|
+
// return balanceJson
|
9096
|
+
};
|
9097
|
+
const decodeHoldsResult = (coder, value, networkId) => {
|
9098
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9099
|
+
|
9100
|
+
const decoded = decodeScale(coder, value, `Failed to decode holds on chain ${networkId}`);
|
9101
|
+
|
9102
|
+
// at this time we re only interested in DelegatedStaking holds, to determine if nom pool staked amount is included in reserved or not
|
9103
|
+
const holdsValues = decoded?.filter(hold => hold.id?.type).map(hold => ({
|
9104
|
+
type: "locked",
|
9105
|
+
source: "substrate-native-holds",
|
9106
|
+
label: hold.id.type,
|
9107
|
+
// anount needs to be 0 or a row could appear in the UI, this entry is just for information
|
9108
|
+
amount: "0",
|
9109
|
+
meta: {
|
9110
|
+
amount: (hold?.amount ?? 0n).toString()
|
9111
|
+
}
|
9112
|
+
})) ?? [];
|
9113
|
+
return holdsValues;
|
9114
|
+
|
9115
|
+
// // values should be replaced entirely, not merged or appended
|
9116
|
+
// const nonHoldsValues = balanceJson.values.filter(
|
9117
|
+
// (v) => v.source !== "substrate-native-holds",
|
9118
|
+
// )
|
9119
|
+
// balanceJson.values = nonHoldsValues.concat(holdsQueryLocks)
|
9120
|
+
|
9121
|
+
// return balanceJson
|
9122
|
+
};
|
9123
|
+
const decodeStakingLedgerResult = (coder, value, networkId) => {
|
9124
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
9126
|
+
|
9127
|
+
const decoded = decodeScale(coder, value, `Failed to decode unbonding query on chain ${networkId}`);
|
9128
|
+
const totalUnlocking = decoded?.unlocking?.reduce?.((acc, unlocking) => acc + unlocking.value, 0n) ?? 0n;
|
9129
|
+
const stakingLedgerResults = totalUnlocking <= 0n ? [] : [{
|
9130
|
+
type: "locked",
|
9131
|
+
source: "substrate-native-unbonding",
|
9132
|
+
label: "Unbonding",
|
9133
|
+
amount: totalUnlocking.toString()
|
9134
|
+
}];
|
9135
|
+
return stakingLedgerResults;
|
9136
|
+
|
9137
|
+
// if (totalUnlocking <= 0n) unbondingQueryLocks = []
|
9138
|
+
// else {
|
9139
|
+
// unbondingQueryLocks = [
|
9140
|
+
// {
|
9141
|
+
// type: "locked",
|
9142
|
+
// source: "substrate-native-unbonding",
|
9143
|
+
// label: "Unbonding",
|
9144
|
+
// amount: totalUnlocking.toString(),
|
9145
|
+
// },
|
9146
|
+
// ]
|
9147
|
+
// }
|
9148
|
+
|
9149
|
+
// // unbonding values should be replaced entirely, not merged or appended
|
9150
|
+
// const nonUnbondingValues = balanceJson.values.filter(
|
9151
|
+
// (v) => v.source !== "substrate-native-unbonding",
|
9152
|
+
// )
|
9153
|
+
// balanceJson.values = nonUnbondingValues.concat(unbondingQueryLocks)
|
9154
|
+
|
9155
|
+
// // fix any double-counting between Balances.Locks (for staking locks) and Staking.Ledger (for unbonding locks)
|
9156
|
+
// balanceJson.values = updateStakingLocksUsingUnbondingLocks(balanceJson.values)
|
9157
|
+
|
9158
|
+
// return balanceJson
|
9159
|
+
};
|
9160
|
+
const decodePoolMemberResult = (coder, value, networkId) => {
|
9161
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9162
|
+
|
9163
|
+
const decoded = decodeScale(coder, value, `Failed to decode poolMembers on chain ${networkId}`);
|
9164
|
+
if (!decoded) return null;
|
9165
|
+
const poolId = decoded.pool_id.toString();
|
9166
|
+
const points = decoded.points.toString();
|
9167
|
+
const unbondingEras = Array.from(decoded.unbonding_eras ?? []).flatMap(entry => {
|
9168
|
+
if (entry === undefined) return [];
|
9169
|
+
const [key, value] = Array.from(entry);
|
9170
|
+
const era = key.toString();
|
9171
|
+
const amount = value.toString();
|
9172
|
+
if (typeof era !== "string" || typeof amount !== "string") return [];
|
9173
|
+
return {
|
9174
|
+
era,
|
9175
|
+
amount
|
9176
|
+
};
|
9177
|
+
});
|
9178
|
+
return {
|
9179
|
+
poolId,
|
9180
|
+
points,
|
9181
|
+
unbondingEras
|
9182
|
+
};
|
9183
|
+
};
|
9184
|
+
|
9185
|
+
// const getBaseQuery = (
|
9186
|
+
// networkId: string,
|
9187
|
+
// address: string,
|
9188
|
+
// coder: ScaleStorageCoder,
|
9189
|
+
// ): RpcStateQuery<Array<AmountWithLabel<string>>> | null => {
|
9190
|
+
// // For chains which are using metadata < v14
|
9191
|
+
// const getFallbackStateKey = () => {
|
9192
|
+
// const addressBytes = decodeAnyAddress(address) // TODO replace with modern api, this is slow
|
9193
|
+
// const addressHash = blake2Concat(addressBytes).replace(/^0x/, "")
|
9194
|
+
// const moduleHash = "26aa394eea5630e07c48ae0c9558cef7" // util_crypto.xxhashAsHex("System", 128);
|
9195
|
+
// const storageHash = "b99d880ec681799c0cf30e8886371da9" // util_crypto.xxhashAsHex("Account", 128);
|
9196
|
+
// const moduleStorageHash = `${moduleHash}${storageHash}` // System.Account is the state_storage key prefix for nativeToken balances
|
9197
|
+
// return `0x${moduleStorageHash}${addressHash}`
|
9198
|
+
// }
|
9199
|
+
|
9200
|
+
// // const scaleCoder = chainStorageCoders.get(chainId)?.base
|
9201
|
+
// // NOTE: Only use fallback key when `scaleCoder` is not defined
|
9202
|
+
// // i.e. when chain doesn't have metadata v14/v15
|
9203
|
+
// const stateKey = coder
|
9204
|
+
// ? encodeStateKey(coder, `Invalid address in ${networkId} base query ${address}`, address)
|
9205
|
+
// : getFallbackStateKey()
|
9206
|
+
// if (!stateKey) return null
|
9207
|
+
|
9208
|
+
// const decodeResult = (change: string | null) => {
|
9209
|
+
// // BEGIN: Handle chains which use metadata < v14
|
9210
|
+
// let oldChainBalance = null
|
9211
|
+
// if (!coder) {
|
9212
|
+
// const scaleAccountInfo = AccountInfoOverrides[networkId]
|
9213
|
+
// if (scaleAccountInfo === undefined) {
|
9214
|
+
// // chain metadata version is < 15 and we also don't have an override hardcoded in
|
9215
|
+
// // the best way to handle this case: log a warning and return an empty balance
|
9216
|
+
// log.debug(
|
9217
|
+
// `Native token on chain ${networkId} has no balance type for decoding. Defaulting to a balance of 0 (zero).`,
|
9218
|
+
// )
|
9219
|
+
// return []
|
9220
|
+
// }
|
9221
|
+
|
9222
|
+
// try {
|
9223
|
+
// // eslint-disable-next-line no-var
|
9224
|
+
// oldChainBalance = change === null ? null : scaleAccountInfo.dec(change)
|
9225
|
+
// } catch (error) {
|
9226
|
+
// log.warn(
|
9227
|
+
// `Failed to create pre-metadataV14 balance type for native on chain ${networkId}: ${error?.toString()}`,
|
9228
|
+
// )
|
9229
|
+
// return []
|
9230
|
+
// }
|
9231
|
+
// }
|
9232
|
+
// // END: Handle chains which use metadata < v14
|
9233
|
+
|
9234
|
+
// /** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9235
|
+
// type DecodedType = {
|
9236
|
+
// data?: {
|
9237
|
+
// flags?: bigint
|
9238
|
+
// free?: bigint
|
9239
|
+
// frozen?: bigint
|
9240
|
+
// reserved?: bigint
|
9241
|
+
|
9242
|
+
// // deprecated fields (they only show up on old chains)
|
9243
|
+
// feeFrozen?: bigint
|
9244
|
+
// miscFrozen?: bigint
|
9245
|
+
// }
|
9246
|
+
// }
|
9247
|
+
// const decoded =
|
9248
|
+
// decodeScale<DecodedType>(
|
9249
|
+
// coder,
|
9250
|
+
// change,
|
9251
|
+
// `Failed to decode base native balance on chain ${networkId}`,
|
9252
|
+
// ) ?? oldChainBalance
|
9253
|
+
|
9254
|
+
// const free = (decoded?.data?.free ?? 0n).toString()
|
9255
|
+
// const reserved = (decoded?.data?.reserved ?? 0n).toString()
|
9256
|
+
// const miscLock = (
|
9257
|
+
// (decoded?.data?.miscFrozen ?? 0n) +
|
9258
|
+
// // new chains don't split their `frozen` amount into `feeFrozen` and `miscFrozen`.
|
9259
|
+
// // for these chains, we'll use the `frozen` amount as `miscFrozen`.
|
9260
|
+
// ((decoded?.data as DecodedType["data"])?.frozen ?? 0n)
|
9261
|
+
// ).toString()
|
9262
|
+
// const feesLock = (decoded?.data?.feeFrozen ?? 0n).toString()
|
9263
|
+
|
9264
|
+
// // even if these values are 0, we still need to add them to the balanceJson.values array
|
9265
|
+
// // so that the balance pool can handle newly zeroed balances
|
9266
|
+
// // const existingValues = Object.fromEntries(
|
9267
|
+
// // balanceJson.values.map((v) => [getValueId(v), v]),
|
9268
|
+
// // )
|
9269
|
+
// const newValues: AmountWithLabel<string>[] = [
|
9270
|
+
// { type: "free", label: "free", amount: free.toString() },
|
9271
|
+
// { type: "reserved", label: "reserved", amount: reserved.toString() },
|
9272
|
+
// { type: "locked", label: "misc", amount: miscLock.toString() },
|
9273
|
+
// { type: "locked", label: "fees", amount: feesLock.toString() },
|
9274
|
+
// ]
|
9275
|
+
|
9276
|
+
// return newValues
|
9277
|
+
|
9278
|
+
// // const newValuesObj = Object.fromEntries(newValues.map((v) => [getValueId(v), v]))
|
9279
|
+
|
9280
|
+
// // balanceJson.values = Object.values({ ...existingValues, ...newValuesObj })
|
9281
|
+
|
9282
|
+
// // return balanceJson
|
9283
|
+
// }
|
9284
|
+
|
9285
|
+
// return { chainId: networkId, stateKey, decodeResult }
|
9286
|
+
// }
|
9287
|
+
|
9288
|
+
/**
|
9289
|
+
* Each nominationPool in the nominationPools pallet has access to some accountIds which have no
|
9290
|
+
* associated private key. Instead, they are derived from this function.
|
9291
|
+
*/
|
9292
|
+
const nompoolAccountId = (palletId, poolId, index) => {
|
9293
|
+
const utf8Encoder = new TextEncoder();
|
9294
|
+
const encModPrefix = utf8Encoder.encode("modl");
|
9295
|
+
const encPalletId = utf8Encoder.encode(palletId);
|
9296
|
+
const encIndex = new Uint8Array([index]);
|
9297
|
+
const encPoolId = u32.enc(typeof poolId === "string" ? parseInt(poolId, 10) : poolId);
|
9298
|
+
const length = encModPrefix.length + encPalletId.length + encIndex.length + encPoolId.length;
|
9299
|
+
const remainingBytes = 32 - length;
|
9300
|
+
const encEmptyH256 = new Uint8Array(remainingBytes);
|
9301
|
+
const bytes = mergeUint8([encModPrefix, encPalletId, encIndex, encPoolId, encEmptyH256]);
|
9302
|
+
return AccountId().dec(bytes);
|
9303
|
+
};
|
9304
|
+
/** The stash account for the nomination pool */
|
9305
|
+
const nompoolStashAccountId = (palletId, poolId) => nompoolAccountId(palletId, poolId, 0);
|
9306
|
+
|
9307
|
+
const buildNomPoolQueries = (networkId, partialBalances, miniMetadata) => {
|
9308
|
+
const networkStorageCoders = getNomPoolCoders(networkId, miniMetadata);
|
9309
|
+
if (!networkStorageCoders) throw new Error(`No network storage coders found for networkId: ${networkId}`);
|
9310
|
+
return partialBalances.map(({
|
9311
|
+
balance,
|
9312
|
+
nomPoolMemberInfo
|
9313
|
+
}) => {
|
9314
|
+
const stateKeys = getNomPoolStateKeys(networkStorageCoders, nomPoolMemberInfo, miniMetadata.extra);
|
9315
|
+
return {
|
9316
|
+
stateKeys,
|
9317
|
+
decodeResult: changes => {
|
9318
|
+
if (!nomPoolMemberInfo || !stateKeys.length || changes.includes(null)) return balance;
|
9319
|
+
const accountPoints = nomPoolMemberInfo?.points ?? "0";
|
9320
|
+
const {
|
9321
|
+
poolPoints = "0"
|
9322
|
+
} = decodePoolPoints(networkStorageCoders.bondedPools, changes[0], networkId);
|
9323
|
+
const {
|
9324
|
+
poolTotalActiveStake = "0"
|
9325
|
+
} = decodePoolStake(networkStorageCoders.ledger, changes[1], networkId);
|
9326
|
+
const {
|
9327
|
+
metadata = "0"
|
9328
|
+
} = decodePoolMeta(networkStorageCoders.metadata, changes[2], networkId);
|
9329
|
+
const amount = accountPoints === "0" || poolPoints === "0" || poolTotalActiveStake === "0" ? 0n : BigInt(poolTotalActiveStake) * BigInt(accountPoints) / BigInt(poolPoints);
|
9330
|
+
const unbondingAmount = nomPoolMemberInfo.unbondingEras.reduce((total, {
|
9331
|
+
amount
|
9332
|
+
}) => total + BigInt(amount ?? "0"), 0n);
|
9333
|
+
return {
|
9334
|
+
...balance,
|
9335
|
+
values: [...(balance.values ?? []), {
|
9336
|
+
source: "nompools-staking",
|
9337
|
+
type: "nompool",
|
9338
|
+
label: "nompools-staking",
|
9339
|
+
amount: amount.toString(),
|
9340
|
+
meta: {
|
9341
|
+
type: "nompool",
|
9342
|
+
poolId: nomPoolMemberInfo.poolId,
|
9343
|
+
description: metadata
|
9344
|
+
}
|
9345
|
+
}, {
|
9346
|
+
source: "nompools-staking",
|
9347
|
+
type: "nompool",
|
9348
|
+
label: "nompools-unbonding",
|
9349
|
+
amount: unbondingAmount.toString(),
|
9350
|
+
meta: {
|
9351
|
+
poolId: nomPoolMemberInfo.poolId,
|
9352
|
+
description: metadata ?? `Pool ${nomPoolMemberInfo.poolId}`,
|
9353
|
+
unbonding: true
|
9354
|
+
}
|
9355
|
+
}]
|
9356
|
+
};
|
9357
|
+
}
|
9358
|
+
};
|
9359
|
+
});
|
9360
|
+
};
|
9361
|
+
const getNomPoolCoders = (networkId, miniMetadata) => {
|
9362
|
+
return buildNetworkStorageCoders(networkId, miniMetadata, {
|
9363
|
+
poolMembers: ["NominationPools", "PoolMembers"],
|
9364
|
+
bondedPools: ["NominationPools", "BondedPools"],
|
9365
|
+
ledger: ["Staking", "Ledger"],
|
9366
|
+
metadata: ["NominationPools", "Metadata"]
|
9367
|
+
});
|
9368
|
+
};
|
9369
|
+
const decodePoolPoints = (coder, value, networkId) => {
|
9370
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9371
|
+
|
9372
|
+
const decoded = decodeScale(coder, value, `Failed to decode bondedPools on chain ${networkId}`);
|
9373
|
+
return {
|
9374
|
+
poolPoints: decoded?.points.toString()
|
9375
|
+
};
|
9376
|
+
};
|
9377
|
+
const decodePoolStake = (coder, value, networkId) => {
|
9378
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9379
|
+
|
9380
|
+
const decoded = decodeScale(coder, value, `Failed to decode ledger on chain ${networkId}`);
|
9381
|
+
return {
|
9382
|
+
poolTotalActiveStake: decoded?.active.toString()
|
9383
|
+
};
|
9384
|
+
};
|
9385
|
+
const decodePoolMeta = (coder, value, networkId) => {
|
9386
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
9387
|
+
|
9388
|
+
const decoded = decodeScale(coder, value, `Failed to decode metadata on chain ${networkId}`);
|
9389
|
+
const metadata = decoded?.asText();
|
9390
|
+
return {
|
9391
|
+
metadata
|
9392
|
+
};
|
9393
|
+
};
|
9394
|
+
const getNomPoolStateKeys = (coders, nomPoolMemberInfo, extra) => {
|
9395
|
+
if (!nomPoolMemberInfo || !extra.nominationPoolsPalletId || !coders?.bondedPools || !coders.ledger || !coders.metadata) return [];
|
9396
|
+
const poolId = nomPoolMemberInfo.poolId;
|
9397
|
+
const stashAddress = nompoolStashAccountId(extra.nominationPoolsPalletId, poolId);
|
9398
|
+
const poolPointsStateKey = coders.bondedPools.keys.enc(poolId);
|
9399
|
+
const poolStakeStateKey = coders.ledger.keys.enc(stashAddress);
|
9400
|
+
const poolMetaStateKey = coders.metadata.keys.enc(poolId);
|
9401
|
+
return [poolPointsStateKey, poolStakeStateKey, poolMetaStateKey];
|
9402
|
+
};
|
9403
|
+
|
9404
|
+
const fetchBalances$2 = async ({
|
9405
|
+
networkId,
|
9406
|
+
addressesByToken,
|
9407
|
+
connector,
|
9408
|
+
miniMetadata
|
9409
|
+
}) => {
|
9410
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
9411
|
+
if (!miniMetadata?.data) {
|
9412
|
+
log.warn("MiniMetadata is required for fetching balances");
|
9413
|
+
return {
|
9414
|
+
success: [],
|
9415
|
+
errors: balanceDefs.map(def => ({
|
9416
|
+
tokenId: def.token.id,
|
9417
|
+
address: def.address,
|
9418
|
+
error: new Error("Minimetadata is required for fetching balances")
|
9419
|
+
}))
|
9420
|
+
};
|
9421
|
+
}
|
9422
|
+
if (miniMetadata.source !== MODULE_TYPE$2) {
|
9423
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE$2}.`);
|
9424
|
+
return {
|
9425
|
+
success: [],
|
9426
|
+
errors: balanceDefs.map(def => ({
|
9427
|
+
tokenId: def.token.id,
|
9428
|
+
address: def.address,
|
9429
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE$2}`)
|
9430
|
+
}))
|
9431
|
+
};
|
9432
|
+
}
|
9433
|
+
if (miniMetadata.chainId !== networkId) {
|
9434
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE$2}. Expected chainId is ${networkId}`);
|
9435
|
+
return {
|
9436
|
+
success: [],
|
9437
|
+
errors: balanceDefs.map(def => ({
|
9438
|
+
tokenId: def.token.id,
|
9439
|
+
address: def.address,
|
9440
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
9441
|
+
}))
|
9442
|
+
};
|
9443
|
+
}
|
9444
|
+
const queries = buildBaseQueries(networkId, balanceDefs, miniMetadata);
|
9445
|
+
const partialBalances = await fetchQueriesPack(connector, networkId, queries);
|
9446
|
+
|
9447
|
+
// now for each balance that includes nomPoolStaking, we need to fetch the metadata for the pool
|
9448
|
+
const nomPoolQueries = buildNomPoolQueries(networkId, partialBalances, miniMetadata);
|
9449
|
+
const balances = await fetchQueriesPack(connector, networkId, nomPoolQueries);
|
9450
|
+
|
9451
|
+
// TODO ⚠️ dedupe locks
|
9452
|
+
|
9453
|
+
const subtensorBalances$ = getSubtensorStakingBalances$(connector, networkId, balanceDefs, miniMetadata);
|
9454
|
+
const subtensorBalancesByAddress = await firstValueFrom(subtensorBalances$);
|
9455
|
+
for (const [address, subtensorBalances] of Object.entries(subtensorBalancesByAddress)) {
|
9456
|
+
const balance = balances.find(b => b.address === address);
|
9457
|
+
if (balance?.values) balance.values.push(...subtensorBalances);
|
9458
|
+
}
|
9459
|
+
return {
|
9460
|
+
success: balances,
|
9461
|
+
errors: []
|
9462
|
+
};
|
9463
|
+
};
|
9464
|
+
|
9465
|
+
const fetchTokens$2 = async ({
|
9466
|
+
networkId,
|
9467
|
+
tokens,
|
9468
|
+
connector,
|
9469
|
+
miniMetadata
|
9470
|
+
}) => {
|
9471
|
+
if (miniMetadata.extra.disable) return [];
|
9472
|
+
const {
|
9473
|
+
tokenSymbol: symbol,
|
9474
|
+
tokenDecimals: decimals
|
9475
|
+
} = await getChainProperties(connector, networkId);
|
9476
|
+
if (!miniMetadata.extra.existentialDeposit) log.warn(`Substrate native module: existentialDeposit is undefined for ${networkId}, using 0`);
|
9477
|
+
const tokenConfig = tokens[0];
|
9478
|
+
const nativeToken = {
|
9479
|
+
id: subNativeTokenId(networkId),
|
9480
|
+
type: "substrate-native",
|
9481
|
+
platform: "polkadot",
|
9482
|
+
networkId,
|
9483
|
+
isDefault: true,
|
9484
|
+
symbol: symbol,
|
9485
|
+
name: symbol,
|
9486
|
+
decimals: decimals,
|
9487
|
+
existentialDeposit: miniMetadata.extra.existentialDeposit ?? "0"
|
9488
|
+
};
|
9489
|
+
return [assign(nativeToken, tokenConfig)];
|
9490
|
+
};
|
9491
|
+
const DotNetworkPropertiesSimple = z.object({
|
9492
|
+
tokenDecimals: z.number().optional().default(0),
|
9493
|
+
tokenSymbol: z.string().optional().default("Unit")
|
9494
|
+
});
|
9495
|
+
const DotNetworkPropertiesArray = z.object({
|
9496
|
+
tokenDecimals: z.array(z.number()).nonempty(),
|
9497
|
+
tokenSymbol: z.array(z.string()).nonempty()
|
9498
|
+
});
|
9499
|
+
const DotNetworkPropertiesSchema = z.union([DotNetworkPropertiesSimple, DotNetworkPropertiesArray]).transform(val => ({
|
9500
|
+
tokenDecimals: Array.isArray(val.tokenDecimals) ? val.tokenDecimals[0] : val.tokenDecimals,
|
9501
|
+
tokenSymbol: Array.isArray(val.tokenSymbol) ? val.tokenSymbol[0] : val.tokenSymbol
|
9502
|
+
}));
|
9503
|
+
const getChainProperties = async (connector, networkId) => {
|
9504
|
+
const properties = await connector.send(networkId, "system_properties", [], true);
|
9505
|
+
return DotNetworkPropertiesSchema.parse(properties);
|
9506
|
+
};
|
9507
|
+
|
9508
|
+
const getMiniMetadata$2 = ({
|
9509
|
+
networkId,
|
9510
|
+
specVersion,
|
9511
|
+
metadataRpc,
|
9512
|
+
config
|
9513
|
+
}) => {
|
9514
|
+
const source = MODULE_TYPE$2;
|
9515
|
+
const chainId = networkId;
|
9516
|
+
const systemVersion = getConstantValue(metadataRpc, "System", "Version");
|
9517
|
+
if (specVersion !== systemVersion.spec_version) log.warn("specVersion mismatch", {
|
9518
|
+
networkId,
|
9519
|
+
specVersion,
|
9520
|
+
systemVersion
|
9521
|
+
});
|
9522
|
+
const id = deriveMiniMetadataId({
|
9523
|
+
source,
|
9524
|
+
chainId,
|
9525
|
+
specVersion
|
9526
|
+
});
|
9527
|
+
const {
|
9528
|
+
metadata,
|
9529
|
+
unifiedMetadata
|
9530
|
+
} = parseMetadataRpc(metadataRpc);
|
9531
|
+
if (unifiedMetadata.version < 14) throw new Error(`Unsupported metadata version: ${unifiedMetadata.version}. Minimum required is 14.`);
|
9532
|
+
if (config?.disable) return {
|
9533
|
+
id,
|
9534
|
+
source,
|
9535
|
+
chainId,
|
9536
|
+
specVersion,
|
9537
|
+
version: MINIMETADATA_VERSION,
|
9538
|
+
data: null,
|
9539
|
+
extra: {
|
9540
|
+
disable: true
|
9541
|
+
}
|
9542
|
+
};
|
9543
|
+
const existentialDeposit = tryGetConstantValue(metadataRpc, "Balances", "ExistentialDeposit")?.toString();
|
9544
|
+
const nominationPoolsPalletId = tryGetConstantValue(metadataRpc, "NominationPools", "PalletId")?.asText();
|
9545
|
+
const hasSubtensorPallet = !!tryGetConstantValue(metadataRpc, "SubtensorModule", "KeySwapCost");
|
9546
|
+
const hasFreezesItem = Boolean(unifiedMetadata.pallets.find(({
|
9547
|
+
name
|
9548
|
+
}) => name === "Balances")?.storage?.items.find(({
|
9549
|
+
name
|
9550
|
+
}) => name === "Freezes"));
|
9551
|
+
const useLegacyTransferableCalculation = !hasFreezesItem;
|
9552
|
+
compactMetadata(metadata, [{
|
9553
|
+
pallet: "System",
|
9554
|
+
constants: ["Version", "SS58Prefix"],
|
9555
|
+
items: ["Account"]
|
9556
|
+
}, {
|
9557
|
+
pallet: "Balances",
|
9558
|
+
items: ["Reserves", "Holds", "Locks", "Freezes"]
|
9559
|
+
}, {
|
9560
|
+
pallet: "NominationPools",
|
9561
|
+
items: ["PoolMembers", "BondedPools", "Metadata"]
|
9562
|
+
}, {
|
9563
|
+
pallet: "Staking",
|
9564
|
+
items: ["Ledger"]
|
9565
|
+
},
|
9566
|
+
// TotalColdkeyStake is used until v.2.2.1, then it is replaced by StakingHotkeys+Stake
|
9567
|
+
// Need to keep TotalColdkeyStake for a while so chaindata keeps including it in miniMetadatas, so it doesnt break old versions of the wallet
|
9568
|
+
// TODO: Since chaindata v4 this is safe to now delete
|
9569
|
+
{
|
9570
|
+
pallet: "SubtensorModule",
|
9571
|
+
items: ["TotalColdkeyStake", "StakingHotkeys", "Stake"]
|
9572
|
+
}], [{
|
9573
|
+
runtimeApi: "StakeInfoRuntimeApi",
|
9574
|
+
methods: ["get_stake_info_for_coldkey"]
|
9575
|
+
}, {
|
9576
|
+
runtimeApi: "SubnetInfoRuntimeApi",
|
9577
|
+
methods: ["get_dynamic_info"]
|
9578
|
+
}]);
|
9579
|
+
return {
|
9580
|
+
id,
|
9581
|
+
source,
|
9582
|
+
chainId,
|
9583
|
+
specVersion,
|
9584
|
+
version: MINIMETADATA_VERSION,
|
9585
|
+
data: encodeMetadata(metadata),
|
9586
|
+
extra: {
|
9587
|
+
useLegacyTransferableCalculation,
|
9588
|
+
existentialDeposit,
|
9589
|
+
nominationPoolsPalletId,
|
9590
|
+
hasSubtensorPallet
|
9591
|
+
}
|
9592
|
+
};
|
9593
|
+
};
|
9594
|
+
|
9595
|
+
const getTransferCallData$2 = ({
|
9596
|
+
from,
|
9597
|
+
to,
|
9598
|
+
value,
|
9599
|
+
token,
|
9600
|
+
type,
|
9601
|
+
metadataRpc
|
9602
|
+
}) => {
|
9603
|
+
if (!isTokenOfType(token, MODULE_TYPE$2)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$2}.`);
|
9604
|
+
const {
|
9605
|
+
unifiedMetadata,
|
9606
|
+
lookupFn,
|
9607
|
+
builder
|
9608
|
+
} = parseMetadataRpc(metadataRpc);
|
9609
|
+
const method = getTransferMethod$1(type, unifiedMetadata, lookupFn);
|
9610
|
+
const {
|
9611
|
+
codec,
|
9612
|
+
location
|
9613
|
+
} = builder.buildCall("Balances", method);
|
9614
|
+
const args = getEncodedArgs(method, to, value, codec);
|
9615
|
+
const callData = Binary.fromBytes(mergeUint8([new Uint8Array(location), args]));
|
9616
|
+
return {
|
9617
|
+
address: from,
|
9618
|
+
method: callData.asHex()
|
9619
|
+
};
|
9620
|
+
};
|
9621
|
+
const getTransferMethod$1 = (type, unifiedMetadata, lookupFn) => {
|
9622
|
+
switch (type) {
|
9623
|
+
case "keep-alive":
|
9624
|
+
return "transfer_keep_alive";
|
9625
|
+
case "all":
|
9626
|
+
return "transfer_all";
|
9627
|
+
case "allow-death":
|
9628
|
+
{
|
9629
|
+
const callType = unifiedMetadata.pallets.find(pallet => pallet.name === "Balances")?.calls?.type;
|
9630
|
+
if (callType) {
|
9631
|
+
const palletCalls = lookupFn(callType);
|
9632
|
+
if (palletCalls.type === "enum" && palletCalls.value["transfer_allow_death"]) return "transfer_allow_death";
|
9633
|
+
}
|
9634
|
+
|
9635
|
+
// legacy fallback
|
9636
|
+
return "transfer";
|
9637
|
+
}
|
9638
|
+
}
|
9639
|
+
};
|
9640
|
+
const getEncodedArgs = (method, to, value, argsCodec) => {
|
9641
|
+
try {
|
9642
|
+
switch (method) {
|
9643
|
+
case "transfer_allow_death":
|
9644
|
+
case "transfer_keep_alive":
|
9645
|
+
case "transfer":
|
9646
|
+
return getTransferEncodedArgs(to, value, argsCodec);
|
9647
|
+
case "transfer_all":
|
9648
|
+
return getTransferAllEncodedArgs(to, argsCodec);
|
9649
|
+
}
|
9650
|
+
} catch {
|
9651
|
+
throw new Error(`Failed to encode arguments for method ${method}, ${to}, ${value}`);
|
9652
|
+
}
|
9653
|
+
};
|
9654
|
+
const getEncodedValue = (codec, possibleValue) => {
|
9655
|
+
for (const getArgs of possibleValue) {
|
9656
|
+
try {
|
9657
|
+
return codec.enc(getArgs());
|
9658
|
+
} catch {
|
9659
|
+
// wrong inputs, ignore and try the next one
|
9660
|
+
}
|
9661
|
+
}
|
9662
|
+
throw new Error("Failed to encode");
|
9663
|
+
};
|
9664
|
+
|
9665
|
+
// same inputs for both KeepAlive and allowDeath
|
9666
|
+
const getTransferEncodedArgs = (to, value, codec) => {
|
9667
|
+
return getEncodedValue(codec, [() => ({
|
9668
|
+
dest: Enum("Id", to),
|
9669
|
+
value: BigInt(value)
|
9670
|
+
})]);
|
9671
|
+
};
|
9672
|
+
const getTransferAllEncodedArgs = (to, codec) => {
|
9673
|
+
return getEncodedValue(codec, [() => ({
|
9674
|
+
dest: Enum("Id", to),
|
9675
|
+
keep_alive: false
|
9676
|
+
})]);
|
9677
|
+
};
|
9678
|
+
|
9679
|
+
const SUBSCRIPTION_INTERVAL$2 = 6_000;
|
9680
|
+
const subscribeBalances$2 = ({
|
9681
|
+
networkId,
|
9682
|
+
addressesByToken,
|
9683
|
+
connector,
|
9684
|
+
miniMetadata
|
9685
|
+
}) => {
|
9686
|
+
return new Observable(subscriber => {
|
9687
|
+
const abortController = new AbortController();
|
9688
|
+
|
9689
|
+
// on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
|
9690
|
+
// => poll values for each block
|
9691
|
+
const poll = async () => {
|
9692
|
+
try {
|
9693
|
+
if (abortController.signal.aborted) return;
|
9694
|
+
const balances = await fetchBalances$2({
|
9695
|
+
networkId,
|
9696
|
+
addressesByToken,
|
9697
|
+
connector,
|
9698
|
+
miniMetadata
|
9699
|
+
});
|
9700
|
+
if (abortController.signal.aborted) return;
|
9701
|
+
subscriber.next(balances);
|
9702
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$2);
|
9703
|
+
} catch (error) {
|
9704
|
+
log.error("Error", {
|
9705
|
+
module: MODULE_TYPE$2,
|
9706
|
+
networkId,
|
9707
|
+
miniMetadata,
|
9708
|
+
addressesByToken,
|
9709
|
+
error
|
9710
|
+
});
|
9711
|
+
subscriber.error(error);
|
9712
|
+
}
|
9713
|
+
};
|
9714
|
+
poll();
|
9715
|
+
return () => {
|
9716
|
+
abortController.abort();
|
9717
|
+
};
|
9718
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
9719
|
+
};
|
9720
|
+
|
9721
|
+
const SubNativeBalanceModule = {
|
9722
|
+
type: MODULE_TYPE$2,
|
9723
|
+
platform: PLATFORM$2,
|
9724
|
+
getMiniMetadata: getMiniMetadata$2,
|
9725
|
+
fetchTokens: fetchTokens$2,
|
9726
|
+
fetchBalances: fetchBalances$2,
|
9727
|
+
subscribeBalances: subscribeBalances$2,
|
9728
|
+
getTransferCallData: getTransferCallData$2
|
9729
|
+
};
|
9730
|
+
|
9731
|
+
const MODULE_TYPE$1 = SubPsp22TokenSchema.shape.type.value;
|
9732
|
+
const PLATFORM$1 = SubPsp22TokenSchema.shape.platform.value;
|
9733
|
+
|
9734
|
+
// to be used by chaindata too
|
9735
|
+
z.strictObject({
|
9736
|
+
contractAddress: SubPsp22TokenSchema.shape.contractAddress,
|
9737
|
+
...TokenConfigBaseSchema.shape
|
9738
|
+
});
|
9739
|
+
|
9740
|
+
const fetchBalances$1 = async ({
|
9741
|
+
networkId,
|
9742
|
+
addressesByToken,
|
9743
|
+
connector
|
9744
|
+
}) => {
|
9745
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
9746
|
+
if (!balanceDefs.length) return {
|
9747
|
+
success: [],
|
9748
|
+
errors: []
|
9749
|
+
};
|
9750
|
+
const registry = new TypeRegistry();
|
9751
|
+
const Psp22Abi = new Abi(psp22Abi);
|
9752
|
+
const contractCall = makeContractCaller({
|
9753
|
+
chainConnector: connector,
|
9754
|
+
chainId: networkId,
|
9755
|
+
registry
|
9756
|
+
});
|
9757
|
+
const results = await Promise.allSettled(balanceDefs.map(async ({
|
9758
|
+
token,
|
9759
|
+
address
|
9760
|
+
}) => {
|
9761
|
+
const result = await contractCall(address, token.contractAddress, Psp22Abi.findMessage("PSP22::balance_of").toU8a([address]));
|
9762
|
+
if (!result.result.isOk) throw new Error("Failed to fetch balance");
|
9763
|
+
const value = registry.createType("Balance", result.result.asOk.data).toString();
|
9764
|
+
const balance = {
|
9765
|
+
source: "substrate-psp22",
|
9766
|
+
status: "live",
|
9767
|
+
address,
|
9768
|
+
networkId: token.networkId,
|
9769
|
+
tokenId: token.id,
|
9770
|
+
value
|
9771
|
+
};
|
9772
|
+
return balance;
|
9773
|
+
}));
|
9774
|
+
return results.reduce((acc, result) => {
|
9775
|
+
if (result.status === "fulfilled") acc.success.push(result.value);else {
|
9776
|
+
const error = result.reason;
|
9777
|
+
acc.errors.push({
|
9778
|
+
tokenId: error.tokenId,
|
9779
|
+
address: error.address,
|
9780
|
+
error
|
9781
|
+
});
|
9782
|
+
}
|
9783
|
+
return acc;
|
9784
|
+
}, {
|
9785
|
+
success: [],
|
9786
|
+
errors: []
|
9787
|
+
});
|
9788
|
+
};
|
9789
|
+
|
9790
|
+
const fetchTokens$1 = async ({
|
9791
|
+
networkId,
|
9792
|
+
tokens,
|
9793
|
+
connector
|
9794
|
+
}) => {
|
9795
|
+
if (!tokens.length) return [];
|
9796
|
+
const registry = new TypeRegistry();
|
9797
|
+
const Psp22Abi = new Abi(psp22Abi);
|
9798
|
+
|
9799
|
+
// TODO: Use `decodeOutput` from `./util/decodeOutput`
|
9800
|
+
const contractCall = makeContractCaller({
|
9801
|
+
chainConnector: connector,
|
9802
|
+
chainId: networkId,
|
9803
|
+
registry
|
9804
|
+
});
|
9805
|
+
const tokenList = {};
|
9806
|
+
for (const tokenConfig of tokens ?? []) {
|
9807
|
+
try {
|
9808
|
+
let symbol = tokenConfig?.symbol ?? "Unit";
|
9809
|
+
let decimals = tokenConfig?.decimals ?? 0;
|
9810
|
+
const contractAddress = tokenConfig?.contractAddress ?? undefined;
|
9811
|
+
if (contractAddress === undefined) continue;
|
9812
|
+
await (async () => {
|
9813
|
+
const [symbolResult, decimalsResult] = await Promise.all([contractCall(contractAddress, contractAddress, Psp22Abi.findMessage("PSP22Metadata::token_symbol").toU8a([])), contractCall(contractAddress, contractAddress, Psp22Abi.findMessage("PSP22Metadata::token_decimals").toU8a([]))]);
|
9814
|
+
|
9815
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
9816
|
+
const symbolData = symbolResult.toJSON()?.result?.ok?.data;
|
9817
|
+
symbol = typeof symbolData === "string" && symbolData.startsWith("0x") ? u8aToString(registry.createType("Option<Vec<u8>>",
|
9818
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
9819
|
+
symbolResult.toJSON()?.result?.ok?.data)?.value)?.replace(/\p{C}/gu, "") : symbol;
|
9820
|
+
|
9821
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
9822
|
+
const decimalsData = decimalsResult.toJSON()?.result?.ok?.data;
|
9823
|
+
decimals = typeof decimalsData === "string" && decimalsData.startsWith("0x") ? hexToNumber(decimalsData) : decimals;
|
9824
|
+
})();
|
9825
|
+
const id = subPsp22TokenId(networkId, contractAddress);
|
9826
|
+
const token = {
|
9827
|
+
id,
|
9828
|
+
type: "substrate-psp22",
|
9829
|
+
platform: "polkadot",
|
9830
|
+
isDefault: tokenConfig.isDefault ?? true,
|
9831
|
+
symbol,
|
9832
|
+
decimals,
|
9833
|
+
name: tokenConfig?.name || symbol,
|
9834
|
+
logo: tokenConfig?.logo,
|
9835
|
+
contractAddress,
|
9836
|
+
networkId
|
9837
|
+
};
|
9838
|
+
if (tokenConfig?.coingeckoId) token.coingeckoId = tokenConfig?.coingeckoId;
|
9839
|
+
if (tokenConfig?.mirrorOf) token.mirrorOf = tokenConfig?.mirrorOf;
|
9840
|
+
tokenList[token.id] = token;
|
9841
|
+
} catch (error) {
|
9842
|
+
log.error(`Failed to build substrate-psp22 token ${tokenConfig.contractAddress} (${tokenConfig.symbol}) on ${networkId}`, error?.message ?? error);
|
9843
|
+
continue;
|
9844
|
+
}
|
9845
|
+
}
|
9846
|
+
return values(tokenList).filter(t => {
|
9847
|
+
const parsed = SubPsp22TokenSchema.safeParse(t);
|
9848
|
+
if (!parsed.success) log.warn(`Ignoring invalid token ${MODULE_TYPE$1}`, t);
|
9849
|
+
return parsed.success;
|
9850
|
+
});
|
9851
|
+
};
|
9852
|
+
|
9853
|
+
const getMiniMetadata$1 = ({
|
9854
|
+
networkId,
|
9855
|
+
specVersion,
|
9856
|
+
metadataRpc
|
9857
|
+
}) => {
|
9858
|
+
const source = MODULE_TYPE$1;
|
9859
|
+
const chainId = networkId;
|
9860
|
+
const systemVersion = getConstantValue(metadataRpc, "System", "Version");
|
9861
|
+
if (specVersion !== systemVersion.spec_version) log.warn("specVersion mismatch", {
|
9862
|
+
networkId,
|
9863
|
+
specVersion,
|
9864
|
+
systemVersion
|
9865
|
+
});
|
9866
|
+
const id = deriveMiniMetadataId({
|
9867
|
+
source,
|
9868
|
+
chainId,
|
9869
|
+
specVersion
|
9870
|
+
});
|
9871
|
+
const {
|
9872
|
+
unifiedMetadata
|
9873
|
+
} = parseMetadataRpc(metadataRpc);
|
9874
|
+
if (unifiedMetadata.version < 14) throw new Error(`Unsupported metadata version: ${unifiedMetadata.version}. Minimum required is 14.`);
|
9875
|
+
return {
|
9876
|
+
id,
|
9877
|
+
source,
|
9878
|
+
chainId,
|
9879
|
+
specVersion,
|
9880
|
+
version: MINIMETADATA_VERSION,
|
9881
|
+
data: null,
|
9882
|
+
extra: null
|
9883
|
+
};
|
9884
|
+
};
|
9885
|
+
|
9886
|
+
const getTransferCallData$1 = async ({
|
9887
|
+
from,
|
9888
|
+
to,
|
9889
|
+
value,
|
9890
|
+
token,
|
9891
|
+
metadataRpc,
|
9892
|
+
connector
|
9893
|
+
}) => {
|
9894
|
+
if (!isTokenOfType(token, MODULE_TYPE$1)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE$1}.`);
|
9895
|
+
const networkId = parseTokenId(token.id).networkId;
|
9896
|
+
const {
|
9897
|
+
builder
|
9898
|
+
} = parseMetadataRpc(metadataRpc);
|
9899
|
+
const {
|
9900
|
+
codec,
|
9901
|
+
location
|
9902
|
+
} = builder.buildCall("Contracts", "call");
|
9903
|
+
const Psp22Abi = new Abi(psp22Abi);
|
9904
|
+
const registry = new TypeRegistry();
|
9905
|
+
const contractCall = makeContractCaller({
|
9906
|
+
chainConnector: connector,
|
9907
|
+
chainId: networkId,
|
9908
|
+
registry
|
9909
|
+
});
|
9910
|
+
|
9911
|
+
// TODO use papi contract api
|
9912
|
+
const data = Psp22Abi.findMessage("PSP22::transfer").toU8a([to, value, undefined]);
|
9913
|
+
const hexData = registry.createType("Vec<u8>", data).toHex();
|
9914
|
+
const dryRunResult = await contractCall(from, token.contractAddress, data);
|
9915
|
+
const args = codec.enc({
|
9916
|
+
dest: Enum("Id", token.contractAddress),
|
9917
|
+
value: 0,
|
9918
|
+
gas_limit: {
|
9919
|
+
ref_time: dryRunResult.gasRequired.refTime.toBigInt(),
|
9920
|
+
proof_size: dryRunResult.gasRequired.proofSize.toBigInt()
|
9921
|
+
},
|
9922
|
+
storage_deposit_limit: dryRunResult.storageDeposit.isCharge ? dryRunResult.storageDeposit.asCharge.toBigInt() : null,
|
9923
|
+
data: Binary.fromHex(hexData)
|
9924
|
+
});
|
9925
|
+
const callData = Binary.fromBytes(mergeUint8([new Uint8Array(location), args]));
|
9926
|
+
return {
|
9927
|
+
address: from,
|
9928
|
+
method: callData.asHex()
|
9929
|
+
};
|
9930
|
+
};
|
9931
|
+
|
9932
|
+
const SUBSCRIPTION_INTERVAL$1 = 6_000;
|
9933
|
+
const subscribeBalances$1 = ({
|
9934
|
+
networkId,
|
9935
|
+
addressesByToken,
|
9936
|
+
connector,
|
9937
|
+
miniMetadata
|
9938
|
+
}) => {
|
9939
|
+
return new Observable(subscriber => {
|
9940
|
+
const abortController = new AbortController();
|
9941
|
+
|
9942
|
+
// on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
|
9943
|
+
// => poll values for each block
|
9944
|
+
const poll = async () => {
|
9945
|
+
try {
|
9946
|
+
if (abortController.signal.aborted) return;
|
9947
|
+
const balances = await fetchBalances$1({
|
9948
|
+
networkId,
|
9949
|
+
addressesByToken,
|
9950
|
+
connector,
|
9951
|
+
miniMetadata
|
9952
|
+
});
|
9953
|
+
if (abortController.signal.aborted) return;
|
9954
|
+
subscriber.next(balances);
|
9955
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL$1);
|
9956
|
+
} catch (error) {
|
9957
|
+
log.error("Error", {
|
9958
|
+
module: MODULE_TYPE$1,
|
9959
|
+
networkId,
|
9960
|
+
miniMetadata,
|
9961
|
+
addressesByToken,
|
9962
|
+
error
|
9963
|
+
});
|
9964
|
+
subscriber.error(error);
|
9965
|
+
}
|
9966
|
+
};
|
9967
|
+
poll();
|
9968
|
+
return () => {
|
9969
|
+
abortController.abort();
|
9970
|
+
};
|
9971
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
9972
|
+
};
|
9973
|
+
|
9974
|
+
const SubPsp22BalanceModule = {
|
9975
|
+
type: MODULE_TYPE$1,
|
9976
|
+
platform: PLATFORM$1,
|
9977
|
+
getMiniMetadata: getMiniMetadata$1,
|
9978
|
+
fetchTokens: fetchTokens$1,
|
9979
|
+
fetchBalances: fetchBalances$1,
|
9980
|
+
subscribeBalances: subscribeBalances$1,
|
9981
|
+
getTransferCallData: getTransferCallData$1
|
9982
|
+
};
|
9983
|
+
|
9984
|
+
const MODULE_TYPE = SubTokensTokenSchema.shape.type.value;
|
9985
|
+
const PLATFORM = SubTokensTokenSchema.shape.platform.value;
|
9986
|
+
|
9987
|
+
// to be used by chaindata too
|
9988
|
+
z.strictObject({
|
9989
|
+
onChainId: SubTokensTokenSchema.shape.onChainId,
|
9990
|
+
...TokenConfigBaseSchema.shape,
|
9991
|
+
// force these 3 fields because in this module we wont pull anything from chain
|
9992
|
+
symbol: z.string().nonempty(),
|
9993
|
+
decimals: z.number(),
|
9994
|
+
existentialDeposit: z.string().nonempty()
|
9995
|
+
});
|
9996
|
+
|
9997
|
+
// Do not use this type outside of this module
|
9998
|
+
|
9999
|
+
// Do not use this type outside of this module
|
10000
|
+
|
10001
|
+
const fetchBalances = async ({
|
10002
|
+
networkId,
|
10003
|
+
addressesByToken,
|
10004
|
+
connector,
|
10005
|
+
miniMetadata
|
10006
|
+
}) => {
|
10007
|
+
const balanceDefs = getBalanceDefs(addressesByToken);
|
10008
|
+
if (!miniMetadata?.data) {
|
10009
|
+
log.warn("MiniMetadata is required for fetching balances");
|
10010
|
+
return {
|
10011
|
+
success: [],
|
10012
|
+
errors: balanceDefs.map(def => ({
|
10013
|
+
tokenId: def.token.id,
|
10014
|
+
address: def.address,
|
10015
|
+
error: new Error("Minimetadata is required for fetching balances")
|
10016
|
+
}))
|
10017
|
+
};
|
10018
|
+
}
|
10019
|
+
if (miniMetadata.source !== MODULE_TYPE) {
|
10020
|
+
log.warn(`Ignoring miniMetadata with source ${miniMetadata.source} in ${MODULE_TYPE}.`);
|
10021
|
+
return {
|
10022
|
+
success: [],
|
10023
|
+
errors: balanceDefs.map(def => ({
|
10024
|
+
tokenId: def.token.id,
|
10025
|
+
address: def.address,
|
10026
|
+
error: new Error(`Invalid request: miniMetadata source is not ${MODULE_TYPE}`)
|
10027
|
+
}))
|
10028
|
+
};
|
10029
|
+
}
|
10030
|
+
if (miniMetadata.chainId !== networkId) {
|
10031
|
+
log.warn(`Ignoring miniMetadata with chainId ${miniMetadata.chainId} in ${MODULE_TYPE}. Expected chainId is ${networkId}`);
|
10032
|
+
return {
|
10033
|
+
success: [],
|
10034
|
+
errors: balanceDefs.map(def => ({
|
10035
|
+
tokenId: def.token.id,
|
10036
|
+
address: def.address,
|
10037
|
+
error: new Error(`Invalid request: Expected chainId is ${networkId}`)
|
10038
|
+
}))
|
10039
|
+
};
|
10040
|
+
}
|
10041
|
+
const queries = buildQueries(networkId, balanceDefs, miniMetadata);
|
10042
|
+
const balances = await new RpcStateQueryHelper(connector, queries).fetch();
|
10043
|
+
return balanceDefs.reduce((acc, def) => {
|
10044
|
+
const balance = balances.find(b => b?.address === def.address && b?.tokenId === def.token.id);
|
10045
|
+
if (balance) acc.success.push(balance);
|
10046
|
+
//if no entry consider empty balance
|
10047
|
+
else acc.success.push({
|
10048
|
+
address: def.address,
|
10049
|
+
networkId,
|
10050
|
+
tokenId: def.token.id,
|
10051
|
+
source: MODULE_TYPE,
|
10052
|
+
status: "live",
|
10053
|
+
values: [{
|
10054
|
+
type: "free",
|
10055
|
+
label: "free",
|
10056
|
+
amount: "0"
|
10057
|
+
}, {
|
10058
|
+
type: "locked",
|
10059
|
+
label: "frozen",
|
10060
|
+
amount: "0"
|
10061
|
+
}]
|
10062
|
+
});
|
10063
|
+
return acc;
|
10064
|
+
}, {
|
10065
|
+
success: [],
|
10066
|
+
errors: []
|
10067
|
+
});
|
10068
|
+
};
|
10069
|
+
const buildQueries = (networkId, balanceDefs, miniMetadata) => {
|
10070
|
+
const networkStorageCoders = buildNetworkStorageCoders(networkId, miniMetadata, {
|
10071
|
+
storage: [miniMetadata.extra.palletId, "Accounts"]
|
10072
|
+
});
|
10073
|
+
return balanceDefs.map(({
|
10074
|
+
token,
|
10075
|
+
address
|
10076
|
+
}) => {
|
10077
|
+
const scaleCoder = networkStorageCoders?.storage;
|
10078
|
+
const getStateKey = onChainId => {
|
10079
|
+
try {
|
10080
|
+
return scaleCoder.keys.enc(address, papiParse(onChainId));
|
10081
|
+
} catch {
|
10082
|
+
return null;
|
10083
|
+
}
|
10084
|
+
};
|
10085
|
+
const stateKey = getStateKey(token.onChainId);
|
10086
|
+
if (!stateKey) {
|
10087
|
+
log.warn(`Invalid assetId / address in ${networkId} storage query ${token.onChainId} / ${address}`);
|
10088
|
+
return null;
|
10089
|
+
}
|
10090
|
+
const decodeResult = change => {
|
10091
|
+
/** NOTE: This type is only a hint for typescript, the chain can actually return whatever it wants to */
|
10092
|
+
|
10093
|
+
const decoded = decodeScale(scaleCoder, change, `Failed to decode substrate-tokens balance on chain ${networkId}`) ?? {
|
10094
|
+
free: 0n,
|
10095
|
+
reserved: 0n,
|
10096
|
+
frozen: 0n
|
10097
|
+
};
|
10098
|
+
const free = (decoded?.free ?? 0n).toString();
|
10099
|
+
const reserved = (decoded?.reserved ?? 0n).toString();
|
10100
|
+
const frozen = (decoded?.frozen ?? 0n).toString();
|
10101
|
+
const balanceValues = [{
|
10102
|
+
type: "free",
|
10103
|
+
label: "free",
|
10104
|
+
amount: free.toString()
|
10105
|
+
}, {
|
10106
|
+
type: "reserved",
|
10107
|
+
label: "reserved",
|
10108
|
+
amount: reserved.toString()
|
10109
|
+
}, {
|
10110
|
+
type: "locked",
|
10111
|
+
label: "frozen",
|
10112
|
+
amount: frozen.toString()
|
10113
|
+
}];
|
10114
|
+
return {
|
10115
|
+
source: "substrate-tokens",
|
10116
|
+
status: "live",
|
10117
|
+
address,
|
10118
|
+
networkId,
|
10119
|
+
tokenId: token.id,
|
10120
|
+
values: balanceValues
|
10121
|
+
};
|
10122
|
+
};
|
10123
|
+
return {
|
10124
|
+
chainId: networkId,
|
10125
|
+
stateKey,
|
10126
|
+
decodeResult
|
10127
|
+
};
|
10128
|
+
}).filter(isNotNil);
|
10129
|
+
};
|
10130
|
+
|
10131
|
+
const fetchTokens = async ({
|
10132
|
+
networkId,
|
10133
|
+
tokens,
|
10134
|
+
miniMetadata
|
10135
|
+
}) => {
|
10136
|
+
if (!miniMetadata?.data) return [];
|
10137
|
+
|
10138
|
+
// in this module we do not fetch any token information from the chain
|
10139
|
+
// this is because there are basically as many pallet implementations as there are networks (Expect Chaos, they said)
|
10140
|
+
// we rely on the config to provide all the info we need
|
10141
|
+
return tokens.map(tokenConfig => assign({
|
10142
|
+
id: subTokensTokenId(networkId, tokenConfig.onChainId),
|
10143
|
+
type: MODULE_TYPE,
|
10144
|
+
platform: PLATFORM,
|
10145
|
+
networkId,
|
10146
|
+
onChainId: tokenConfig.onChainId,
|
10147
|
+
symbol: tokenConfig.symbol ?? "Unit",
|
10148
|
+
decimals: tokenConfig.decimals ?? 0,
|
10149
|
+
name: tokenConfig.name ?? tokenConfig.symbol ?? "Unit",
|
10150
|
+
existentialDeposit: tokenConfig.existentialDeposit ?? "0",
|
10151
|
+
isDefault: true
|
10152
|
+
}, tokenConfig));
|
10153
|
+
};
|
10154
|
+
|
10155
|
+
const getMiniMetadata = ({
|
10156
|
+
networkId,
|
10157
|
+
specVersion,
|
10158
|
+
metadataRpc,
|
10159
|
+
config
|
10160
|
+
}) => {
|
10161
|
+
const source = MODULE_TYPE;
|
10162
|
+
const chainId = networkId;
|
10163
|
+
const systemVersion = getConstantValue(metadataRpc, "System", "Version");
|
10164
|
+
if (specVersion !== systemVersion.spec_version) log.warn("specVersion mismatch", {
|
10165
|
+
networkId,
|
10166
|
+
specVersion,
|
10167
|
+
systemVersion
|
10168
|
+
});
|
10169
|
+
const id = deriveMiniMetadataId({
|
10170
|
+
source,
|
10171
|
+
chainId,
|
10172
|
+
specVersion
|
10173
|
+
});
|
10174
|
+
const {
|
10175
|
+
unifiedMetadata
|
10176
|
+
} = parseMetadataRpc(metadataRpc);
|
10177
|
+
if (unifiedMetadata.version < 14) throw new Error(`Unsupported metadata version: ${unifiedMetadata.version}. Minimum required is 14.`);
|
10178
|
+
const extra = {
|
10179
|
+
palletId: config?.palletId ?? "Tokens"
|
10180
|
+
};
|
10181
|
+
return {
|
10182
|
+
id,
|
10183
|
+
source,
|
10184
|
+
chainId,
|
10185
|
+
specVersion,
|
10186
|
+
version: MINIMETADATA_VERSION,
|
10187
|
+
data: getData(metadataRpc, extra.palletId),
|
10188
|
+
extra
|
10189
|
+
};
|
10190
|
+
};
|
10191
|
+
const getData = (metadataRpc, pallet) => {
|
10192
|
+
const {
|
10193
|
+
metadata,
|
10194
|
+
unifiedMetadata
|
10195
|
+
} = parseMetadataRpc(metadataRpc);
|
10196
|
+
|
10197
|
+
// ensure the network has all the required bits
|
10198
|
+
if (!hasStorageItems(unifiedMetadata, pallet, ["Accounts"])) return null;
|
10199
|
+
compactMetadata(metadata, [{
|
10200
|
+
pallet,
|
10201
|
+
items: ["Accounts"]
|
10202
|
+
}]);
|
10203
|
+
return encodeMetadata(metadata);
|
10204
|
+
};
|
10205
|
+
|
10206
|
+
const getTransferCallData = ({
|
10207
|
+
from,
|
10208
|
+
to,
|
10209
|
+
value,
|
10210
|
+
token,
|
10211
|
+
type,
|
10212
|
+
metadataRpc,
|
10213
|
+
config
|
10214
|
+
}) => {
|
10215
|
+
if (!isTokenOfType(token, MODULE_TYPE)) throw new Error(`Token type ${token.type} is not ${MODULE_TYPE}.`);
|
10216
|
+
const {
|
10217
|
+
builder
|
10218
|
+
} = parseMetadataRpc(metadataRpc);
|
10219
|
+
|
10220
|
+
// each chain has its own way of encoding the transfer call data
|
10221
|
+
// let's try our luck until one works!
|
10222
|
+
const options = getCallDataOptions(to, token, value, type, config);
|
10223
|
+
const callData = getCallDataFromOptions(builder, options);
|
10224
|
+
return {
|
10225
|
+
address: from,
|
10226
|
+
method: callData.asHex()
|
10227
|
+
};
|
10228
|
+
};
|
10229
|
+
const getTransferMethod = type => {
|
10230
|
+
switch (type) {
|
10231
|
+
case "keep-alive":
|
10232
|
+
return "transfer_keep_alive";
|
10233
|
+
case "all":
|
10234
|
+
return "transfer_all";
|
10235
|
+
case "allow-death":
|
10236
|
+
return "transfer";
|
10237
|
+
}
|
10238
|
+
};
|
10239
|
+
const getCallDataFromOptions = (builder, options) => {
|
10240
|
+
for (const {
|
10241
|
+
pallet,
|
10242
|
+
method,
|
10243
|
+
getArgs
|
10244
|
+
} of options) {
|
10245
|
+
try {
|
10246
|
+
return buildCallData(builder, pallet, method, getArgs());
|
10247
|
+
} catch {
|
10248
|
+
// wrong inputs, ignore and try the next one
|
10249
|
+
}
|
10250
|
+
}
|
10251
|
+
throw new Error("Failed to encode call data");
|
10252
|
+
};
|
10253
|
+
const buildCallData = (builder, pallet, method, args) => {
|
10254
|
+
const {
|
10255
|
+
location,
|
10256
|
+
codec
|
10257
|
+
} = builder.buildCall(pallet, method);
|
10258
|
+
return Binary.fromBytes(mergeUint8([new Uint8Array(location), codec.enc(args)]));
|
10259
|
+
};
|
10260
|
+
const getCallDataOptions = (to, token, value, type, config) => {
|
10261
|
+
const onChainId = papiParse(token.onChainId);
|
10262
|
+
const method = getTransferMethod(type);
|
10263
|
+
return [{
|
10264
|
+
pallet: "Currencies",
|
10265
|
+
method: "transfer",
|
10266
|
+
getArgs: () => ({
|
10267
|
+
dest: Enum("Id", to),
|
10268
|
+
currency_id: onChainId,
|
10269
|
+
amount: BigInt(value)
|
10270
|
+
})
|
10271
|
+
}, {
|
10272
|
+
pallet: "Currencies",
|
10273
|
+
method: "transfer",
|
10274
|
+
getArgs: () => ({
|
10275
|
+
dest: to,
|
10276
|
+
currency_id: onChainId,
|
10277
|
+
amount: BigInt(value)
|
10278
|
+
})
|
10279
|
+
}, {
|
10280
|
+
pallet: "Tokens",
|
10281
|
+
method,
|
10282
|
+
getArgs: () => method === "transfer_all" ? {
|
10283
|
+
dest: Enum("Id", to),
|
10284
|
+
currency_id: onChainId,
|
10285
|
+
keepAlive: false
|
10286
|
+
} : {
|
10287
|
+
dest: Enum("Id", to),
|
10288
|
+
currency_id: onChainId,
|
10289
|
+
amount: BigInt(value)
|
10290
|
+
}
|
10291
|
+
}, {
|
10292
|
+
pallet: "Tokens",
|
10293
|
+
method,
|
10294
|
+
getArgs: () => method === "transfer_all" ? {
|
10295
|
+
dest: to,
|
10296
|
+
currency_id: onChainId,
|
10297
|
+
keepAlive: false
|
10298
|
+
} : {
|
10299
|
+
dest: to,
|
10300
|
+
currency_id: onChainId,
|
10301
|
+
amount: BigInt(value)
|
10302
|
+
}
|
10303
|
+
}].concat(...(config?.palletId ? [{
|
10304
|
+
pallet: config.palletId,
|
10305
|
+
method,
|
10306
|
+
getArgs: () => method === "transfer_all" ? {
|
10307
|
+
dest: Enum("Id", to),
|
10308
|
+
currency_id: onChainId,
|
10309
|
+
keepAlive: false
|
10310
|
+
} : {
|
10311
|
+
dest: Enum("Id", to),
|
10312
|
+
currency_id: onChainId,
|
10313
|
+
amount: BigInt(value)
|
10314
|
+
}
|
10315
|
+
}, {
|
10316
|
+
pallet: config.palletId,
|
10317
|
+
method,
|
10318
|
+
getArgs: () => method === "transfer_all" ? {
|
10319
|
+
dest: to,
|
10320
|
+
currency_id: onChainId,
|
10321
|
+
keepAlive: false
|
10322
|
+
} : {
|
10323
|
+
dest: to,
|
10324
|
+
currency_id: onChainId,
|
10325
|
+
amount: BigInt(value)
|
10326
|
+
}
|
10327
|
+
}] : []));
|
10328
|
+
};
|
10329
|
+
|
10330
|
+
const SUBSCRIPTION_INTERVAL = 6_000;
|
10331
|
+
const subscribeBalances = ({
|
10332
|
+
networkId,
|
10333
|
+
addressesByToken,
|
10334
|
+
connector,
|
10335
|
+
miniMetadata
|
10336
|
+
}) => {
|
10337
|
+
return new Observable(subscriber => {
|
10338
|
+
const abortController = new AbortController();
|
10339
|
+
|
10340
|
+
// on hydration balances are fetched using a runtimeApi, which can't be subscribed to.
|
10341
|
+
// => poll values for each block
|
10342
|
+
const poll = async () => {
|
10343
|
+
try {
|
10344
|
+
if (abortController.signal.aborted) return;
|
10345
|
+
const balances = await fetchBalances({
|
10346
|
+
networkId,
|
10347
|
+
addressesByToken,
|
10348
|
+
connector,
|
10349
|
+
miniMetadata
|
10350
|
+
});
|
10351
|
+
if (abortController.signal.aborted) return;
|
10352
|
+
subscriber.next(balances);
|
10353
|
+
setTimeout(poll, SUBSCRIPTION_INTERVAL);
|
10354
|
+
} catch (error) {
|
10355
|
+
log.error("Error", {
|
10356
|
+
module: MODULE_TYPE,
|
10357
|
+
networkId,
|
10358
|
+
miniMetadata,
|
10359
|
+
addressesByToken,
|
10360
|
+
error
|
10361
|
+
});
|
10362
|
+
subscriber.error(error);
|
10363
|
+
}
|
10364
|
+
};
|
10365
|
+
poll();
|
10366
|
+
return () => {
|
10367
|
+
abortController.abort();
|
10368
|
+
};
|
10369
|
+
}).pipe(distinctUntilChanged(isEqual$1));
|
10370
|
+
};
|
10371
|
+
|
10372
|
+
const SubTokensBalanceModule = {
|
10373
|
+
type: MODULE_TYPE,
|
10374
|
+
platform: PLATFORM,
|
10375
|
+
getMiniMetadata,
|
10376
|
+
fetchTokens,
|
10377
|
+
fetchBalances,
|
10378
|
+
subscribeBalances,
|
10379
|
+
getTransferCallData
|
10380
|
+
};
|
10381
|
+
|
10382
|
+
const NEW_BALANCE_MODULES = [SubNativeBalanceModule, SubAssetsBalanceModule, SubHydrationBalanceModule, SubForeignAssetsBalanceModule, SubPsp22BalanceModule, SubTokensBalanceModule, EvmErc20BalanceModule, EvmUniswapV2BalanceModule, EvmNativeBalanceModule];
|
10383
|
+
|
10384
|
+
export { Balance, BalanceFormatter, BalanceValueGetter, Balances, Change24hCurrencyFormatter, DefaultBalanceModule, EvmErc20Module, EvmErc20TokenConfigSchema, EvmNativeModule, EvmNativeTokenConfigSchema, EvmUniswapV2Module, EvmUniswapV2TokenConfigSchema, FiatSumBalancesFormatter, NEW_BALANCE_MODULES, ONE_ALPHA_TOKEN$1 as ONE_ALPHA_TOKEN, PlanckSumBalancesFormatter, RpcStateQueryHelper, SCALE_FACTOR$1 as SCALE_FACTOR, SUBTENSOR_MIN_STAKE_AMOUNT_PLANK$1 as SUBTENSOR_MIN_STAKE_AMOUNT_PLANK, SUBTENSOR_ROOT_NETUID$1 as SUBTENSOR_ROOT_NETUID, SubAssetsModule, SubAssetsTokenConfigSchema, SubForeignAssetsModule, SubForeignAssetsTokenConfigSchema, SubNativeModule, SubNativeTokenConfigSchema, SubPsp22Module, SubPsp22TokenConfigSchema, SubTokensModule, SubTokensTokenConfigSchema, SumBalancesFormatter, TalismanBalancesDatabase, abiMulticall, balances, buildNetworkStorageCoders, buildStorageCoders, calculateAlphaPrice$1 as calculateAlphaPrice, calculateTaoAmountFromAlpha$1 as calculateTaoAmountFromAlpha, calculateTaoFromDynamicInfo$1 as calculateTaoFromDynamicInfo, compress, configureStore, db, decodeOutput, decompress, defaultBalanceModules, deriveMiniMetadataId, detectTransferMethod, erc20BalancesAggregatorAbi, excludeFromFeePayableLocks, excludeFromTransferableAmount, filterBaseLocks, filterMirrorTokens, getBalanceId, getLockTitle, getUniqueChainIds, getValueId, includeInTotalExtraAmount, makeContractCaller, uniswapV2PairAbi };
|