@flapsdk/vault-runtime 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -6,9 +6,9 @@ This package is the shared runtime surface that local preview, Artifact Workbenc
6
6
 
7
7
  ## Exports
8
8
 
9
- - `./sdk`: component-facing SDK hooks, helpers, types, and provider
9
+ - `./sdk`: component-facing SDK hooks, helpers, types, provider, and local oracle reader helper
10
10
  - `./ui`: shared UI primitives
11
11
  - `./host`: host/runtime preflight helpers
12
- - `./server`: server-side presentation and host-proxy helpers
12
+ - `./server`: server-side presentation plus runtime oracle-registry helpers
13
13
 
14
14
  See `runtime-contract.json` for the machine-readable subpath contract.
package/host.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { a as Address, x as TokenRuntimeSnapshot, w as TokenMetadataSnapshot, n as HostRuntimeInput, H as HostRuntimeAddresses, r as HostRuntimeResult, c as CreateVaultRuntimeContextInput, G as VaultRuntimeContext } from './types-CX7pLmiT.mjs';
2
- export { A as ActionAvailabilityStage, C as ContractReadRequest, b as ContractWriteRequest, E as EndpointPolicy, F as FeeMode, d as FlapFeeVaultInfo, e as FlapI18n, f as FlapNotify, g as FlapTaxInfo, h as FlapTokenInfo, i as FlapVaultPortalInfo, j as FlapVaultSdk, k as FlapWallet, l as HostRuntimeDataSource, m as HostRuntimeDegradeReason, o as HostRuntimePolicy, p as HostRuntimePresentationFetcher, q as HostRuntimePresentationRequest, s as HostRuntimeSources, t as HostRuntimeStatus, u as HostRuntimeWarning, v as HostTokenPresentation, M as ManifestBindingEntry, P as PaymentToken, S as SimulateResult, T as TokenMarketPhase, y as TxReceipt, V as VaultComponentProps, z as VaultHostContext, B as VaultManifest, D as VaultRenderSurface, I as VaultRuntimeContextOverrides, J as VaultRuntimeExtraConfig } from './types-CX7pLmiT.mjs';
3
- export { C as CreateTaxInfoHostContextInput, R as ResolveRenderSurfaceInput, a as RuntimeMatchInput, T as TaxVaultHostSnapshot, b as TxErrorKind, V as VaultBindingPolicy, Z as ZERO_ADDRESS, c as createTaxInfoHostContext, f as formatCountdown, d as formatPercentBps, e as formatTokenAmount, g as getTupleField, h as getTxErrorKind, i as handleTxError, j as isActionAvailableForPhase, k as isCustomVaultTaxToken, l as isManifestRuntimeMatch, m as isSameAddress, n as isValidAddress, o as isVaultBindingMatch, p as normalizeAddress, q as parseFeeVaultInfo, r as parsePortalTokenInfo, s as parseTaxTokenInfo, t as parseTokenAmount, u as parseVaultPortalInfo, v as readTaxVaultHostContext, w as resolveFeeMode, x as resolveManifestBinding, y as resolveTaxInfoRenderSurface, z as resolveTokenMarketPhase, A as resolveVaultBinding, B as shortenAddress, D as standardErc20Abi } from './txError-1lPZHdqI.mjs';
1
+ import { a as Address, B as TokenRuntimeSnapshot, z as TokenMetadataSnapshot, n as HostRuntimeInput, H as HostRuntimeAddresses, r as HostRuntimeResult, c as CreateVaultRuntimeContextInput, K as VaultRuntimeContext } from './types-DzTX2u7q.mjs';
2
+ export { A as ActionAvailabilityStage, C as ContractReadRequest, b as ContractWriteRequest, E as EndpointPolicy, F as FeeMode, d as FlapFeeVaultInfo, e as FlapI18n, f as FlapNotify, g as FlapTaxInfo, h as FlapTokenInfo, i as FlapVaultPortalInfo, j as FlapVaultSdk, k as FlapWallet, l as HostRuntimeDataSource, m as HostRuntimeDegradeReason, o as HostRuntimePolicy, p as HostRuntimePresentationFetcher, q as HostRuntimePresentationRequest, s as HostRuntimeSources, t as HostRuntimeStatus, u as HostRuntimeWarning, v as HostTokenPresentation, M as ManifestBindingEntry, w as ManifestExternalContract, O as OracleProvision, x as OracleReadRequest, y as OracleReader, P as PaymentToken, R as RuntimeOracleRegistry, S as SimulateResult, T as TokenMarketPhase, D as TxReceipt, V as VaultComponentProps, G as VaultHostContext, I as VaultManifest, J as VaultRenderSurface, L as VaultRuntimeContextOverrides, N as VaultRuntimeExtraConfig } from './types-DzTX2u7q.mjs';
3
+ export { C as CreateTaxInfoHostContextInput, O as OracleReadError, R as ResolveRenderSurfaceInput, a as RuntimeMatchInput, T as TaxVaultHostSnapshot, b as TxErrorKind, V as VaultBindingPolicy, Z as ZERO_ADDRESS, c as buildLocalOracleUrl, d as createLocalOracleReader, e as createTaxInfoHostContext, f as fetchOracleJson, g as fetchProvisionedOracle, h as formatCountdown, i as formatPercentBps, j as formatTokenAmount, k as getTupleField, l as getTxErrorKind, m as handleTxError, n as isActionAvailableForPhase, o as isCustomVaultTaxToken, p as isManifestRuntimeMatch, q as isSameAddress, r as isValidAddress, s as isVaultBindingMatch, t as normalizeAddress, u as parseFeeVaultInfo, v as parsePortalTokenInfo, w as parseTaxTokenInfo, x as parseTokenAmount, y as parseVaultPortalInfo, z as readTaxVaultHostContext, A as resolveFeeMode, B as resolveManifestBinding, D as resolveTaxInfoRenderSurface, E as resolveTokenMarketPhase, F as resolveVaultBinding, G as shortenAddress, H as standardErc20Abi } from './txError-BmDt5Z4B.mjs';
4
4
  import { PublicClient } from 'viem';
