@ape.swap/bonds-sdk 5.1.49-test.2 → 5.1.49-test.3

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.
Files changed (42) hide show
  1. package/dist/config/constants/chains.js +1 -1
  2. package/dist/config/constants/chains.js.map +1 -1
  3. package/dist/config/constants/suiZapTokens.js +19 -7
  4. package/dist/config/constants/suiZapTokens.js.map +1 -1
  5. package/dist/constants/suiConstants.d.ts +1 -7
  6. package/dist/constants/suiConstants.js +6 -34
  7. package/dist/constants/suiConstants.js.map +1 -1
  8. package/dist/state/bonds/discoverSuiBonds.d.ts +6 -0
  9. package/dist/state/bonds/fetchBondsDataSui.js +38 -11
  10. package/dist/state/bonds/fetchBondsDataSui.js.map +1 -1
  11. package/dist/state/bonds/useBondsData.js +17 -9
  12. package/dist/state/bonds/useBondsData.js.map +1 -1
  13. package/dist/state/bonds/useBondsList.js +22 -22
  14. package/dist/state/bonds/useBondsList.js.map +1 -1
  15. package/dist/state/tokenPrices/useTokenPrices.js +1 -13
  16. package/dist/state/tokenPrices/useTokenPrices.js.map +1 -1
  17. package/dist/state/useSDKConfig.d.ts +1 -0
  18. package/dist/state/useSDKConfig.js.map +1 -1
  19. package/dist/state/zap/useSuiZapQuote.js +5 -28
  20. package/dist/state/zap/useSuiZapQuote.js.map +1 -1
  21. package/dist/utils/suiHelpers.d.ts +1 -1
  22. package/dist/utils/suiHelpers.js +5 -5
  23. package/dist/utils/suiHelpers.js.map +1 -1
  24. package/dist/views/BuyBond/BuyComponentSui.js +134 -50
  25. package/dist/views/BuyBond/BuyComponentSui.js.map +1 -1
  26. package/dist/views/BuyBond/utils.js +3 -0
  27. package/dist/views/BuyBond/utils.js.map +1 -1
  28. package/dist/views/YourBonds/components/UserBondRow/UserBondRowSui.js +2 -2
  29. package/dist/views/YourBonds/components/UserBondRow/UserBondRowSui.js.map +1 -1
  30. package/dist/views/YourBondsModal/components/Actions/ActionsSui.js +2 -2
  31. package/dist/views/YourBondsModal/components/Actions/ActionsSui.js.map +1 -1
  32. package/dist/views/YourBondsModal/components/TransferBondModal/TransferActionSui.d.ts +9 -0
  33. package/dist/views/YourBondsModal/components/TransferBondModal/TransferActionSui.js +71 -0
  34. package/dist/views/YourBondsModal/components/TransferBondModal/TransferActionSui.js.map +1 -0
  35. package/dist/views/YourBondsModal/components/TransferBondModal/index.js +2 -1
  36. package/dist/views/YourBondsModal/components/TransferBondModal/index.js.map +1 -1
  37. package/package.json +1 -1
  38. package/dist/state/bonds/aptosBondsConfig.js +0 -183
  39. package/dist/state/bonds/aptosBondsConfig.js.map +0 -1
  40. package/dist/state/bonds/suiBondsConfig.d.ts +0 -4
  41. package/dist/state/bonds/suiBondsConfig.js +0 -227
  42. package/dist/state/bonds/suiBondsConfig.js.map +0 -1
@@ -1,10 +1,8 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
2
  import axios from 'axios';
3
- import { ChainId } from '@ape.swap/apeswap-lists';
4
3
  import { QUERY_KEYS } from '../../config/constants/queryKeys.js';
5
4
  import { useSDKConfig } from '../useSDKConfig.js';
6
5
  import { reportError } from '../../utils/reportError.js';
7
- import { getSuiFallbackPrices } from '../bonds/suiBondsConfig.js';
8
6
 
