@vultisig/core-chain 2.20.0 → 2.21.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @vultisig/core-chain
2
2
 
3
+ ## 2.21.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#912](https://github.com/vultisig/vultisig-sdk/pull/912) [`403d5d5`](https://github.com/vultisig/vultisig-sdk/commit/403d5d5f7c7dba3e45cb818899db00f765541ecf) Thanks [@Ehsan-saradar](https://github.com/Ehsan-saradar)! - feat(solana): validator-metadata seam (Stakewiz, swappable)
8
+
9
+ Phase 2 of Solana native staking. Adds a swappable off-chain enrichment seam for
10
+ validator display metadata (name / logo / APY estimate / score) on top of the
11
+ Phase 1 on-chain `getVoteAccounts` rows, under
12
+ `@vultisig/core-chain/chains/solana/staking/metadata`:
13
+
14
+ - `ValidatorMetadataProvider` — provider interface; contract: never throws.
15
+ - `stakewizProvider` — concrete impl over api.stakewiz.com (`apy_estimate`
16
+ percent→fraction, `image`→logo, `wiz_score`→score). Degrades to an empty map
17
+ on any outage / non-OK / parse error so callers fall back to on-chain-only.
18
+ - `enrichValidators` — merges the metadata map onto the validator rows; the
19
+ provider is injectable for tests.
20
+
3
21
  ## 2.20.0
4
22
 
5
23
  ### Minor Changes