5
5
  export { erc20Abi } from 'viem';
6
- export { T as TaxVaultHostChainConfig, g as getTaxVaultHostChainConfig } from './hostRuntimeConfig-BOEJo2nq.mjs';
6
+ export { T as TaxVaultHostChainConfig, g as getTaxVaultHostChainConfig } from './hostRuntimeConfig-8d2c6qsO.mjs';
7
7
 
8
8
  declare function readErc20TokenMetadata(publicClient: PublicClient, tokenAddress?: Address | null): Promise<TokenMetadataSnapshot | null>;
9
9
  declare function loadTokenRuntimeSnapshot(publicClient: PublicClient, chainId: number, tokenAddress?: Address | null): Promise<TokenRuntimeSnapshot | null>;
package/host.js CHANGED
@@ -28,9 +28,11 @@ function formatCountdown(targetTimeMs) {
28
28
  if (!targetTimeMs) return "-";
29
29
  const diff = Math.max(0, targetTimeMs - Date.now());
30
30
  const totalSeconds = Math.floor(diff / 1e3);
31
- const hours = Math.floor(totalSeconds / 3600);
31
+ const days = Math.floor(totalSeconds / 86400);
32
+ const hours = Math.floor(totalSeconds % 86400 / 3600);
32
33
  const minutes = Math.floor(totalSeconds % 3600 / 60);
33
34
  const seconds = totalSeconds % 60;
35
+ if (days > 0) return `${days}d ${hours}h ${minutes}m`;
34
36
  if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
35
37
  return `${minutes}m ${seconds}s`;
36
38
  }