9
7
  function useTokenPrices() {
10
8
  const SDKConfig = useSDKConfig();
@@ -21,17 +19,7 @@ function useTokenPrices() {
21
19
  const getTokenPrices = async (priceApi, apiv2) => {
22
20
  try {
23
21
  const response = await axios.get(`${priceApi}/realtime/prices`);
24
- const data = response.data ?? {};
25
- // Merge fallback prices for Sui tokens not returned by price-api (e.g. testnet tokens)
26
- const suiKey = ChainId.SUI;
27
- const suiFallback = getSuiFallbackPrices();
28
- const suiApiPrices = data[suiKey] ?? [];
29
- const suiApiAddresses = new Set(suiApiPrices.map((p) => p.tokenAddress.toLowerCase()));
30
- const missingSuiPrices = suiFallback.filter((p) => !suiApiAddresses.has(p.tokenAddress.toLowerCase()));
31
- if (missingSuiPrices.length > 0) {
32
- data[suiKey] = [...suiApiPrices, ...missingSuiPrices];
33
- }
34
- return data;
22
+ return response.data;
35
23
  }
36
24
  catch (error) {
37
25
  reportError({
@@ -1 +1 @@
1
- {"version":3,"file":"useTokenPrices.js","sources":["../../../src/state/tokenPrices/useTokenPrices.ts"],"sourcesContent":["import { useQuery, UseQueryResult } from '@tanstack/react-query'\nimport axios from 'axios'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport { QUERY_KEYS } from '../../config/constants/queryKeys'\nimport { useSDKConfig } from '../useSDKConfig'\nimport { reportError } from '../../utils/reportError'\nimport { getSuiFallbackPrices } from '../bonds/suiBondsConfig'\n\nexport interface TokenPrices {\n chainId: ChainId\n chainName: string\n tokenAddress: string\n name: string\n symbol: string\n price: number\n priceSource: string\n updatedAt: number\n secondsAgoUpdated: number\n error?: string\n}\n\nexport default function useTokenPrices(): UseQueryResult<Partial<Record<ChainId, TokenPrices[]>>> {\n const SDKConfig = useSDKConfig()\n const priceApi = SDKConfig?.urls?.priceApi\n const apiv2 = SDKConfig?.urls?.apiV2\n return useQuery({\n queryKey: [QUERY_KEYS.TOKEN_PRICES],\n queryFn: () => getTokenPrices(priceApi, apiv2),\n refetchInterval: 30000, // 30 sec\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n })\n}\n\nexport const getTokenPrices = async (\n priceApi: string,\n apiv2: string,\n): Promise<Partial<Record<ChainId, TokenPrices[]>>> => {\n try {\n const response = await axios.get(`${priceApi}/realtime/prices`)\n const data: Partial<Record<ChainId, TokenPrices[]>> = response.data ?? {}\n\n // Merge fallback prices for Sui tokens not returned by price-api (e.g. testnet tokens)\n const suiKey = ChainId.SUI\n const suiFallback = getSuiFallbackPrices()\n const suiApiPrices = data[suiKey] ?? []\n const suiApiAddresses = new Set(suiApiPrices.map((p) => p.tokenAddress.toLowerCase()))\n const missingSuiPrices = suiFallback.filter((p) => !suiApiAddresses.has(p.tokenAddress.toLowerCase()))\n if (missingSuiPrices.length > 0) {\n data[suiKey] = [...suiApiPrices, ...missingSuiPrices]\n }\n\n return data\n } catch (error) {\n reportError({\n apiUrl: apiv2,\n error,\n extraInfo: { type: 'getTokenPrices', error },\n })\n throw new Error('Error getting tokens')\n }\n}\n"],"names":[],"mappings":";;;;;;;;AAqBc,SAAU,cAAc,GAAA;AACpC,IAAA,MAAM,SAAS,GAAG,YAAY,EAAE;AAChC,IAAA,MAAM,QAAQ,GAAG,SAAS,EAAE,IAAI,EAAE,QAAQ;AAC1C,IAAA,MAAM,KAAK,GAAG,SAAS,EAAE,IAAI,EAAE,KAAK;AACpC,IAAA,OAAO,QAAQ,CAAC;AACd,QAAA,QAAQ,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QACnC,OAAO,EAAE,MAAM,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC9C,eAAe,EAAE,KAAK;AACtB,QAAA,cAAc,EAAE,KAAK;AACrB,QAAA,oBAAoB,EAAE,KAAK;AAC5B,KAAA,CAAC;AACJ;AAEO,MAAM,cAAc,GAAG,OAC5B,QAAgB,EAChB,KAAa,KACuC;AACpD,IAAA,IAAI;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA,EAAG,QAAQ,CAAA,gBAAA,CAAkB,CAAC;AAC/D,QAAA,MAAM,IAAI,GAA4C,QAAQ,CAAC,IAAI,IAAI,EAAE;;AAGzE,QAAA,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG;AAC1B,QAAA,MAAM,WAAW,GAAG,oBAAoB,EAAE;QAC1C,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;QACvC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;QACtF,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;AACtG,QAAA,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE;YAC/B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,EAAE,GAAG,gBAAgB,CAAC;QACvD;AAEA,QAAA,OAAO,IAAI;IACb;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,WAAW,CAAC;AACV,YAAA,MAAM,EAAE,KAAK;YACb,KAAK;AACL,YAAA,SAAS,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE;AAC7C,SAAA,CAAC;AACF,QAAA,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC;IACzC;AACF;;;;"}
1
+ {"version":3,"file":"useTokenPrices.js","sources":["../../../src/state/tokenPrices/useTokenPrices.ts"],"sourcesContent":["import { useQuery, UseQueryResult } from '@tanstack/react-query'\nimport axios from 'axios'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport { QUERY_KEYS } from '../../config/constants/queryKeys'\nimport { useSDKConfig } from '../useSDKConfig'\nimport { reportError } from '../../utils/reportError'\n\nexport interface TokenPrices {\n chainId: ChainId\n chainName: string\n tokenAddress: string\n name: string\n symbol: string\n price: number\n priceSource: string\n updatedAt: number\n secondsAgoUpdated: number\n error?: string\n}\n\nexport default function useTokenPrices(): UseQueryResult<Partial<Record<ChainId, TokenPrices[]>>> {\n const SDKConfig = useSDKConfig()\n const priceApi = SDKConfig?.urls?.priceApi\n const apiv2 = SDKConfig?.urls?.apiV2\n return useQuery({\n queryKey: [QUERY_KEYS.TOKEN_PRICES],\n queryFn: () => getTokenPrices(priceApi, apiv2),\n refetchInterval: 30000, // 30 sec\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n })\n}\n\nexport const getTokenPrices = async (\n priceApi: string,\n apiv2: string,\n): Promise<Partial<Record<ChainId, TokenPrices[]>>> => {\n try {\n const response = await axios.get(`${priceApi}/realtime/prices`)\n return response.data\n } catch (error) {\n reportError({\n apiUrl: apiv2,\n error,\n extraInfo: { type: 'getTokenPrices', error },\n })\n throw new Error('Error getting tokens')\n }\n}\n"],"names":[],"mappings":";;;;;;AAoBc,SAAU,cAAc,GAAA;AACpC,IAAA,MAAM,SAAS,GAAG,YAAY,EAAE;AAChC,IAAA,MAAM,QAAQ,GAAG,SAAS,EAAE,IAAI,EAAE,QAAQ;AAC1C,IAAA,MAAM,KAAK,GAAG,SAAS,EAAE,IAAI,EAAE,KAAK;AACpC,IAAA,OAAO,QAAQ,CAAC;AACd,QAAA,QAAQ,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QACnC,OAAO,EAAE,MAAM,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC9C,eAAe,EAAE,KAAK;AACtB,QAAA,cAAc,EAAE,KAAK;AACrB,QAAA,oBAAoB,EAAE,KAAK;AAC5B,KAAA,CAAC;AACJ;AAEO,MAAM,cAAc,GAAG,OAC5B,QAAgB,EAChB,KAAa,KACuC;AACpD,IAAA,IAAI;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA,EAAG,QAAQ,CAAA,gBAAA,CAAkB,CAAC;QAC/D,OAAO,QAAQ,CAAC,IAAI;IACtB;IAAE,OAAO,KAAK,EAAE;AACd,QAAA,WAAW,CAAC;AACV,YAAA,MAAM,EAAE,KAAK;YACb,KAAK;AACL,YAAA,SAAS,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE;AAC7C,SAAA,CAAC;AACF,QAAA,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC;IACzC;AACF;;;;"}
@@ -20,6 +20,7 @@ export interface SDKPropsDTO {
20
20
  customRPCS?: Partial<Record<ChainId, string[]>>;
21
21
  aptosAddress?: string;
22
22
  suiAddress?: string;
23
+ suiTreasuryAddresses?: string[];
23
24
  }
24
25
  export interface SDKProps {
25
26
  referenceId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"useSDKConfig.js","sources":["../../src/state/useSDKConfig.ts"],"sourcesContent":["import { useEffect } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { QUERY_KEYS } from '../config/constants/queryKeys'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport { setCustomRPCS } from '../config/constants/networks'\n\n// Prod URLS. Do not make changes here\nconst defaultUrls = {\n apiV2: 'https://api.ape.bond',\n realTimeApi: 'https://realtime-api.ape.bond',\n mainUrl: 'https://ape.bond',\n priceApi: 'https://price-api.ape.bond',\n}\n\n// Staging URLS.\nconst stagingUrls = {\n apiV2: 'https://staging-api.ape.bond',\n realTimeApi: 'https://realtime-api-staging.ape.bond',\n mainUrl: 'https://staging.ape.bond',\n priceApi: 'https://price-api.ape.bond',\n}\n\nexport interface SDKPropsDTO {\n referenceId: string\n chains: number[]\n hotBondChains?: number[]\n connector?: 'rainbowkit' | 'appkit' | 'default'\n useHotBonds?: boolean\n useTiers?: boolean\n useTGEBonds?: boolean\n useCardsView?: boolean\n showLowValueBonds?: boolean\n bondPartner?: string\n namingPreference?: 'Discount' | 'Bonus'\n urls?: Partial<Record<URLKeys, string>>\n theme?: any\n highestCompatibleVersion?: string\n blockSales?: boolean\n customBranch?: string\n highlightedBond?: string\n customRPCS?: Partial<Record<ChainId, string[]>>\n aptosAddress?: string\n suiAddress?: string\n}\n\nexport interface SDKProps {\n referenceId: string\n chains: number[]\n hotBondChains: number[]\n connector?: 'rainbowkit' | 'appkit' | 'default'\n useHotBonds: boolean\n useTiers: boolean\n useTGEBonds: boolean\n showLowValueBonds: boolean\n bondPartner?: string\n namingPreference: 'Discount' | 'Bonus'\n urls: Record<URLKeys, string>\n theme?: any\n highestCompatibleVersion?: string\n evmAddress?: string\n solAddress?: string\n aptosAddress?: string\n suiAddress?: string\n useCardsView?: boolean\n blockSales?: boolean\n customBranch?: string\n highlightedBond?: string\n}\n\nexport const useSDKConfig = (config?: SDKPropsDTO): SDKProps => {\n const urls = process.env.NODE_ENV === 'production' ? defaultUrls : stagingUrls\n\n const initialData: SDKProps = {\n referenceId: '',\n chains: [],\n hotBondChains: [ChainId.BSC, ChainId.MATIC],\n useHotBonds: true,\n connector: 'default',\n useTGEBonds: false,\n showLowValueBonds: false,\n bondPartner: undefined,\n namingPreference: 'Bonus',\n useCardsView: false,\n blockSales: false,\n customBranch: '',\n ...config,\n useTiers: config?.namingPreference === 'Discount' ? false : (config?.useTiers ?? false), // for the time being we'll assume discount preference does not want to useTiers\n urls: { ...urls, ...config?.urls },\n highestCompatibleVersion: '2.2.0',\n }\n\n useEffect(() => {\n if (config?.customRPCS) {\n setCustomRPCS(config.customRPCS)\n }\n }, [config?.customRPCS])\n\n const { data } = useQuery({\n queryKey: [QUERY_KEYS.SDK_CONFIG],\n queryFn: () => {\n throw new Error('Just a hotfix for latest versions of react-query that make queryFn a required prop')\n },\n initialData,\n })\n return data as SDKProps\n}\n\nexport type URLKeys = 'apiV2' | 'realTimeApi' | 'mainUrl' | 'priceApi'\n\nexport const useURLByEnvironment = (key: URLKeys) => {\n const config = useSDKConfig()\n return config?.urls?.[key] as string\n}\n"],"names":[],"mappings":";;;;;;AAMA;AACA,MAAM,WAAW,GAAG;AAClB,IAAA,KAAK,EAAE,sBAAsB;AAC7B,IAAA,WAAW,EAAE,+BAA+B;AAC5C,IAAA,OAAO,EAAE,kBAAkB;AAC3B,IAAA,QAAQ,EAAE,4BAA4B;CACvC;AAED;AACA,MAAM,WAAW,GAAG;AAClB,IAAA,KAAK,EAAE,8BAA8B;AACrC,IAAA,WAAW,EAAE,uCAAuC;AACpD,IAAA,OAAO,EAAE,0BAA0B;AACnC,IAAA,QAAQ,EAAE,4BAA4B;CACvC;AAiDM,MAAM,YAAY,GAAG,CAAC,MAAoB,KAAc;AAC7D,IAAA,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,GAAG,WAAW,GAAG,WAAW;AAE9E,IAAA,MAAM,WAAW,GAAa;AAC5B,QAAA,WAAW,EAAE,EAAE;AACf,QAAA,MAAM,EAAE,EAAE;QACV,aAAa,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC;AAC3C,QAAA,WAAW,EAAE,IAAI;AACjB,QAAA,SAAS,EAAE,SAAS;AACpB,QAAA,WAAW,EAAE,KAAK;AAClB,QAAA,iBAAiB,EAAE,KAAK;AACxB,QAAA,WAAW,EAAE,SAAS;AACtB,QAAA,gBAAgB,EAAE,OAAO;AACzB,QAAA,YAAY,EAAE,KAAK;AACnB,QAAA,UAAU,EAAE,KAAK;AACjB,QAAA,YAAY,EAAE,EAAE;AAChB,QAAA,GAAG,MAAM;QACT,QAAQ,EAAE,MAAM,EAAE,gBAAgB,KAAK,UAAU,GAAG,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,KAAK,CAAC;QACvF,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE;AAClC,QAAA,wBAAwB,EAAE,OAAO;KAClC;IAED,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,MAAM,EAAE,UAAU,EAAE;AACtB,YAAA,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC;AACF,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAExB,IAAA,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;AACxB,QAAA,QAAQ,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QACjC,OAAO,EAAE,MAAK;AACZ,YAAA,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC;QACvG,CAAC;QACD,WAAW;AACZ,KAAA,CAAC;AACF,IAAA,OAAO,IAAgB;AACzB;AAIO,MAAM,mBAAmB,GAAG,CAAC,GAAY,KAAI;AAClD,IAAA,MAAM,MAAM,GAAG,YAAY,EAAE;AAC7B,IAAA,OAAO,MAAM,EAAE,IAAI,GAAG,GAAG,CAAW;AACtC;;;;"}
1
+ {"version":3,"file":"useSDKConfig.js","sources":["../../src/state/useSDKConfig.ts"],"sourcesContent":["import { useEffect } from 'react'\nimport { useQuery } from '@tanstack/react-query'\nimport { QUERY_KEYS } from '../config/constants/queryKeys'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport { setCustomRPCS } from '../config/constants/networks'\n\n// Prod URLS. Do not make changes here\nconst defaultUrls = {\n apiV2: 'https://api.ape.bond',\n realTimeApi: 'https://realtime-api.ape.bond',\n mainUrl: 'https://ape.bond',\n priceApi: 'https://price-api.ape.bond',\n}\n\n// Staging URLS.\nconst stagingUrls = {\n apiV2: 'https://staging-api.ape.bond',\n realTimeApi: 'https://realtime-api-staging.ape.bond',\n mainUrl: 'https://staging.ape.bond',\n priceApi: 'https://price-api.ape.bond',\n}\n\nexport interface SDKPropsDTO {\n referenceId: string\n chains: number[]\n hotBondChains?: number[]\n connector?: 'rainbowkit' | 'appkit' | 'default'\n useHotBonds?: boolean\n useTiers?: boolean\n useTGEBonds?: boolean\n useCardsView?: boolean\n showLowValueBonds?: boolean\n bondPartner?: string\n namingPreference?: 'Discount' | 'Bonus'\n urls?: Partial<Record<URLKeys, string>>\n theme?: any\n highestCompatibleVersion?: string\n blockSales?: boolean\n customBranch?: string\n highlightedBond?: string\n customRPCS?: Partial<Record<ChainId, string[]>>\n aptosAddress?: string\n suiAddress?: string\n suiTreasuryAddresses?: string[]\n}\n\nexport interface SDKProps {\n referenceId: string\n chains: number[]\n hotBondChains: number[]\n connector?: 'rainbowkit' | 'appkit' | 'default'\n useHotBonds: boolean\n useTiers: boolean\n useTGEBonds: boolean\n showLowValueBonds: boolean\n bondPartner?: string\n namingPreference: 'Discount' | 'Bonus'\n urls: Record<URLKeys, string>\n theme?: any\n highestCompatibleVersion?: string\n evmAddress?: string\n solAddress?: string\n aptosAddress?: string\n suiAddress?: string\n useCardsView?: boolean\n blockSales?: boolean\n customBranch?: string\n highlightedBond?: string\n}\n\nexport const useSDKConfig = (config?: SDKPropsDTO): SDKProps => {\n const urls = process.env.NODE_ENV === 'production' ? defaultUrls : stagingUrls\n\n const initialData: SDKProps = {\n referenceId: '',\n chains: [],\n hotBondChains: [ChainId.BSC, ChainId.MATIC],\n useHotBonds: true,\n connector: 'default',\n useTGEBonds: false,\n showLowValueBonds: false,\n bondPartner: undefined,\n namingPreference: 'Bonus',\n useCardsView: false,\n blockSales: false,\n customBranch: '',\n ...config,\n useTiers: config?.namingPreference === 'Discount' ? false : (config?.useTiers ?? false), // for the time being we'll assume discount preference does not want to useTiers\n urls: { ...urls, ...config?.urls },\n highestCompatibleVersion: '2.2.0',\n }\n\n useEffect(() => {\n if (config?.customRPCS) {\n setCustomRPCS(config.customRPCS)\n }\n }, [config?.customRPCS])\n\n const { data } = useQuery({\n queryKey: [QUERY_KEYS.SDK_CONFIG],\n queryFn: () => {\n throw new Error('Just a hotfix for latest versions of react-query that make queryFn a required prop')\n },\n initialData,\n })\n return data as SDKProps\n}\n\nexport type URLKeys = 'apiV2' | 'realTimeApi' | 'mainUrl' | 'priceApi'\n\nexport const useURLByEnvironment = (key: URLKeys) => {\n const config = useSDKConfig()\n return config?.urls?.[key] as string\n}\n"],"names":[],"mappings":";;;;;;AAMA;AACA,MAAM,WAAW,GAAG;AAClB,IAAA,KAAK,EAAE,sBAAsB;AAC7B,IAAA,WAAW,EAAE,+BAA+B;AAC5C,IAAA,OAAO,EAAE,kBAAkB;AAC3B,IAAA,QAAQ,EAAE,4BAA4B;CACvC;AAED;AACA,MAAM,WAAW,GAAG;AAClB,IAAA,KAAK,EAAE,8BAA8B;AACrC,IAAA,WAAW,EAAE,uCAAuC;AACpD,IAAA,OAAO,EAAE,0BAA0B;AACnC,IAAA,QAAQ,EAAE,4BAA4B;CACvC;AAkDM,MAAM,YAAY,GAAG,CAAC,MAAoB,KAAc;AAC7D,IAAA,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,GAAG,WAAW,GAAG,WAAW;AAE9E,IAAA,MAAM,WAAW,GAAa;AAC5B,QAAA,WAAW,EAAE,EAAE;AACf,QAAA,MAAM,EAAE,EAAE;QACV,aAAa,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC;AAC3C,QAAA,WAAW,EAAE,IAAI;AACjB,QAAA,SAAS,EAAE,SAAS;AACpB,QAAA,WAAW,EAAE,KAAK;AAClB,QAAA,iBAAiB,EAAE,KAAK;AACxB,QAAA,WAAW,EAAE,SAAS;AACtB,QAAA,gBAAgB,EAAE,OAAO;AACzB,QAAA,YAAY,EAAE,KAAK;AACnB,QAAA,UAAU,EAAE,KAAK;AACjB,QAAA,YAAY,EAAE,EAAE;AAChB,QAAA,GAAG,MAAM;QACT,QAAQ,EAAE,MAAM,EAAE,gBAAgB,KAAK,UAAU,GAAG,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,KAAK,CAAC;QACvF,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE;AAClC,QAAA,wBAAwB,EAAE,OAAO;KAClC;IAED,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,MAAM,EAAE,UAAU,EAAE;AACtB,YAAA,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC;QAClC;AACF,IAAA,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAExB,IAAA,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;AACxB,QAAA,QAAQ,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QACjC,OAAO,EAAE,MAAK;AACZ,YAAA,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC;QACvG,CAAC;QACD,WAAW;AACZ,KAAA,CAAC;AACF,IAAA,OAAO,IAAgB;AACzB;AAIO,MAAM,mBAAmB,GAAG,CAAC,GAAY,KAAI;AAClD,IAAA,MAAM,MAAM,GAAG,YAAY,EAAE;AAC7B,IAAA,OAAO,MAAM,EAAE,IAAI,GAAG,GAAG,CAAW;AACtC;;;;"}
@@ -5,8 +5,7 @@ import { useQuery } from '@tanstack/react-query';
5
5
  import { QUERY_KEYS } from '../../config/constants/queryKeys.js';
6
6
  import useDebounce from '../../hooks/useDebounce.js';
7
7
  import axios from 'axios';
8
- import { IS_SUI_MAINNET } from '../../constants/suiConstants.js';
9
- import { getSuiFallbackPrices } from '../bonds/suiBondsConfig.js';
8
+ import '../../constants/suiConstants.js';
10
9
 
11
10
  // =============================================================================
12
11
  // useSuiZapQuote — Aftermath Finance DEX aggregator quote for Sui zap
@@ -54,28 +53,6 @@ const useSuiZapQuote = (typedValue, inputToken, bond, account, slippage = 0.5) =
54
53
  };
55
54
  }, [inputTokenAddress, principalTokenAddress, inputAmountAtomic, account]);
56
55
  // -------------------------------------------------------------------------
57
- // Testnet mock: calculate quote from fallback prices (no DEX available)
58
- // -------------------------------------------------------------------------
59
- const testnetMockQuote = useMemo(() => {
60
- if (IS_SUI_MAINNET || !queryParams)
61
- return null;
62
- const fallbackPrices = getSuiFallbackPrices();
63
- const inputPrice = fallbackPrices.find((p) => p.tokenAddress.toLowerCase() === queryParams.coinInType.toLowerCase())?.price;
64
- const outputPrice = fallbackPrices.find((p) => p.tokenAddress.toLowerCase() === queryParams.coinOutType.toLowerCase())?.price;
65
- if (!inputPrice || !outputPrice || outputPrice === 0)
66
- return null;
67
- const inputHuman = new BigNumber(queryParams.coinInAmount).div(new BigNumber(10).pow(inputDecimals));
68
- const outputHuman = inputHuman.times(inputPrice).div(outputPrice);
69
- const outputAtomic = outputHuman.times(new BigNumber(10).pow(principalDecimals)).integerValue(BigNumber.ROUND_FLOOR);
70
- return {
71
- routes: [],
72
- coinIn: { type: queryParams.coinInType, amount: queryParams.coinInAmount, tradeFee: '0' },
73
- coinOut: { type: queryParams.coinOutType, amount: outputAtomic.toFixed(0), tradeFee: '0' },
74
- spotPrice: inputPrice / outputPrice,
75
- netTradeFeePercentage: 0,
76
- };
77
- }, [queryParams, inputDecimals, principalDecimals]);
78
- // -------------------------------------------------------------------------
79
56
  // Mainnet: fetch real quote from Aftermath
80
57
  // -------------------------------------------------------------------------
81
58
  const fetchAftermathQuote = async () => {
@@ -91,13 +68,13 @@ const useSuiZapQuote = (typedValue, inputToken, bond, account, slippage = 0.5) =
91
68
  queryKey: [QUERY_KEYS.ZAP_TOKEN_QUOTE, 'aftermath-sui', queryParams],
92
69
  queryFn: fetchAftermathQuote,
93
70
  refetchInterval: 60000,
94
- enabled: IS_SUI_MAINNET && !!queryParams,
71
+ enabled: !!queryParams,
95
72
  retry: 1,
96
73
  staleTime: 30000,
97
74
  });
98
75
  // Use testnet mock on testnet, real quote on mainnet
99
- const activeQuote = IS_SUI_MAINNET ? (quoteData ?? null) : testnetMockQuote;
100
- const zapError = IS_SUI_MAINNET ? !!error : false;
76
+ const activeQuote = quoteData ?? null;
77
+ const zapError = !!error;
101
78
  // Convert amounts to human-readable
102
79
  const estimatedDepositAmount = activeQuote?.coinOut?.amount
103
80
  ? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))