@@ -0,0 +1,25 @@
1
+ import { ValidatorMetadata } from '../models/validator.js';
2
+ /**
3
+ * Swappable seam for off-chain validator enrichment (name / logo / APY /
4
+ * score). The core stake/commission/activation data comes from the on-chain
5
+ * `getVoteAccounts` read; this interface only supplies display metadata, which
6
+ * is the part most likely to change source (Stakewiz today, validators.app or
7
+ * an in-house endpoint tomorrow). Keeping it behind an interface lets the picker
8
+ * and views stay independent of any single provider.
9
+ *
10
+ * Contract: implementations MUST NOT throw on a provider outage. A failed or
11
+ * rate-limited source returns a partial or empty map so callers degrade
12
+ * gracefully — falling back to a truncated vote pubkey and the on-chain
13
+ * commission, with no logo, never a crash.
14
+ *
15
+ * Port of iOS `ValidatorMetadataProvider`.
16
+ */
17
+ export type ValidatorMetadataProvider = {
18
+ /**
19
+ * Returns metadata keyed by vote pubkey for the requested validators. The
20
+ * returned map may be empty or cover only a subset of the input — a validator
21
+ * absent from the map simply has no enrichment available. Never throws.
22
+ */
23
+ metadata: (votePubkeys: string[]) => Promise<Record<string, ValidatorMetadata>>;
24
+ };
25
+ //# sourceMappingURL=ValidatorMetadataProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValidatorMetadataProvider.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/staking/metadata/ValidatorMetadataProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;OAIG;IACH,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAA;CAChF,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ValidatorMetadataProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValidatorMetadataProvider.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/staking/metadata/ValidatorMetadataProvider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { SolanaValidator } from '../models/validator.js';
2
+ import { ValidatorMetadataProvider } from './ValidatorMetadataProvider.js';
3
+ /**
4
+ * Merges off-chain metadata (name / logo / APY) onto on-chain validator rows
5
+ * via the swappable provider. The provider never throws — on an outage the rows
6
+ * pass through with empty metadata and the display layer falls back to a
7
+ * truncated vote pubkey + on-chain commission.
8
+ *
9
+ * Replaces the call site where iOS's view models invoke the
10
+ * `ValidatorMetadataProvider`; the provider is injectable for tests.
11
+ */
12
+ export declare const enrichValidatorsWithMetadata: (validators: SolanaValidator[], provider?: ValidatorMetadataProvider) => Promise<SolanaValidator[]>;
13
+ //# sourceMappingURL=enrichValidators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrichValidators.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/staking/metadata/enrichValidators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAErD,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACvC,YAAY,eAAe,EAAE,EAC7B,WAAU,yBAA6D,KACtE,OAAO,CAAC,eAAe,EAAE,CAM3B,CAAA"}
@@ -0,0 +1,18 @@
1
+ import { stakewizValidatorMetadataProvider } from './stakewizProvider.js';
2
+ /**
3
+ * Merges off-chain metadata (name / logo / APY) onto on-chain validator rows
4
+ * via the swappable provider. The provider never throws — on an outage the rows
5
+ * pass through with empty metadata and the display layer falls back to a
6
+ * truncated vote pubkey + on-chain commission.
7
+ *
8
+ * Replaces the call site where iOS's view models invoke the
9
+ * `ValidatorMetadataProvider`; the provider is injectable for tests.
10
+ */
11
+ export const enrichValidatorsWithMetadata = async (validators, provider = stakewizValidatorMetadataProvider) => {
12
+ const metadataByVotePubkey = await provider.metadata(validators.map(v => v.votePubkey));
13
+ return validators.map(validator => {
14
+ const metadata = metadataByVotePubkey[validator.votePubkey];
15
+ return metadata ? { ...validator, metadata } : validator;
16
+ });
17
+ };
18
+ //# sourceMappingURL=enrichValidators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrichValidators.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/staking/metadata/enrichValidators.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iCAAiC,EAAE,MAAM,oBAAoB,CAAA;AAGtE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,EAC/C,UAA6B,EAC7B,WAAsC,iCAAiC,EAC3C,EAAE;IAC9B,MAAM,oBAAoB,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAA;IACvF,OAAO,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;QAChC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;QAC3D,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IAC1D,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import { ValidatorMetadataProvider } from './ValidatorMetadataProvider.js';
2
+ export declare const stakewizValidatorMetadataProvider: ValidatorMetadataProvider;
3
+ //# sourceMappingURL=stakewizProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stakewizProvider.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/staking/metadata/stakewizProvider.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AAmDvE,eAAO,MAAM,iCAAiC,EAAE,yBAoC/C,CAAA"}
@@ -0,0 +1,73 @@
1
+ import { attempt } from '@vultisig/lib-utils/attempt';
2
+ /**
3
+ * Concrete `ValidatorMetadataProvider` backed by Stakewiz
4
+ * (https://api.stakewiz.com). The `/validators` endpoint returns the full
5
+ * validator set in one response, so a single fetch enriches an arbitrary batch
6
+ * of vote pubkeys. A failed or rate-limited fetch yields an empty map — the
7
+ * call never throws, so callers degrade to on-chain-only display. The ~1h cache
8
+ * is handled by the consuming react-query hook's `staleTime`.
9
+ *
10
+ * Field mapping (Stakewiz → ValidatorMetadata):
11
+ * name → name
12
+ * image → logoUrl
13
+ * apy_estimate → apyEstimate (percent on the wire, stored as a fraction)
14
+ * wiz_score → score
15
+ *
16
+ * Port of iOS `StakewizValidatorMetadataProvider`.
17
+ */
18
+ const stakewizValidatorsUrl = 'https://api.stakewiz.com/validators';
19
+ const asTrimmedString = (value) => typeof value === 'string' ? value.trim() || undefined : undefined;
20
+ const asFiniteNumber = (value) => typeof value === 'number' && Number.isFinite(value) ? value : undefined;
21
+ /**
22
+ * Stakewiz reports `apy_estimate` as a percentage (e.g. 5.72) — store as a
23
+ * fraction. A genuine `0` is preserved (0% APY ≠ "no APY"); only missing /
24
+ * non-finite / negative values collapse to `undefined`.
25
+ */
26
+ const apyFraction = (percent) => {
27
+ const value = asFiniteNumber(percent);
28
+ return value !== undefined && value >= 0 ? value / 100 : undefined;
29
+ };
30
+ const toMetadata = (row) => {
31
+ const wizScore = asFiniteNumber(row.wiz_score);
32
+ return {
33
+ name: asTrimmedString(row.name),
34
+ logoUrl: asTrimmedString(row.image),
35
+ apyEstimate: apyFraction(row.apy_estimate),
36
+ score: wizScore === undefined ? undefined : Math.round(wizScore),
37
+ };
38
+ };
39
+ export const stakewizValidatorMetadataProvider = {
40
+ metadata: async (votePubkeys) => {
41
+ const requested = new Set(votePubkeys.filter(Boolean));
42
+ if (requested.size === 0) {
43
+ return {};
44
+ }
45
+ const result = await attempt(async () => {
46
+ const response = await fetch(stakewizValidatorsUrl, {
47
+ headers: { Accept: 'application/json' },
48
+ });
49
+ if (!response.ok) {
50
+ throw new Error(`Stakewiz responded ${response.status}`);
51
+ }
52
+ return (await response.json());
53
+ });
54
+ // Outage / rate-limit / malformed response — degrade to on-chain-only.
55
+ if ('error' in result || !Array.isArray(result.data)) {
56
+ return {};
57
+ }
58
+ const map = {};
59
+ for (const row of result.data) {
60
+ // Skip malformed elements (`null`, primitives, missing/invalid pubkey)
61
+ // rather than letting a field access throw — the never-throw contract.
62
+ if (typeof row !== 'object' || row === null) {
63
+ continue;
64
+ }
65
+ const votePubkey = asTrimmedString(row.vote_identity);
66
+ if (votePubkey && requested.has(votePubkey)) {
67
+ map[votePubkey] = toMetadata(row);
68
+ }
69
+ }
70
+ return map;
71
+ },
72
+ };
73
+ //# sourceMappingURL=stakewizProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stakewizProvider.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/staking/metadata/stakewizProvider.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vultisig/core-chain",
3
- "version": "2.20.0",
3
+ "version": "2.21.0",
4
4
  "description": "Blockchain chain logic shared across Vultisig clients",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -690,6 +690,21 @@