@@ -181,26 +183,52 @@ function resolveManifestBinding(manifest, input) {
181
183
  if (candidates.length === 1) return candidates[0];
182
184
  return null;
183
185
  };
184
- if (input.chainId && input.factoryAddress) {
185
- return resolveUnique(bindings.filter((binding) => binding.chainId === input.chainId && isSameAddress(binding.factoryAddress, input.factoryAddress)));
186
+ const matchesChain = (binding) => !input.chainId || binding.chainId === input.chainId;
187
+ const matchesToken = (binding) => !input.tokenAddress || !binding.tokenAddresses?.length || binding.tokenAddresses.some((address) => isSameAddress(address, input.tokenAddress));
188
+ const isVaultScopedBinding = (binding) => !binding.factoryAddress && Boolean(binding.vaultAddresses?.length);
189
+ if (input.factoryAddress) {
190
+ const factoryMatch = resolveUnique(
191
+ bindings.filter((binding) => matchesChain(binding) && Boolean(binding.factoryAddress) && isSameAddress(binding.factoryAddress, input.factoryAddress) && matchesToken(binding))
192
+ );
193
+ if (factoryMatch || !input.vaultAddress) return factoryMatch;
194
+ }
195
+ if (input.vaultAddress) {
196
+ const vaultMatch = resolveUnique(
197
+ bindings.filter(
198
+ (binding) => matchesChain(binding) && isVaultScopedBinding(binding) && Boolean(binding.vaultAddresses?.some((address) => isSameAddress(address, input.vaultAddress))) && matchesToken(binding)
199
+ )
200
+ );
201
+ if (vaultMatch) return vaultMatch;
202
+ }
203
+ if (input.factoryAddress || input.vaultAddress) {
204
+ return null;
186
205
  }
187
206
  if (input.chainId) {
188
- return resolveUnique(bindings.filter((binding) => binding.chainId === input.chainId));
207
+ return resolveUnique(bindings.filter((binding) => binding.chainId === input.chainId && matchesToken(binding)));
189
208
  }
190
- if (input.factoryAddress) {
191
- return resolveUnique(bindings.filter((binding) => isSameAddress(binding.factoryAddress, input.factoryAddress)));
209
+ if (input.tokenAddress) {
210
+ return resolveUnique(bindings.filter((binding) => binding.tokenAddresses?.some((address) => isSameAddress(address, input.tokenAddress))));
192
211
  }
193
212
  return null;
194
213
  }
195
214
  function isManifestRuntimeMatch(manifest, input) {
196
- if (!input.chainId || !input.factoryAddress) return false;
215
+ if (!input.chainId || !input.factoryAddress && !input.vaultAddress) return false;
197
216
  const matchingBinding = resolveManifestBinding(manifest, input);
198
217
  if (!matchingBinding) return false;
199
- return matchingBinding.chainId === input.chainId && isSameAddress(matchingBinding.factoryAddress, input.factoryAddress);
218
+ if (matchingBinding.chainId !== input.chainId) return false;
219
+ if (matchingBinding.factoryAddress) return isSameAddress(matchingBinding.factoryAddress, input.factoryAddress);
220
+ const vaultAddress = matchingBinding.vaultAddresses?.[0];
221
+ if (!vaultAddress || !isSameAddress(vaultAddress, input.vaultAddress)) return false;
222
+ if (input.tokenAddress && matchingBinding.tokenAddresses?.length) {
223
+ return matchingBinding.tokenAddresses.some((address) => isSameAddress(address, input.tokenAddress));
224
+ }
225
+ return true;
200
226
  }
201
227
  function isVaultBindingMatch(binding, input) {
202
228
  if (!input.chainId || binding.chainId !== input.chainId) return false;
203
- if (!input.factoryAddress || !isSameAddress(binding.factoryAddress, input.factoryAddress)) return false;
229
+ if (binding.factoryAddress) return Boolean(input.factoryAddress && isSameAddress(binding.factoryAddress, input.factoryAddress));
230
+ const vaultAddress = binding.vaultAddresses?.[0];
231
+ if (!vaultAddress || !input.vaultAddress || !isSameAddress(vaultAddress, input.vaultAddress)) return false;
204
232
  return true;
205
233
  }
206
234
  function resolveVaultBinding(bindings, input) {
@@ -482,8 +510,15 @@ async function readErc20TokenMetadata(publicClient, tokenAddress) {
482
510
  }
483
511
  async function loadTokenRuntimeSnapshot(publicClient, chainId, tokenAddress) {
484
512
  if (!tokenAddress || !isValidAddress(tokenAddress) || tokenAddress === ZERO_ADDRESS) return null;
485
- const metadata = await readErc20TokenMetadata(publicClient, tokenAddress);
486
513
  const chainConfig = getTaxVaultHostChainConfig(chainId);
514
+ const metadataPromise = readErc20TokenMetadata(publicClient, tokenAddress);
515
+ const portalPromise = chainConfig ? publicClient.readContract({
516
+ address: chainConfig.portal,
517
+ abi: portalAbi,
518
+ functionName: "getTokenV7",
519
+ args: [tokenAddress]
520
+ }).then((data) => ({ ok: true, data })).catch(() => ({ ok: false })) : Promise.resolve(null);
521
+ const [metadata, portalResult] = await Promise.all([metadataPromise, portalPromise]);
487
522
  const baseSnapshot = {
488
523
  tokenSymbol: metadata?.tokenSymbol,
489
524
  tokenName: metadata?.tokenName,
@@ -497,15 +532,7 @@ async function loadTokenRuntimeSnapshot(publicClient, chainId, tokenAddress) {
497
532
  hostReadFromChain: false
498
533
  };
499
534
  }
500
- let tokenData;
501
- try {
502
- tokenData = await publicClient.readContract({
503
- address: chainConfig.portal,
504
- abi: portalAbi,
505
- functionName: "getTokenV7",
506
- args: [tokenAddress]
507
- });
508
- } catch {
535
+ if (!portalResult || !portalResult.ok) {
509
536
  return {
510
537
  ...baseSnapshot,
511
538
  hasTaxVaults: Boolean(chainConfig.vaultPortal),
@@ -514,6 +541,7 @@ async function loadTokenRuntimeSnapshot(publicClient, chainId, tokenAddress) {
514
541
  giftVaultFactory: chainConfig.giftVaultFactory
515
542
  };
516
543
  }
544
+ const tokenData = portalResult.data;
517
545
  const tokenInfo = parsePortalTokenInfo(tokenData);
518
546
  const hasTaxVaults = Boolean(chainConfig.vaultPortal);
519
547
  if (!tokenInfo) {
@@ -607,8 +635,8 @@ function resolveAddresses(input, snapshot) {
607
635
  tokenAddress: input.tokenAddress,
608
636
  factoryAddressHint: input.factoryAddressHint,
609
637
  vaultAddressHint: input.vaultAddressHint,
610
- factoryAddress: pickAddress(input.factoryAddressHint, snapshot?.vaultInfo?.vaultFactory, snapshot?.taxInfo?.vaultInfo?.factory ?? void 0),
611
- vaultAddress: pickAddress(input.vaultAddressHint, snapshot?.vaultInfo?.vault, snapshot?.taxInfo?.vaultInfo?.addr ?? void 0)
638
+ factoryAddress: pickAddress(snapshot?.vaultInfo?.vaultFactory, snapshot?.taxInfo?.vaultInfo?.factory ?? void 0, input.factoryAddressHint),
639
+ vaultAddress: pickAddress(snapshot?.vaultInfo?.vault, snapshot?.taxInfo?.vaultInfo?.addr ?? void 0, input.vaultAddressHint)
612
640
  };
613
641
  }
614
642
  function hasPresentationMetadata(result) {
@@ -700,11 +728,12 @@ async function runHostRuntime(input) {
700
728
  let presentationFailure;
701
729
  if (input.presentationFetcher) {
702
730
  try {
731
+ const resolvedAddrs = resolveAddresses(input, snapshot);
703
732
  presentation = await input.presentationFetcher({
704
733
  chainId: input.chainId,
705
734
  tokenAddress: input.tokenAddress,
706
- factoryAddress: resolveAddresses(input, snapshot).factoryAddress,
707
- vaultAddress: resolveAddresses(input, snapshot).vaultAddress,
735
+ factoryAddress: resolvedAddrs.factoryAddress,
736
+ vaultAddress: resolvedAddrs.vaultAddress,
708
737
  snapshot
709
738
  });
710
739
  if (!presentation) {
@@ -752,6 +781,73 @@ async function runHostRuntime(input) {
752
781
  return result;
753
782
  }
754
783
 
784
+ // src/sdk/oracle.ts
785
+ var DEFAULT_LOCAL_ORACLE_ENDPOINT_BASE = "/api/runtime/oracle";
786
+ var OracleReadError = class extends Error {
787
+ constructor(message, status) {
788
+ super(message);
789
+ this.name = "OracleReadError";
790
+ this.status = status;
791
+ }
792
+ };
793
+ function appendSearchParams(url, params) {
794
+ for (const [key, value] of Object.entries(params ?? {})) {
795
+ url.searchParams.set(key, value);
796
+ }
797
+ }
798
+ function toOracleProvision(provision) {
799
+ return typeof provision === "string" ? { endpoint: provision } : provision;
800
+ }
801
+ function buildLocalOracleUrl(oracleId, params, endpointBase = DEFAULT_LOCAL_ORACLE_ENDPOINT_BASE) {
802
+ const normalizedBase = endpointBase.replace(/\/+$/, "");
803
+ const url = new URL(`${normalizedBase}/${encodeURIComponent(oracleId)}`, "http://localhost");
804
+ appendSearchParams(url, params);
805
+ return `${url.pathname}${url.search}`;
806
+ }
807
+ async function fetchOracleJson({
808
+ endpoint,
809
+ params,
810
+ fetchImpl,
811
+ headers
812
+ }) {
813
+ const fallbackOrigin = typeof window !== "undefined" ? window.location.origin : "http://localhost";
814
+ const url = new URL(endpoint, fallbackOrigin);
815
+ appendSearchParams(url, params);
816
+ const response = await (fetchImpl ?? fetch)(url.toString(), {
817
+ cache: "no-store",
818
+ headers,
819
+ method: "GET"
820
+ });
821
+ if (!response.ok) {
822
+ throw new OracleReadError(`Oracle request returned ${response.status}.`, response.status);
823
+ }
824
+ return await response.json();
825
+ }
826
+ async function fetchProvisionedOracle({
827
+ provision,
828
+ params,
829
+ fetchImpl
830
+ }) {
831
+ const normalizedProvision = toOracleProvision(provision);
832
+ const filteredParams = normalizedProvision.allowedParams && normalizedProvision.allowedParams.length ? Object.fromEntries(Object.entries(params ?? {}).filter(([key]) => normalizedProvision.allowedParams?.includes(key))) : params;
833
+ return fetchOracleJson({
834
+ endpoint: normalizedProvision.endpoint,
835
+ params: filteredParams,
836
+ fetchImpl,
837
+ headers: normalizedProvision.headers
838
+ });
839
+ }
840
+ function createLocalOracleReader(options = {}) {
841
+ const endpointBase = options.endpointBase ?? DEFAULT_LOCAL_ORACLE_ENDPOINT_BASE;
842
+ const fetchImpl = options.fetchImpl;
843
+ return async function readLocalOracle({ oracleId, params }) {
844
+ return fetchOracleJson({
845
+ endpoint: buildLocalOracleUrl(oracleId, params, endpointBase),
846
+ fetchImpl
847
+ });
848
+ };
849
+ }
850
+
755
851
  // src/sdk/runtimeContext.ts
756
852
  var zeroAddress = "0x0000000000000000000000000000000000000000";
757
853
  function explorerForChain(chainId) {
@@ -782,7 +878,9 @@ function createVaultRuntimeContext(input) {
782
878
  const runtimeOverrides = input.runtimeOverrides;
783
879
  const resolvedBinding = resolveManifestBinding(input.manifest, {
784
880
  chainId: runtimeOverrides?.chainId ?? input.connectedChainId ?? input.hostRuntimeResult?.addresses.chainId,
785
- factoryAddress: runtimeOverrides?.factoryAddress ?? input.hostRuntimeResult?.addresses.factoryAddress
881
+ factoryAddress: runtimeOverrides?.factoryAddress ?? input.hostRuntimeResult?.addresses.factoryAddress,
882
+ vaultAddress: runtimeOverrides?.vaultAddress ?? input.hostRuntimeResult?.addresses.vaultAddress,
883
+ tokenAddress: runtimeOverrides?.tokenAddress ?? input.hostRuntimeResult?.addresses.tokenAddress
786
884
  });
787
885
  const effectiveChainId = runtimeOverrides?.chainId ?? input.hostRuntimeResult?.addresses.chainId ?? input.connectedChainId ?? resolvedBinding?.chainId ?? input.manifest.match.bindings[0]?.chainId ?? 56;
788
886
  return {
@@ -866,6 +964,6 @@ function handleTxError(error, messages) {
866
964
  return messages?.[kind] ?? defaultMessages[kind];
867
965
  }
868
966
 
869
- export { ZERO_ADDRESS, chainLabelForChain, createTaxInfoHostContext, createVaultRuntimeContext, explorerForChain, formatCountdown, formatPercentBps, formatTokenAmount, getTaxVaultHostChainConfig, getTupleField, getTxErrorKind, handleTxError, isActionAvailableForPhase, isCustomVaultTaxToken, isManifestRuntimeMatch, isSameAddress, isValidAddress, isVaultBindingMatch, loadTokenRuntimeSnapshot, normalizeAddress, parseFeeVaultInfo, parsePortalTokenInfo, parseTaxTokenInfo, parseTokenAmount, parseVaultPortalInfo, readErc20TokenMetadata, readTaxVaultHostContext, resolveFeeMode, resolveHostRuntimeAddresses, resolveManifestBinding, resolveTaxInfoRenderSurface, resolveTokenMarketPhase, resolveVaultBinding, runHostRuntime, shortenAddress, standardErc20Abi };
967
+ export { OracleReadError, ZERO_ADDRESS, buildLocalOracleUrl, chainLabelForChain, createLocalOracleReader, createTaxInfoHostContext, createVaultRuntimeContext, explorerForChain, fetchOracleJson, fetchProvisionedOracle, formatCountdown, formatPercentBps, formatTokenAmount, getTaxVaultHostChainConfig, getTupleField, getTxErrorKind, handleTxError, isActionAvailableForPhase, isCustomVaultTaxToken, isManifestRuntimeMatch, isSameAddress, isValidAddress, isVaultBindingMatch, loadTokenRuntimeSnapshot, normalizeAddress, parseFeeVaultInfo, parsePortalTokenInfo, parseTaxTokenInfo, parseTokenAmount, parseVaultPortalInfo, readErc20TokenMetadata, readTaxVaultHostContext, resolveFeeMode, resolveHostRuntimeAddresses, resolveManifestBinding, resolveTaxInfoRenderSurface, resolveTokenMarketPhase, resolveVaultBinding, runHostRuntime, shortenAddress, standardErc20Abi };
870
968
  //# sourceMappingURL=host.js.map
871
969
  //# sourceMappingURL=host.js.map