@@ -111,7 +88,7 @@ const useSuiZapQuote = (typedValue, inputToken, bond, account, slippage = 0.5) =
111
88
  .times(1 - slippage / 100)
112
89
  .toFixed(principalDecimals)
113
90
  : '0';
114
- const zapLoading = IS_SUI_MAINNET ? isLoading || isFetching : false;
91
+ const zapLoading = isLoading || isFetching ;
115
92
  return [zapLoading, activeQuote, estimatedDepositAmount, zapError, minimumDepositAmount];
116
93
  };
117
94
 
@@ -1 +1 @@
1
- {"version":3,"file":"useSuiZapQuote.js","sources":["../../../src/state/zap/useSuiZapQuote.ts"],"sourcesContent":["// =============================================================================\n// useSuiZapQuote — Aftermath Finance DEX aggregator quote for Sui zap\n// =============================================================================\n//\n// Mainnet: fetches a swap quote from Aftermath Finance (Sui DEX aggregator)\n// when the user selects an input token different from the bond's principal token.\n//\n// Testnet: no DEX aggregator supports Sui testnet or our custom test tokens.\n// Returns a mock quote calculated from fallback prices so the full zap UI and\n// two-TX flow (mint → deposit) can be tested end-to-end.\n//\n// Aftermath API: POST https://aftermath.finance/api/router/trade-route\n\nimport { useMemo } from 'react'\nimport BigNumber from 'bignumber.js'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport { useQuery } from '@tanstack/react-query'\nimport { QUERY_KEYS } from '../../config/constants/queryKeys'\nimport { BondsData } from '../../types/bonds'\nimport useDebounce from '../../hooks/useDebounce'\nimport axios from 'axios'\nimport type { SuiZapToken } from '../../config/constants/suiZapTokens'\nimport { IS_SUI_MAINNET } from '../../constants/suiConstants'\nimport { getSuiFallbackPrices } from '../bonds/suiBondsConfig'\n\nconst AFTERMATH_API_URL = 'https://aftermath.finance/api/router/trade-route'\nconst AFTERMATH_TX_API_URL = 'https://aftermath.finance/api/router/transactions/trade'\n\ninterface AftermathCoinInfo {\n type: string\n amount: string // atomic units with BigInt \"n\" suffix, e.g. \"963906n\"\n tradeFee: string\n}\n\nexport interface AftermathTradeRoute {\n routes: Array<{\n paths: Array<Record<string, unknown>>\n portion: string\n coinIn: AftermathCoinInfo\n coinOut: AftermathCoinInfo\n spotPrice: number\n }>\n coinIn: AftermathCoinInfo\n coinOut: AftermathCoinInfo\n spotPrice: number\n netTradeFeePercentage: number\n}\n\n/** Parse Aftermath amount strings — they may have a trailing \"n\" (BigInt notation) */\nconst parseAftermathAmount = (amount: string): string => {\n return amount.replace(/n$/, '')\n}\n\nconst useSuiZapQuote = (\n typedValue: string,\n inputToken: SuiZapToken | undefined,\n bond: BondsData | undefined,\n account?: string,\n slippage = 0.5,\n): [\n loading: boolean,\n response: AftermathTradeRoute | null,\n depositAmount: string,\n error: boolean,\n minimumDepositAmount: string,\n] => {\n const debouncedInput = useDebounce(typedValue, 400)\n\n const principalTokenAddress = bond?.lpToken?.address?.[ChainId.SUI]\n const inputTokenAddress = inputToken?.address\n\n // Aftermath uses atomic units — convert from human-readable\n const inputDecimals = inputToken?.decimals ?? 9\n const inputAmountAtomic = useMemo(() => {\n const val = new BigNumber(debouncedInput ?? '0')\n if (val.isNaN() || val.lte(0)) return '0'\n return val.times(new BigNumber(10).pow(inputDecimals)).integerValue(BigNumber.ROUND_FLOOR).toFixed(0)\n }, [debouncedInput, inputDecimals])\n\n const principalDecimals = bond?.lpToken?.decimals?.[ChainId.SUI] ?? 6\n\n const queryParams = useMemo(() => {\n if (\n !inputTokenAddress ||\n !principalTokenAddress ||\n inputAmountAtomic === '0' ||\n !account ||\n inputTokenAddress.toLowerCase() === principalTokenAddress.toLowerCase()\n ) {\n return null\n }\n\n return {\n coinInType: inputTokenAddress,\n coinOutType: principalTokenAddress,\n coinInAmount: inputAmountAtomic,\n }\n }, [inputTokenAddress, principalTokenAddress, inputAmountAtomic, account])\n\n // -------------------------------------------------------------------------\n // Testnet mock: calculate quote from fallback prices (no DEX available)\n // -------------------------------------------------------------------------\n const testnetMockQuote = useMemo((): AftermathTradeRoute | null => {\n if (IS_SUI_MAINNET || !queryParams) return null\n\n const fallbackPrices = getSuiFallbackPrices()\n const inputPrice = fallbackPrices.find(\n (p) => p.tokenAddress.toLowerCase() === queryParams.coinInType.toLowerCase(),\n )?.price\n const outputPrice = fallbackPrices.find(\n (p) => p.tokenAddress.toLowerCase() === queryParams.coinOutType.toLowerCase(),\n )?.price\n\n if (!inputPrice || !outputPrice || outputPrice === 0) return null\n\n const inputHuman = new BigNumber(queryParams.coinInAmount).div(new BigNumber(10).pow(inputDecimals))\n const outputHuman = inputHuman.times(inputPrice).div(outputPrice)\n const outputAtomic = outputHuman.times(new BigNumber(10).pow(principalDecimals)).integerValue(BigNumber.ROUND_FLOOR)\n\n return {\n routes: [],\n coinIn: { type: queryParams.coinInType, amount: queryParams.coinInAmount, tradeFee: '0' },\n coinOut: { type: queryParams.coinOutType, amount: outputAtomic.toFixed(0), tradeFee: '0' },\n spotPrice: inputPrice / outputPrice,\n netTradeFeePercentage: 0,\n }\n }, [queryParams, inputDecimals, principalDecimals])\n\n // -------------------------------------------------------------------------\n // Mainnet: fetch real quote from Aftermath\n // -------------------------------------------------------------------------\n const fetchAftermathQuote = async (): Promise<AftermathTradeRoute | null> => {\n if (!queryParams) return null\n\n const response = await axios.post<AftermathTradeRoute>(AFTERMATH_API_URL, queryParams)\n const data = response.data\n if (!data?.coinOut?.amount) return null\n return data\n }\n\n const {\n data: quoteData,\n isLoading,\n isFetching,\n error,\n } = useQuery({\n queryKey: [QUERY_KEYS.ZAP_TOKEN_QUOTE, 'aftermath-sui', queryParams],\n queryFn: fetchAftermathQuote,\n refetchInterval: 60000,\n enabled: IS_SUI_MAINNET && !!queryParams,\n retry: 1,\n staleTime: 30000,\n })\n\n // Use testnet mock on testnet, real quote on mainnet\n const activeQuote = IS_SUI_MAINNET ? (quoteData ?? null) : testnetMockQuote\n const zapError = IS_SUI_MAINNET ? !!error : false\n\n // Convert amounts to human-readable\n const estimatedDepositAmount = activeQuote?.coinOut?.amount\n ? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))\n .div(new BigNumber(10).pow(principalDecimals))\n .toFixed(principalDecimals)\n : '0'\n\n // Apply slippage for minimum deposit amount\n const minimumDepositAmount = activeQuote?.coinOut?.amount\n ? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))\n .div(new BigNumber(10).pow(principalDecimals))\n .times(1 - slippage / 100)\n .toFixed(principalDecimals)\n : '0'\n\n const zapLoading = IS_SUI_MAINNET ? isLoading || isFetching : false\n\n return [zapLoading, activeQuote, estimatedDepositAmount, zapError, minimumDepositAmount]\n}\n\nexport { AFTERMATH_TX_API_URL }\n\nexport default useSuiZapQuote\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSuiZapQuote.js","sources":["../../../src/state/zap/useSuiZapQuote.ts"],"sourcesContent":["// =============================================================================\n// useSuiZapQuote — Aftermath Finance DEX aggregator quote for Sui zap\n// =============================================================================\n//\n// Mainnet: fetches a swap quote from Aftermath Finance (Sui DEX aggregator)\n// when the user selects an input token different from the bond's principal token.\n//\n// Testnet: no DEX aggregator supports Sui testnet or our custom test tokens.\n// Returns a mock quote calculated from fallback prices so the full zap UI and\n// two-TX flow (mint → deposit) can be tested end-to-end.\n//\n// Aftermath API: POST https://aftermath.finance/api/router/trade-route\n\nimport { useMemo } from 'react'\nimport BigNumber from 'bignumber.js'\nimport { ChainId } from '@ape.swap/apeswap-lists'\nimport { useQuery } from '@tanstack/react-query'\nimport { QUERY_KEYS } from '../../config/constants/queryKeys'\nimport { BondsData } from '../../types/bonds'\nimport useDebounce from '../../hooks/useDebounce'\nimport axios from 'axios'\nimport type { SuiZapToken } from '../../config/constants/suiZapTokens'\nimport { IS_SUI_MAINNET } from '../../constants/suiConstants'\n\nconst AFTERMATH_API_URL = 'https://aftermath.finance/api/router/trade-route'\nconst AFTERMATH_TX_API_URL = 'https://aftermath.finance/api/router/transactions/trade'\n\ninterface AftermathCoinInfo {\n type: string\n amount: string // atomic units with BigInt \"n\" suffix, e.g. \"963906n\"\n tradeFee: string\n}\n\nexport interface AftermathTradeRoute {\n routes: Array<{\n paths: Array<Record<string, unknown>>\n portion: string\n coinIn: AftermathCoinInfo\n coinOut: AftermathCoinInfo\n spotPrice: number\n }>\n coinIn: AftermathCoinInfo\n coinOut: AftermathCoinInfo\n spotPrice: number\n netTradeFeePercentage: number\n}\n\n/** Parse Aftermath amount strings — they may have a trailing \"n\" (BigInt notation) */\nconst parseAftermathAmount = (amount: string): string => {\n return amount.replace(/n$/, '')\n}\n\nconst useSuiZapQuote = (\n typedValue: string,\n inputToken: SuiZapToken | undefined,\n bond: BondsData | undefined,\n account?: string,\n slippage = 0.5,\n): [\n loading: boolean,\n response: AftermathTradeRoute | null,\n depositAmount: string,\n error: boolean,\n minimumDepositAmount: string,\n] => {\n const debouncedInput = useDebounce(typedValue, 400)\n\n const principalTokenAddress = bond?.lpToken?.address?.[ChainId.SUI]\n const inputTokenAddress = inputToken?.address\n\n // Aftermath uses atomic units — convert from human-readable\n const inputDecimals = inputToken?.decimals ?? 9\n const inputAmountAtomic = useMemo(() => {\n const val = new BigNumber(debouncedInput ?? '0')\n if (val.isNaN() || val.lte(0)) return '0'\n return val.times(new BigNumber(10).pow(inputDecimals)).integerValue(BigNumber.ROUND_FLOOR).toFixed(0)\n }, [debouncedInput, inputDecimals])\n\n const principalDecimals = bond?.lpToken?.decimals?.[ChainId.SUI] ?? 6\n\n const queryParams = useMemo(() => {\n if (\n !inputTokenAddress ||\n !principalTokenAddress ||\n inputAmountAtomic === '0' ||\n !account ||\n inputTokenAddress.toLowerCase() === principalTokenAddress.toLowerCase()\n ) {\n return null\n }\n\n return {\n coinInType: inputTokenAddress,\n coinOutType: principalTokenAddress,\n coinInAmount: inputAmountAtomic,\n }\n }, [inputTokenAddress, principalTokenAddress, inputAmountAtomic, account])\n\n // -------------------------------------------------------------------------\n // Mainnet: fetch real quote from Aftermath\n // -------------------------------------------------------------------------\n const fetchAftermathQuote = async (): Promise<AftermathTradeRoute | null> => {\n if (!queryParams) return null\n\n const response = await axios.post<AftermathTradeRoute>(AFTERMATH_API_URL, queryParams)\n const data = response.data\n if (!data?.coinOut?.amount) return null\n return data\n }\n\n const {\n data: quoteData,\n isLoading,\n isFetching,\n error,\n } = useQuery({\n queryKey: [QUERY_KEYS.ZAP_TOKEN_QUOTE, 'aftermath-sui', queryParams],\n queryFn: fetchAftermathQuote,\n refetchInterval: 60000,\n enabled: IS_SUI_MAINNET && !!queryParams,\n retry: 1,\n staleTime: 30000,\n })\n\n // Use testnet mock on testnet, real quote on mainnet\n const activeQuote = quoteData ?? null\n const zapError = !!error\n\n // Convert amounts to human-readable\n const estimatedDepositAmount = activeQuote?.coinOut?.amount\n ? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))\n .div(new BigNumber(10).pow(principalDecimals))\n .toFixed(principalDecimals)\n : '0'\n\n // Apply slippage for minimum deposit amount\n const minimumDepositAmount = activeQuote?.coinOut?.amount\n ? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))\n .div(new BigNumber(10).pow(principalDecimals))\n .times(1 - slippage / 100)\n .toFixed(principalDecimals)\n : '0'\n\n const zapLoading = IS_SUI_MAINNET ? isLoading || isFetching : false\n\n return [zapLoading, activeQuote, estimatedDepositAmount, zapError, minimumDepositAmount]\n}\n\nexport { AFTERMATH_TX_API_URL }\n\nexport default useSuiZapQuote\n"],"names":[],"mappings":"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"}
@@ -2,5 +2,5 @@ import { SuiClient } from '@mysten/sui/client';
2
2
  export declare const getSuiClient: () => SuiClient;
3
3
  export declare const toAtomicUnits: (amount: string | number, decimals: number) => string;
4
4
  export declare const fromAtomicUnits: (amount: string | number, decimals: number) => number;
5
- export declare const readFreshTrueBondPrice: (marketObjectId: string) => Promise<bigint>;
5
+ export declare const readFreshTrueBondPrice: (marketObjectId?: string) => Promise<bigint>;
6
6
  export declare const verifySuiTxSuccess: (digest: string) => Promise<void>;
@@ -45,14 +45,16 @@ const valueOfToken = (fromDec, toDec, amount) => {
45
45
  return amount / pow10bi(fromDec - toDec);
46
46
  };
47
47
  const readFreshTrueBondPrice = async (marketObjectId) => {
48
+ if (!marketObjectId) {
49
+ throw new Error('marketObjectId is required');
50
+ }
48
51
  const client = getSuiClient();
49
52
  const obj = await client.getObject({ id: marketObjectId, options: { showContent: true } });
50
53
  if (!obj.data?.content || obj.data.content.dataType !== 'moveObject') {
51
54
  throw new Error(`[Sui] Could not read BondMarket ${marketObjectId}`);
52
55
  }
53
56
  const f = obj.data.content.fields;
54
- const terms = f.terms?.fields ??
55
- f.terms;
57
+ const terms = f.terms?.fields ?? f.terms;
56
58
  const totalDebt = BigInt(String(f.total_debt ?? '0'));
57
59
  const payoutInitialSupply = BigInt(String(f.payout_token_initial_supply ?? '0'));
58
60
  const controlVariable = BigInt(String(terms.control_variable ?? '0'));
@@ -75,9 +77,7 @@ const readFreshTrueBondPrice = async (marketObjectId) => {
75
77
  const currentDebtPayout = valueOfToken(principalDecimals, payoutDecimals, currentDebtU64);
76
78
  const payoutScale = pow10bi(payoutDecimals);
77
79
  const principalScale = pow10bi(principalDecimals);
78
- const debtRatio = payoutInitialSupply > 0n
79
- ? (currentDebtPayout * payoutScale) / payoutInitialSupply
80
- : 0n;
80
+ const debtRatio = payoutInitialSupply > 0n ? (currentDebtPayout * payoutScale) / payoutInitialSupply : 0n;
81
81
  const basePrice = (controlVariable * debtRatio * principalScale) / PRICE_DECIMALS;
82
82
  const price = basePrice < minimumPrice ? minimumPrice : basePrice;
83
83
  // calculate_true_price (includes fee)
@@ -1 +1 @@
1
- {"version":3,"file":"suiHelpers.js","sources":["../../src/utils/suiHelpers.ts"],"sourcesContent":["import { SuiClient } from '@mysten/sui/client'\nimport BigNumber from 'bignumber.js'\nimport { SUI_FULLNODE_URL } from '../constants/suiConstants'\n\n// ---------------------------------------------------------------------------\n// Sui client — lazy singleton\n// ---------------------------------------------------------------------------\nlet _suiClient: SuiClient | undefined\n\nexport const getSuiClient = (): SuiClient => {\n if (_suiClient) return _suiClient\n _suiClient = new SuiClient({ url: SUI_FULLNODE_URL })\n return _suiClient\n}\n\n// ---------------------------------------------------------------------------\n// Unit conversion\n// ---------------------------------------------------------------------------\n// User inputs live in \"whole tokens\" (e.g. \"1.5 SUI\"). The Sui SDK\n// serialises numeric function arguments from strings, so we convert to\n// atomic units and return a stringified BigInt.\nexport const toAtomicUnits = (amount: string | number, decimals: number): string => {\n if (amount === '' || amount == null) return '0'\n const bn = new BigNumber(amount).times(new BigNumber(10).pow(decimals))\n // Floor so we never over-spend what the user typed.\n return bn.integerValue(BigNumber.ROUND_FLOOR).toFixed(0)\n}\n\n// Atomic → human-readable.\nexport const fromAtomicUnits = (amount: string | number, decimals: number): number => {\n if (amount === '' || amount == null) return 0\n return new BigNumber(amount).div(new BigNumber(10).pow(decimals)).toNumber()\n}\n\n// ---------------------------------------------------------------------------\n// Fresh bond price reader — reads the BondMarket object on-chain and\n// computes the current true_price using the same bond_math formulas as\n// the Move contract. Returns the u64 value (scaled 1e8).\n// ---------------------------------------------------------------------------\n\nconst PRICE_DECIMALS = 100_000_000n // 1e8\nconst PERCENTAGE_BASE = 1_000_000n // 1e6\n\nconst pow10bi = (n: number): bigint => 10n ** BigInt(n)\n\nconst valueOfToken = (fromDec: number, toDec: number, amount: bigint): bigint => {\n if (toDec >= fromDec) return amount * pow10bi(toDec - fromDec)\n return amount / pow10bi(fromDec - toDec)\n}\n\nexport const readFreshTrueBondPrice = async (marketObjectId: string): Promise<bigint> => {\n const client = getSuiClient()\n const obj = await client.getObject({ id: marketObjectId, options: { showContent: true } })\n\n if (!obj.data?.content || obj.data.content.dataType !== 'moveObject') {\n throw new Error(`[Sui] Could not read BondMarket ${marketObjectId}`)\n }\n\n const f = obj.data.content.fields as Record<string, unknown>\n const terms = (f.terms as Record<string, unknown>)?.fields as Record<string, unknown> ??\n f.terms as Record<string, unknown>\n\n const totalDebt = BigInt(String(f.total_debt ?? '0'))\n const payoutInitialSupply = BigInt(String(f.payout_token_initial_supply ?? '0'))\n const controlVariable = BigInt(String(terms.control_variable ?? '0'))\n const minimumPrice = BigInt(String(terms.minimum_price ?? '0'))\n const vestingTerm = BigInt(String(terms.vesting_term ?? '1'))\n const lastDecay = BigInt(String(f.last_decay ?? '0'))\n const feeInPrincipal = BigInt(String(f.fee_in_principal ?? '0'))\n const payoutDecimals = Number(f.payout_decimals ?? 9)\n const principalDecimals = Number(f.principal_decimals ?? 9)\n\n // Decay debt client-side (same as contract's decay_debt)\n const nowSeconds = BigInt(Math.floor(Date.now() / 1000))\n let currentDebt = totalDebt\n if (nowSeconds > lastDecay && vestingTerm > 0n) {\n const elapsed = nowSeconds - lastDecay\n const decay = (totalDebt * elapsed) / vestingTerm\n currentDebt = totalDebt > decay ? totalDebt - decay : 0n\n }\n\n // calculate_price — contract casts total_debt (u128) to u64 via `as u64`\n const currentDebtU64 = currentDebt & 0xFFFFFFFFFFFFFFFFn\n const currentDebtPayout = valueOfToken(principalDecimals, payoutDecimals, currentDebtU64)\n const payoutScale = pow10bi(payoutDecimals)\n const principalScale = pow10bi(principalDecimals)\n\n const debtRatio = payoutInitialSupply > 0n\n ? (currentDebtPayout * payoutScale) / payoutInitialSupply\n : 0n\n const basePrice = (controlVariable * debtRatio * principalScale) / PRICE_DECIMALS\n\n const price = basePrice < minimumPrice ? minimumPrice : basePrice\n\n // calculate_true_price (includes fee)\n const denominator = PERCENTAGE_BASE - feeInPrincipal\n const truePrice = denominator > 0n ? (price * PERCENTAGE_BASE) / denominator : price\n\n return truePrice\n}\n\n// ---------------------------------------------------------------------------\n// TX status verification — Sui returns digests for failed TXs too.\n// Call this after waitForTransaction to ensure the TX actually succeeded.\n// ---------------------------------------------------------------------------\n\nexport const verifySuiTxSuccess = async (digest: string): Promise<void> => {\n const client = getSuiClient()\n const txBlock = await client.getTransactionBlock({\n digest,\n options: { showEffects: true },\n })\n const status = txBlock.effects?.status\n if (status?.status !== 'success') {\n const errorMsg = status?.error ?? 'Transaction failed on-chain'\n throw new Error(`Transaction failed: ${errorMsg}`)\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"suiHelpers.js","sources":["../../src/utils/suiHelpers.ts"],"sourcesContent":["import { SuiClient } from '@mysten/sui/client'\nimport BigNumber from 'bignumber.js'\nimport { SUI_FULLNODE_URL } from '../constants/suiConstants'\n\n// ---------------------------------------------------------------------------\n// Sui client — lazy singleton\n// ---------------------------------------------------------------------------\nlet _suiClient: SuiClient | undefined\n\nexport const getSuiClient = (): SuiClient => {\n if (_suiClient) return _suiClient\n _suiClient = new SuiClient({ url: SUI_FULLNODE_URL })\n return _suiClient\n}\n\n// ---------------------------------------------------------------------------\n// Unit conversion\n// ---------------------------------------------------------------------------\n// User inputs live in \"whole tokens\" (e.g. \"1.5 SUI\"). The Sui SDK\n// serialises numeric function arguments from strings, so we convert to\n// atomic units and return a stringified BigInt.\nexport const toAtomicUnits = (amount: string | number, decimals: number): string => {\n if (amount === '' || amount == null) return '0'\n const bn = new BigNumber(amount).times(new BigNumber(10).pow(decimals))\n // Floor so we never over-spend what the user typed.\n return bn.integerValue(BigNumber.ROUND_FLOOR).toFixed(0)\n}\n\n// Atomic → human-readable.\nexport const fromAtomicUnits = (amount: string | number, decimals: number): number => {\n if (amount === '' || amount == null) return 0\n return new BigNumber(amount).div(new BigNumber(10).pow(decimals)).toNumber()\n}\n\n// ---------------------------------------------------------------------------\n// Fresh bond price reader — reads the BondMarket object on-chain and\n// computes the current true_price using the same bond_math formulas as\n// the Move contract. Returns the u64 value (scaled 1e8).\n// ---------------------------------------------------------------------------\n\nconst PRICE_DECIMALS = 100_000_000n // 1e8\nconst PERCENTAGE_BASE = 1_000_000n // 1e6\n\nconst pow10bi = (n: number): bigint => 10n ** BigInt(n)\n\nconst valueOfToken = (fromDec: number, toDec: number, amount: bigint): bigint => {\n if (toDec >= fromDec) return amount * pow10bi(toDec - fromDec)\n return amount / pow10bi(fromDec - toDec)\n}\n\nexport const readFreshTrueBondPrice = async (marketObjectId?: string): Promise<bigint> => {\n if (!marketObjectId) {\n throw new Error('marketObjectId is required')\n }\n\n const client = getSuiClient()\n const obj = await client.getObject({ id: marketObjectId, options: { showContent: true } })\n\n if (!obj.data?.content || obj.data.content.dataType !== 'moveObject') {\n throw new Error(`[Sui] Could not read BondMarket ${marketObjectId}`)\n }\n\n const f = obj.data.content.fields as Record<string, unknown>\n const terms =\n ((f.terms as Record<string, unknown>)?.fields as Record<string, unknown>) ?? (f.terms as Record<string, unknown>)\n\n const totalDebt = BigInt(String(f.total_debt ?? '0'))\n const payoutInitialSupply = BigInt(String(f.payout_token_initial_supply ?? '0'))\n const controlVariable = BigInt(String(terms.control_variable ?? '0'))\n const minimumPrice = BigInt(String(terms.minimum_price ?? '0'))\n const vestingTerm = BigInt(String(terms.vesting_term ?? '1'))\n const lastDecay = BigInt(String(f.last_decay ?? '0'))\n const feeInPrincipal = BigInt(String(f.fee_in_principal ?? '0'))\n const payoutDecimals = Number(f.payout_decimals ?? 9)\n const principalDecimals = Number(f.principal_decimals ?? 9)\n\n // Decay debt client-side (same as contract's decay_debt)\n const nowSeconds = BigInt(Math.floor(Date.now() / 1000))\n let currentDebt = totalDebt\n if (nowSeconds > lastDecay && vestingTerm > 0n) {\n const elapsed = nowSeconds - lastDecay\n const decay = (totalDebt * elapsed) / vestingTerm\n currentDebt = totalDebt > decay ? totalDebt - decay : 0n\n }\n\n // calculate_price — contract casts total_debt (u128) to u64 via `as u64`\n const currentDebtU64 = currentDebt & 0xffffffffffffffffn\n const currentDebtPayout = valueOfToken(principalDecimals, payoutDecimals, currentDebtU64)\n const payoutScale = pow10bi(payoutDecimals)\n const principalScale = pow10bi(principalDecimals)\n\n const debtRatio = payoutInitialSupply > 0n ? (currentDebtPayout * payoutScale) / payoutInitialSupply : 0n\n const basePrice = (controlVariable * debtRatio * principalScale) / PRICE_DECIMALS\n\n const price = basePrice < minimumPrice ? minimumPrice : basePrice\n\n // calculate_true_price (includes fee)\n const denominator = PERCENTAGE_BASE - feeInPrincipal\n const truePrice = denominator > 0n ? (price * PERCENTAGE_BASE) / denominator : price\n\n return truePrice\n}\n\n// ---------------------------------------------------------------------------\n// TX status verification — Sui returns digests for failed TXs too.\n// Call this after waitForTransaction to ensure the TX actually succeeded.\n// ---------------------------------------------------------------------------\n\nexport const verifySuiTxSuccess = async (digest: string): Promise<void> => {\n const client = getSuiClient()\n const txBlock = await client.getTransactionBlock({\n digest,\n options: { showEffects: true },\n })\n const status = txBlock.effects?.status\n if (status?.status !== 'success') {\n const errorMsg = status?.error ?? 'Transaction failed on-chain'\n throw new Error(`Transaction failed: ${errorMsg}`)\n }\n}\n"],"names":[],"mappings":"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"}
@@ -29,12 +29,82 @@ import Flex from '../../components/uikit-sdk/Flex/index.js';
29
29
  import Text from '../../components/uikit-sdk/Text/index.js';
30
30
  import Button from '../../components/uikit-sdk/Button/Button.js';
31
31
  import SafeHTMLComponent from '../../components/SafeHTMLComponent/index.js';
32
- import { IS_SUI_MAINNET, SUI_PRINCIPAL_TOKEN_ADMIN_TESTNET, SUI_MINT_PRINCIPAL_FUNCTION, SUI_BOND_MARKET, SUI_TREASURY, SUI_BOND_NFT_REGISTRY, SUI_DEPOSIT_FUNCTION } from '../../constants/suiConstants.js';
32
+ import { SUI_DEPOSIT_FUNCTION, SUI_PACKAGE_ID } from '../../constants/suiConstants.js';
33
33
  import { getSuiClient, readFreshTrueBondPrice, verifySuiTxSuccess } from '../../utils/suiHelpers.js';
34
34
  import { useQueryClient } from '@tanstack/react-query';
35
35
  import { QUERY_KEYS } from '../../config/constants/queryKeys.js';
36
36
  import axios from 'axios';
37
37
 
38
+ // ---------------------------------------------------------------------------
39
+ // Helper: extract the bill_id from a confirmed Sui deposit transaction.
40
+ // Logs the full tx block so the bill_id can be located manually if the
41
+ // automatic extraction fails.
42
+ // ---------------------------------------------------------------------------
43
+ const extractSuiBillId = async (digest) => {
44
+ const suiClient = getSuiClient();
45
+ const txBlock = await suiClient.getTransactionBlock({
46
+ digest,
47
+ options: {
48
+ showEffects: true,
49
+ showObjectChanges: true,
50
+ showEvents: true,
51
+ },
52
+ });
53
+ console.log('[SUI AFTER-PURCHASE] Full tx block for digest', digest, txBlock);
54
+ console.log('[SUI AFTER-PURCHASE] objectChanges:', txBlock.objectChanges);
55
+ console.log('[SUI AFTER-PURCHASE] events:', txBlock.events);
56
+ // ── Strategy 1: find the created BondNFT in objectChanges ──────────────
57
+ const bondNftType = `${SUI_PACKAGE_ID}::bond_nft::BondNFT`;
58
+ const createdNft = txBlock.objectChanges?.find((change) => change.type === 'created' &&
59
+ 'objectType' in change &&
60
+ change.objectType?.includes(bondNftType));
61
+ console.log('[SUI AFTER-PURCHASE] Created BondNFT objectChange:', createdNft);
62
+ if (createdNft && 'objectId' in createdNft) {
63
+ const nftObjectId = createdNft.objectId;
64
+ console.log('[SUI AFTER-PURCHASE] BondNFT objectId:', nftObjectId);
65
+ try {
66
+ const nftObj = await suiClient.getObject({
67
+ id: nftObjectId,
68
+ options: { showContent: true },
69
+ });
70
+ console.log('[SUI AFTER-PURCHASE] BondNFT full object:', nftObj);
71
+ if (nftObj.data?.content?.dataType === 'moveObject') {
72
+ const fields = nftObj.data.content.fields;
73
+ console.log('[SUI AFTER-PURCHASE] BondNFT fields:', fields);
74
+ const billId = String(fields?.bill_id ?? '');
75
+ if (billId && billId !== 'undefined' && billId !== '') {
76
+ console.log('[SUI AFTER-PURCHASE] ✅ Extracted bill_id from BondNFT object:', billId);
77
+ return billId;
78
+ }
79
+ }
80
+ }
81
+ catch (e) {
82
+ console.warn('[SUI AFTER-PURCHASE] Failed to read BondNFT object:', nftObjectId, e);
83
+ }
84
+ }
85
+ // ── Strategy 2: look for bill_id in emitted Move events ────────────────
86
+ if (txBlock.events && txBlock.events.length > 0) {
87
+ for (const event of txBlock.events) {
88
+ console.log('[SUI AFTER-PURCHASE] Event type:', event.type, 'parsedJson:', event.parsedJson);
89
+ const eventData = event.parsedJson;
90
+ if (eventData?.bill_id !== undefined) {
91
+ const billId = String(eventData.bill_id);
92
+ console.log('[SUI AFTER-PURCHASE] ✅ Extracted bill_id from event:', event.type, billId);
93
+ return billId;
94
+ }
95
+ }
96
+ }
97
+ // ── Strategy 3: log all object changes for manual inspection ──────────
98
+ if (txBlock.objectChanges) {
99
+ for (const change of txBlock.objectChanges) {
100
+ console.log('[SUI AFTER-PURCHASE] objectChange entry:', change);
101
+ }
102
+ }
103
+ console.warn('[SUI AFTER-PURCHASE] ⚠️ Could not extract bill_id automatically from digest:', digest, '— check the logs above to find it manually.');
104
+ // Signal that the tx succeeded but we don't have a bill ID yet.
105
+ // ModalHandler will fall back to a query-refresh-based flow.
106
+ return `digest:${digest}`;
107
+ };
38
108
  const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, setBillId, }) => {
39
109
  const SDKConfig = useSDKConfig();
40
110
  const queryClient = useQueryClient();
@@ -122,7 +192,7 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
122
192
  .times(new BigNumber(10).pow(principalDecimals))
123
193
  .integerValue(BigNumber.ROUND_FLOOR)
124
194
  .toFixed(0);
125
- const market = bondData.contractAddress[bondData.chainId] ?? SUI_BOND_MARKET;
195
+ const market = bondData.contractAddress[bondData.chainId];
126
196
  // Read fresh bond price from chain to avoid stale cache after previous purchases
127
197
  const freshTruePrice = await readFreshTrueBondPrice(market);
128
198
  const atomicMaxPrice = new BigNumber(freshTruePrice.toString())
@@ -157,13 +227,16 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
157
227
  [principalCoin] = tx.splitCoins(tx.object(userCoins.data[0].coinObjectId), [tx.pure.u64(atomicAmount)]);
158
228
  }
159
229
  }
230
+ if (!market || !bondData?.suiTreasuryId || !bondData?.billNnftAddress?.[ChainId.SUI]) {
231
+ return;
232
+ }
160
233
  tx.moveCall({
161
234
  target: SUI_DEPOSIT_FUNCTION,
162
235
  typeArguments: [payoutCoinType, principalCoinType],
163
236
  arguments: [
164
237
  tx.object(market),
165
- tx.object(bondData.suiTreasuryId ?? SUI_TREASURY),
166
- tx.object(SUI_BOND_NFT_REGISTRY),
238
+ tx.object(bondData.suiTreasuryId),
239
+ tx.object(bondData.billNnftAddress[ChainId.SUI]),
167
240
  principalCoin,
168
241
  tx.pure.u64(atomicMaxPrice),
169
242
  tx.pure.address(suiAccount),
@@ -186,6 +259,11 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
186
259
  const suiClient = getSuiClient();
187
260
  await suiClient.waitForTransaction({ digest });
188
261
  await verifySuiTxSuccess(digest);
262
+ // Extract the bill_id from the confirmed transaction.
263
+ // extractSuiBillId logs the full tx block so you can inspect the raw data
264
+ // in case automatic extraction fails.
265
+ const billId = await extractSuiBillId(digest);
266
+ console.log('[SUI AFTER-PURCHASE] handleBuyCallback billId:', billId);
189
267
  // Invalidate bonds data so next purchase uses fresh pricing
190
268
  await queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.BONDS_DATA] });
191
269
  // Sui's indexer has a short lag — getOwnedObjects may not return the
@@ -207,9 +285,7 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
207
285
  },
208
286
  });