690
690
  "import": "./dist/chains/solana/staking/cooldownGate.js",
691
691
  "default": "./dist/chains/solana/staking/cooldownGate.js"
692
692
  },
693
+ "./chains/solana/staking/metadata/enrichValidators": {
694
+ "types": "./dist/chains/solana/staking/metadata/enrichValidators.d.ts",
695
+ "import": "./dist/chains/solana/staking/metadata/enrichValidators.js",
696
+ "default": "./dist/chains/solana/staking/metadata/enrichValidators.js"
697
+ },
698
+ "./chains/solana/staking/metadata/stakewizProvider": {
699
+ "types": "./dist/chains/solana/staking/metadata/stakewizProvider.d.ts",
700
+ "import": "./dist/chains/solana/staking/metadata/stakewizProvider.js",
701
+ "default": "./dist/chains/solana/staking/metadata/stakewizProvider.js"
702
+ },
703
+ "./chains/solana/staking/metadata/ValidatorMetadataProvider": {
704
+ "types": "./dist/chains/solana/staking/metadata/ValidatorMetadataProvider.d.ts",
705
+ "import": "./dist/chains/solana/staking/metadata/ValidatorMetadataProvider.js",
706
+ "default": "./dist/chains/solana/staking/metadata/ValidatorMetadataProvider.js"
707
+ },
693
708
  "./chains/solana/staking/models/stakeAccount": {
694
709
  "types": "./dist/chains/solana/staking/models/stakeAccount.d.ts",
695
710
  "import": "./dist/chains/solana/staking/models/stakeAccount.js",
@@ -1994,7 +2009,7 @@
1994
2009
  "@cosmjs/proto-signing": "^0.39.0",
1995
2010
  "@cosmjs/stargate": "^0.39.0",
1996
2011
  "@lifi/sdk": "^4.0.0",
1997
- "@mysten/sui": "^2.19.0",
2012
+ "@mysten/sui": "^2.20.1",
1998
2013
  "@noble/hashes": "^1.8.0",
1999
2014
  "@polkadot/api": "^16.5.6",
2000
2015
  "@polkadot/util": "^14.0.3",
@@ -2014,7 +2029,7 @@
2014
2029
  "date-fns": "^4.4.0",
2015
2030
  "ethers": "^6.17.0",
2016
2031
  "tiny-secp256k1": "^2.2.4",
2017
- "viem": "^2.52.2",
2032
+ "viem": "^2.53.1",
2018
2033
  "xrpl": "^5.0.0"
2019
2034
  }
2020
2035
  }