209
287
  addToastSuccess(digest, bondChain);
210
- // Sui does not return a bill_id from the tx response easily —
211
- // the frontend will pick it up on the next query refresh.
212
- setBillId?.('');
288
+ setBillId?.(billId);
213
289
  }
214
290
  catch (error) {
215
291
  console.error('[SUI DEBUG] Error during Sui bond purchase:', error);
@@ -247,47 +323,47 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
247
323
  .integerValue(BigNumber.ROUND_FLOOR)
248
324
  .toFixed(0);
249
325
  // TX1: Acquire principal tokens
250
- if (IS_SUI_MAINNET) {
251
- // Mainnet: swap via Aftermath DEX aggregator
252
- const txResponse = await axios.post(AFTERMATH_TX_API_URL, {
253
- walletAddress: suiAccount,
254
- completeRoute: aftermathQuote,
255
- slippage: slippage / 100,
256
- });
257
- const swapTxBytes = txResponse.data;
258
- if (!swapTxBytes)
259
- throw new Error('Aftermath returned no transaction data');
260
- const swapTx = Transaction.from(swapTxBytes);
261
- const swapResponse = await signAndExecuteTransaction({ transaction: swapTx });
262
- const swapDigest = swapResponse?.digest;
263
- if (!swapDigest)
264
- throw new Error('No tx digest returned for swap');
265
- await suiClient.waitForTransaction({ digest: swapDigest });
266
- }
267
- else {
268
- // Testnet: mint principal tokens via test_principal_token::mint
269
- // No DEX aggregator supports Sui testnet or custom test tokens.
270
- const mintTx = new Transaction();
271
- mintTx.moveCall({
272
- target: SUI_MINT_PRINCIPAL_FUNCTION,
273
- arguments: [
274
- mintTx.object(SUI_PRINCIPAL_TOKEN_ADMIN_TESTNET),
275
- mintTx.pure.u64(depositAmountAtomic),
276
- mintTx.pure.address(suiAccount),
277
- ],
278
- });
279
- mintTx.setSender(suiAccount);
280
- mintTx.setGasBudget(50000000);
281
- const builtMint = await mintTx.build({ client: suiClient });
282
- const prebuiltMint = Transaction.from(builtMint);
283
- const mintResponse = await signAndExecuteTransaction({ transaction: prebuiltMint });
284
- const mintDigest = mintResponse?.digest;
285
- if (!mintDigest)
286
- throw new Error('No tx digest returned for testnet mint');
287
- await suiClient.waitForTransaction({ digest: mintDigest });
288
- }
326
+ // if (IS_SUI_MAINNET) {
327
+ // Mainnet: swap via Aftermath DEX aggregator
328
+ const txResponse = await axios.post(AFTERMATH_TX_API_URL, {
329
+ walletAddress: suiAccount,
330
+ completeRoute: aftermathQuote,
331
+ slippage: slippage / 100,
332
+ });
333
+ const swapTxBytes = txResponse.data;
334
+ if (!swapTxBytes)
335
+ throw new Error('Aftermath returned no transaction data');
336
+ const swapTx = Transaction.from(swapTxBytes);
337
+ const swapResponse = await signAndExecuteTransaction({ transaction: swapTx });
338
+ const swapDigest = swapResponse?.digest;
339
+ if (!swapDigest)
340
+ throw new Error('No tx digest returned for swap');
341
+ await suiClient.waitForTransaction({ digest: swapDigest });
342
+ // } else {
343
+ // // Testnet: mint principal tokens via test_principal_token::mint
344
+ // // No DEX aggregator supports Sui testnet or custom test tokens.
345
+ // const mintTx = new Transaction()
346
+ // mintTx.moveCall({
347
+ // target: SUI_MINT_PRINCIPAL_FUNCTION,
348
+ // arguments: [
349
+ // mintTx.object(SUI_PRINCIPAL_TOKEN_ADMIN_TESTNET),
350
+ // mintTx.pure.u64(depositAmountAtomic),
351
+ // mintTx.pure.address(suiAccount),
352
+ // ],
353
+ // })
354
+ //
355
+ // mintTx.setSender(suiAccount)
356
+ // mintTx.setGasBudget(50_000_000)
357
+ // const builtMint = await mintTx.build({ client: suiClient })
358
+ // const prebuiltMint = Transaction.from(builtMint)
359
+ //
360
+ // const mintResponse = await signAndExecuteTransaction({ transaction: prebuiltMint })
361
+ // const mintDigest = mintResponse?.digest
362
+ // if (!mintDigest) throw new Error('No tx digest returned for testnet mint')
363
+ // await suiClient.waitForTransaction({ digest: mintDigest })
364
+ // }
289
365
  // TX2: Deposit into bond with principal tokens
290
- const market = bondData.contractAddress[bondData.chainId] ?? SUI_BOND_MARKET;
366
+ const market = bondData.contractAddress[ChainId.SUI];
291
367
  // Read fresh bond price from chain to avoid stale cache
292
368
  const freshTruePrice = await readFreshTrueBondPrice(market);
293
369
  const atomicMaxPrice = new BigNumber(freshTruePrice.toString())
@@ -322,13 +398,16 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
322
398
  ]);
323
399
  }
324
400
  }
401
+ if (!market || !bondData?.suiTreasuryId || !bondData?.billNnftAddress?.[ChainId.SUI]) {
402
+ return;
403
+ }
325
404
  tx.moveCall({
326
405
  target: SUI_DEPOSIT_FUNCTION,
327
406
  typeArguments: [payoutCoinType, principalCoinType],
328
407
  arguments: [
329
408
  tx.object(market),
330
- tx.object(bondData.suiTreasuryId ?? SUI_TREASURY),
331
- tx.object(SUI_BOND_NFT_REGISTRY),
409
+ tx.object(bondData.suiTreasuryId),
410
+ tx.object(bondData.billNnftAddress[ChainId.SUI]),
332
411
  principalCoin,
333
412
  tx.pure.u64(atomicMaxPrice),
334
413
  tx.pure.address(suiAccount),
@@ -345,6 +424,11 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
345
424
  throw new Error('No tx digest returned for deposit');
346
425
  await suiClient.waitForTransaction({ digest: depositDigest });
347
426
  await verifySuiTxSuccess(depositDigest);
427
+ // Extract the bill_id from the confirmed deposit transaction.
428
+ // extractSuiBillId logs the full tx block so you can inspect the raw data
429
+ // in case automatic extraction fails.
430
+ const billId = await extractSuiBillId(depositDigest);
431
+ console.log('[SUI AFTER-PURCHASE] handleZapCallback billId:', billId);
348
432
  // Invalidate bonds data so next purchase uses fresh pricing
349
433
  await queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.BONDS_DATA] });
350
434
  // Sui indexer lag — schedule delayed re-invalidations for user bonds
@@ -374,7 +458,7 @@ const BuyComponentSui = ({ onDismiss, bondAddress, bondChain, isProjectView, set
374
458
  },
375
459
  });
376
460
  addToastSuccess(depositDigest, bondChain);
377
- setBillId?.('');
461
+ setBillId?.(billId);
378
462
  }
379
463
  catch (error) {
380
464
  console.error('Error during Sui zap bond purchase:', error);