@ape.swap/bonds-sdk 6.1.17-test.1 → 6.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ConnectAptosWalletButton/ConnectAptosWalletButton.js +32 -0
- package/dist/components/ConnectAptosWalletButton/ConnectAptosWalletButton.js.map +1 -0
- package/dist/components/ConnectButton/index.js +1 -1
- package/dist/components/ConnectButton/index.js.map +1 -1
- package/dist/components/ConnectSolanaWalletButton/ConnectSolanaWalletButton.js +3 -15
- package/dist/components/ConnectSolanaWalletButton/ConnectSolanaWalletButton.js.map +1 -1
- package/dist/components/ConnectoToVmBanners/ConnectToAptosBanner.js +2 -6
- package/dist/components/ConnectoToVmBanners/ConnectToAptosBanner.js.map +1 -1
- package/dist/components/ConnectoToVmBanners/ConnectToSolanaBanner.js +1 -3
- package/dist/components/ConnectoToVmBanners/ConnectToSolanaBanner.js.map +1 -1
- package/dist/components/ConnectoToVmBanners/ConnectToSuiBanner.js +2 -8
- package/dist/components/ConnectoToVmBanners/ConnectToSuiBanner.js.map +1 -1
- package/dist/components/uikit-sdk/Svg/Icons/NewOffer.js.map +1 -1
- package/dist/components/uikit-sdk/Svg/Icons/Robinhood.d.ts +4 -0
- package/dist/components/uikit-sdk/Svg/Icons/Robinhood.js +9 -0
- package/dist/components/uikit-sdk/Svg/Icons/Robinhood.js.map +1 -0
- package/dist/components/uikit-sdk/Svg/Icons/SellingFast.js.map +1 -1
- package/dist/components/uikit-sdk/Svg/Icons/WhaleActivity.js.map +1 -1
- package/dist/config/constants/addresses.js +3 -1
- package/dist/config/constants/addresses.js.map +1 -1
- package/dist/config/constants/aptosZapTokens.js +15 -1
- package/dist/config/constants/aptosZapTokens.js.map +1 -1
- package/dist/config/constants/chains.js +6 -0
- package/dist/config/constants/chains.js.map +1 -1
- package/dist/config/constants/networks.js +1 -0
- package/dist/config/constants/networks.js.map +1 -1
- package/dist/config/constants/suiZapTokens.js +36 -0
- package/dist/config/constants/suiZapTokens.js.map +1 -0
- package/dist/config/constants/variables.d.ts +1 -1
- package/dist/config/constants/variables.js +1 -0
- package/dist/config/constants/variables.js.map +1 -1
- package/dist/constants/aptosConstants.js +5 -1
- package/dist/constants/aptosConstants.js.map +1 -1
- package/dist/constants/suiConstants.js +3 -1
- package/dist/constants/suiConstants.js.map +1 -1
- package/dist/hooks/accounts/useSOLAccount.js +0 -1
- package/dist/hooks/accounts/useSOLAccount.js.map +1 -1
- package/dist/state/bonds/useBondsData.js +0 -1
- package/dist/state/bonds/useBondsData.js.map +1 -1
- package/dist/state/useSDKConfig.d.ts +1 -1
- package/dist/state/useSDKConfig.js +5 -2
- package/dist/state/useSDKConfig.js.map +1 -1
- package/dist/state/zap/getChainParam.d.ts +2 -1
- package/dist/state/zap/getChainParam.js +4 -1
- package/dist/state/zap/getChainParam.js.map +1 -1
- package/dist/state/zap/useAptosZapQuote.js +83 -0
- package/dist/state/zap/useAptosZapQuote.js.map +1 -0
- package/dist/state/zap/useSuiZapQuote.js +96 -0
- package/dist/state/zap/useSuiZapQuote.js.map +1 -0
- package/dist/utils/aptosHelpers.js +25 -1
- package/dist/utils/aptosHelpers.js.map +1 -1
- package/dist/views/BuyBond/BuyBondModal.js +3 -14
- package/dist/views/BuyBond/BuyBondModal.js.map +1 -1
- package/dist/views/BuyBond/BuyComponentAptos.js +349 -0
- package/dist/views/BuyBond/BuyComponentAptos.js.map +1 -0
- package/dist/views/BuyBond/BuyComponentSui.js +500 -0
- package/dist/views/BuyBond/BuyComponentSui.js.map +1 -0
- package/dist/views/BuyBond/index.d.ts +2 -2
- package/dist/views/BuyBond/index.js.map +1 -1
- package/dist/views/YourBonds/components/UserBondRow/UserBondRowSolana.js +2 -10
- package/dist/views/YourBonds/components/UserBondRow/UserBondRowSolana.js.map +1 -1
- package/dist/views/YourBondsModal/components/TransferBondModal/TransferActionSolana.js +2 -9
- package/dist/views/YourBondsModal/components/TransferBondModal/TransferActionSolana.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import BigNumber from 'bignumber.js';
|
|
3
|
+
import { ChainId } from '@ape.swap/apeswap-lists';
|
|
4
|
+
import { useQuery } from '@tanstack/react-query';
|
|
5
|
+
import { QUERY_KEYS } from '../../config/constants/queryKeys.js';
|
|
6
|
+
import useDebounce from '../../hooks/useDebounce.js';
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import '../../constants/suiConstants.js';
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// useSuiZapQuote — Aftermath Finance DEX aggregator quote for Sui zap
|
|
12
|
+
// =============================================================================
|
|
13
|
+
//
|
|
14
|
+
// Mainnet: fetches a swap quote from Aftermath Finance (Sui DEX aggregator)
|
|
15
|
+
// when the user selects an input token different from the bond's principal token.
|
|
16
|
+
//
|
|
17
|
+
// Testnet: no DEX aggregator supports Sui testnet or our custom test tokens.
|
|
18
|
+
// Returns a mock quote calculated from fallback prices so the full zap UI and
|
|
19
|
+
// two-TX flow (mint → deposit) can be tested end-to-end.
|
|
20
|
+
//
|
|
21
|
+
// Aftermath API: POST https://aftermath.finance/api/router/trade-route
|
|
22
|
+
const AFTERMATH_API_URL = 'https://aftermath.finance/api/router/trade-route';
|
|
23
|
+
const AFTERMATH_TX_API_URL = 'https://aftermath.finance/api/router/transactions/trade';
|
|
24
|
+
/** Parse Aftermath amount strings — they may have a trailing "n" (BigInt notation) */
|
|
25
|
+
const parseAftermathAmount = (amount) => {
|
|
26
|
+
return amount.replace(/n$/, '');
|
|
27
|
+
};
|
|
28
|
+
const useSuiZapQuote = (typedValue, inputToken, bond, account, slippage = 0.5) => {
|
|
29
|
+
const debouncedInput = useDebounce(typedValue, 400);
|
|
30
|
+
const principalTokenAddress = bond?.lpToken?.address?.[ChainId.SUI];
|
|
31
|
+
const inputTokenAddress = inputToken?.address?.[ChainId.SUI];
|
|
32
|
+
// Aftermath uses atomic units — convert from human-readable
|
|
33
|
+
const inputDecimals = inputToken?.decimals?.[ChainId.SUI] ?? 9;
|
|
34
|
+
const inputAmountAtomic = useMemo(() => {
|
|
35
|
+
const val = new BigNumber(debouncedInput ?? '0');
|
|
36
|
+
if (val.isNaN() || val.lte(0))
|
|
37
|
+
return '0';
|
|
38
|
+
return val.times(new BigNumber(10).pow(inputDecimals)).integerValue(BigNumber.ROUND_FLOOR).toFixed(0);
|
|
39
|
+
}, [debouncedInput, inputDecimals]);
|
|
40
|
+
const principalDecimals = bond?.lpToken?.decimals?.[ChainId.SUI] ?? 6;
|
|
41
|
+
const queryParams = useMemo(() => {
|
|
42
|
+
if (!inputTokenAddress ||
|
|
43
|
+
!principalTokenAddress ||
|
|
44
|
+
inputAmountAtomic === '0' ||
|
|
45
|
+
!account ||
|
|
46
|
+
inputTokenAddress.toLowerCase() === principalTokenAddress.toLowerCase()) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
coinInType: inputTokenAddress,
|
|
51
|
+
coinOutType: principalTokenAddress,
|
|
52
|
+
coinInAmount: inputAmountAtomic,
|
|
53
|
+
};
|
|
54
|
+
}, [inputTokenAddress, principalTokenAddress, inputAmountAtomic, account]);
|
|
55
|
+
// -------------------------------------------------------------------------
|
|
56
|
+
// Mainnet: fetch real quote from Aftermath
|
|
57
|
+
// -------------------------------------------------------------------------
|
|
58
|
+
const fetchAftermathQuote = async () => {
|
|
59
|
+
if (!queryParams)
|
|
60
|
+
return null;
|
|
61
|
+
const response = await axios.post(AFTERMATH_API_URL, queryParams);
|
|
62
|
+
const data = response.data;
|
|
63
|
+
if (!data?.coinOut?.amount)
|
|
64
|
+
return null;
|
|
65
|
+
return data;
|
|
66
|
+
};
|
|
67
|
+
const { data: quoteData, isLoading, isFetching, error, } = useQuery({
|
|
68
|
+
queryKey: [QUERY_KEYS.ZAP_TOKEN_QUOTE, 'aftermath-sui', queryParams],
|
|
69
|
+
queryFn: fetchAftermathQuote,
|
|
70
|
+
refetchInterval: 60000,
|
|
71
|
+
enabled: !!queryParams,
|
|
72
|
+
retry: 1,
|
|
73
|
+
staleTime: 30000,
|
|
74
|
+
});
|
|
75
|
+
// Use testnet mock on testnet, real quote on mainnet
|
|
76
|
+
const activeQuote = quoteData ?? null;
|
|
77
|
+
const zapError = !!error;
|
|
78
|
+
// Convert amounts to human-readable
|
|
79
|
+
const estimatedDepositAmount = activeQuote?.coinOut?.amount
|
|
80
|
+
? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))
|
|
81
|
+
.div(new BigNumber(10).pow(principalDecimals))
|
|
82
|
+
.toFixed(principalDecimals)
|
|
83
|
+
: '0';
|
|
84
|
+
// Apply slippage for minimum deposit amount
|
|
85
|
+
const minimumDepositAmount = activeQuote?.coinOut?.amount
|
|
86
|
+
? new BigNumber(parseAftermathAmount(activeQuote.coinOut.amount))
|
|
87
|
+
.div(new BigNumber(10).pow(principalDecimals))
|
|
88
|
+
.times(1 - slippage / 100)
|
|
89
|
+
.toFixed(principalDecimals)
|
|
90
|
+
: '0';
|
|
91
|
+
const zapLoading = isLoading || isFetching ;
|
|
92
|
+
return [zapLoading, activeQuote, estimatedDepositAmount, zapError, minimumDepositAmount];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export { AFTERMATH_TX_API_URL, useSuiZapQuote as default };
|
|
96
|
+
//# sourceMappingURL=useSuiZapQuote.js.map
|
|
@@ -0,0 +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, Token } 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 { 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: Token | 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?.[ChainId.SUI]\n\n // Aftermath uses atomic units — convert from human-readable\n const inputDecimals = inputToken?.decimals?.[ChainId.SUI] ?? 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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"}
|
|
@@ -72,6 +72,30 @@ const bytesToHex = (bytes) => {
|
|
|
72
72
|
}
|
|
73
73
|
return out;
|
|
74
74
|
};
|
|
75
|
+
const extractEventData = (txn, eventSuffix) => {
|
|
76
|
+
const event = txn?.events?.find((e) => e.type.includes(eventSuffix));
|
|
77
|
+
return event?.data;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* `bond::DepositEvent` — emitted by the deposit entry function.
|
|
81
|
+
* Contains bill_id, bill_address, deposit_amount, payout_amount, price.
|
|
82
|
+
*/
|
|
83
|
+
const extractDepositEvent = (txn) => extractEventData(txn, '::DepositEvent');
|
|
84
|
+
/**
|
|
85
|
+
* Extract bill_id from transaction events. Checks both `BillMinted` (from bond_nft)
|
|
86
|
+
* and `DepositEvent` (from bond) since the contract emits both.
|
|
87
|
+
*/
|
|
88
|
+
const extractBillMinted = (txn) => {
|
|
89
|
+
// Try BillMinted event first (emitted by bond_nft module)
|
|
90
|
+
const billMinted = extractEventData(txn, '::BillMinted');
|
|
91
|
+
if (billMinted)
|
|
92
|
+
return billMinted;
|
|
93
|
+
// Fall back to DepositEvent (emitted by bond module)
|
|
94
|
+
const deposit = extractDepositEvent(txn);
|
|
95
|
+
if (!deposit)
|
|
96
|
+
return undefined;
|
|
97
|
+
return { bill_id: deposit.bill_id, owner: deposit.depositor };
|
|
98
|
+
};
|
|
75
99
|
// ---------------------------------------------------------------------------
|
|
76
100
|
// View function readers (PLAN_001 Etapa 5b — real read skeleton)
|
|
77
101
|
// ---------------------------------------------------------------------------
|
|
@@ -174,5 +198,5 @@ const readNftAddressByBillId = async (billId) => {
|
|
|
174
198
|
return String(result[0] ?? '');
|
|
175
199
|
};
|
|
176
200
|
|
|
177
|
-
export { getAptosClient, readAptosBillInfo, readAptosMarketInfo, readBondMarketByBillId, readNftAddressByBillId, readUserBillIds, readVestingProgress, safeExtractAddress };
|
|
201
|
+
export { extractBillMinted, extractDepositEvent, extractEventData, getAptosClient, readAptosBillInfo, readAptosMarketInfo, readBondMarketByBillId, readNftAddressByBillId, readUserBillIds, readVestingProgress, safeExtractAddress };
|
|
178
202
|
//# sourceMappingURL=aptosHelpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aptosHelpers.js","sources":["../../src/utils/aptosHelpers.ts"],"sourcesContent":["import { Aptos, AptosConfig } from '@aptos-labs/ts-sdk'\nimport BigNumber from 'bignumber.js'\nimport {\n APTOS_CLAIM_FUNCTION,\n APTOS_DEPOSIT_FUNCTION,\n APTOS_FULLNODE_URL,\n APTOS_NETWORK,\n APTOS_VIEW_BILL_INFO,\n APTOS_VIEW_BOND_MARKET_BY_ID,\n APTOS_VIEW_MARKET_INFO,\n APTOS_VIEW_NFT_ADDRESS,\n APTOS_VIEW_USER_BILLS,\n APTOS_VIEW_VESTING_PROGRESS,\n getRandomAptosApiKey,\n} from '../constants/aptosConstants'\nimport type {\n AptosBillInfo,\n AptosClaimPayload,\n AptosDepositPayload,\n AptosMarketInfoTuple,\n AptosVestingProgressTuple,\n} from '../types/aptosBonds'\n\n// ---------------------------------------------------------------------------\n// Aptos client — lazy singleton\n// ---------------------------------------------------------------------------\n// We keep one client per (network, api key) pair. The API key is picked with\n// `getRandomAptosApiKey()` on first call, so different sessions hit a\n// different key (very simple load balancing). If the pool is empty we fall\n// back to unauthenticated requests.\nlet _aptosClient: Aptos | undefined\n\nexport const getAptosClient = (): Aptos => {\n if (_aptosClient) return _aptosClient\n const apiKey = getRandomAptosApiKey()\n // The Aptos ts-sdk exposes `API_KEY` directly on `clientConfig`; it adds\n // the `Authorization: Bearer <key>` header internally. This is the\n // idiomatic way to authenticate — no manual header wiring needed.\n const config = new AptosConfig({\n network: APTOS_NETWORK,\n ...(apiKey && { clientConfig: { API_KEY: apiKey } }),\n })\n _aptosClient = new Aptos(config)\n return _aptosClient\n}\n\n// ---------------------------------------------------------------------------\n// Address normalisation\n// ---------------------------------------------------------------------------\n// Aptos wallet adapters can surface account addresses in several shapes:\n// plain string, `{ data: Uint8Array }`, `Uint8Array`, or an `AccountAddress`\n// instance. This helper returns a canonical `0x`-prefixed hex string, or\n// `undefined` if the input cannot be decoded. We keep the interface loose so\n// callers can pass whatever the SDK hands them.\nexport const safeExtractAddress = (input: unknown): string | undefined => {\n if (input == null) return undefined\n\n // Already a string\n if (typeof input === 'string') {\n return input.startsWith('0x') ? input : `0x${input}`\n }\n\n // AccountAddress instance (has `toString()` that returns `0x...`)\n if (typeof input === 'object' && typeof (input as { toString?: () => string }).toString === 'function') {\n const asObject = input as { data?: Uint8Array | number[]; toString: () => string }\n // Prefer `.data` if it looks like a byte array — `.toString()` on\n // AccountAddress already returns the canonical hex, but some wallets\n // return `{ data: Uint8Array }` without the method.\n if (asObject.data instanceof Uint8Array) {\n return bytesToHex(asObject.data)\n }\n if (Array.isArray(asObject.data)) {\n return bytesToHex(new Uint8Array(asObject.data))\n }\n const stringified = asObject.toString()\n if (stringified.startsWith('0x')) return stringified\n }\n\n // Raw Uint8Array\n if (input instanceof Uint8Array) {\n return bytesToHex(input)\n }\n\n // Raw number[]\n if (Array.isArray(input)) {\n return bytesToHex(new Uint8Array(input))\n }\n\n return undefined\n}\n\nconst bytesToHex = (bytes: Uint8Array): string => {\n let out = '0x'\n for (let i = 0; i < bytes.length; i++) {\n out += bytes[i].toString(16).padStart(2, '0')\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Unit conversion\n// ---------------------------------------------------------------------------\n// User inputs live in \"whole tokens\" (e.g. \"1.5 USDC\"). The Aptos ts-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// ---------------------------------------------------------------------------\n// Payload builders\n// ---------------------------------------------------------------------------\n// These return the `InputEntryFunctionData` shape consumed by\n// `wallet.signAndSubmitTransaction({ sender, data: ... })`. Keeping them in\n// one place means the buy/claim components do not have to repeat the\n// function-name and argument order.\n\nexport const buildDepositPayload = (args: AptosDepositPayload) => ({\n function: APTOS_DEPOSIT_FUNCTION,\n typeArguments: [] as string[],\n functionArguments: [args.market, args.amount, args.max_price, args.depositor] as (string | number)[],\n})\n\nexport const buildClaimPayload = (args: AptosClaimPayload) => ({\n function: APTOS_CLAIM_FUNCTION,\n typeArguments: [] as string[],\n // `apebond::bond::claim(market, bill_id: u64)` — the contract takes the\n // scalar bill_id, NOT the object address.\n functionArguments: [args.market, args.bill_id] as (string | number)[],\n})\n\n// ---------------------------------------------------------------------------\n// Event extractors\n// ---------------------------------------------------------------------------\n// Parse events out of a `waitForTransaction` response. The event `type`\n// format is `${module}::${EventStruct}`. We match by `.endsWith('::Foo')` so\n// we do not have to hard-code the full module path (which contains the\n// contract address).\n\ninterface AptosTxnWithEvents {\n events?: Array<{ type: string; data: Record<string, unknown> }>\n}\n\nexport const extractEventData = <T = Record<string, unknown>>(\n txn: AptosTxnWithEvents,\n eventSuffix: string,\n): T | undefined => {\n const event = txn?.events?.find((e) => e.type.includes(eventSuffix))\n return event?.data as T | undefined\n}\n\n/**\n * `bond::DepositEvent` — emitted by the deposit entry function.\n * Contains bill_id, bill_address, deposit_amount, payout_amount, price.\n */\nexport const extractDepositEvent = (\n txn: AptosTxnWithEvents,\n):\n | {\n bill_id: string\n bill_address: string\n market: string\n deposit_amount: string\n payout_amount: string\n price: string\n depositor: string\n recipient: string\n }\n | undefined => extractEventData(txn, '::DepositEvent')\n\n/**\n * Extract bill_id from transaction events. Checks both `BillMinted` (from bond_nft)\n * and `DepositEvent` (from bond) since the contract emits both.\n */\nexport const extractBillMinted = (txn: AptosTxnWithEvents): { bill_id: string; owner: string } | undefined => {\n // Try BillMinted event first (emitted by bond_nft module)\n const billMinted = extractEventData<{ bill_id: string; owner: string }>(txn, '::BillMinted')\n if (billMinted) return billMinted\n // Fall back to DepositEvent (emitted by bond module)\n const deposit = extractDepositEvent(txn)\n if (!deposit) return undefined\n return { bill_id: deposit.bill_id, owner: deposit.depositor }\n}\n\n// ---------------------------------------------------------------------------\n// Fetch helper — GraphQL to the Aptos indexer\n// ---------------------------------------------------------------------------\n// Uses the same API key that is configured on the Aptos client. Everything\n// else on the HTTP layer goes through the Aptos ts-sdk, but the indexer\n// needs a raw `fetch` call because GraphQL is not part of the SDK surface.\nexport const aptosIndexerFetch = async <T = unknown>(\n url: string,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> => {\n const apiKey = getRandomAptosApiKey()\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey && { Authorization: `Bearer ${apiKey}` }),\n },\n body: JSON.stringify({ query, variables }),\n })\n if (!response.ok) {\n throw new Error(`Aptos indexer request failed: ${response.status}`)\n }\n const json = (await response.json()) as { data: T; errors?: Array<{ message: string }> }\n if (json.errors?.length) {\n throw new Error(`Aptos indexer returned errors: ${json.errors.map((e) => e.message).join(', ')}`)\n }\n return json.data\n}\n\n// ---------------------------------------------------------------------------\n// View function readers (PLAN_001 Etapa 5b — real read skeleton)\n// ---------------------------------------------------------------------------\n// Thin wrappers around `aptos.view(...)` for the specific view functions the\n// SDK needs. Every reader throws on RPC / contract error — callers should\n// wrap in try/catch so a missing deployment degrades gracefully rather than\n// crashing the UI.\n//\n// Shapes are verified against apebond-move-playground pages. See the matching\n// `AptosMarketInfoTuple` / `AptosBillInfo` / `AptosVestingProgressTuple`\n// types for documentation on each field.\n\n/**\n * `bond::market_info(market)` — returns the 27-element market info tuple.\n */\nexport const readAptosMarketInfo = async (marketAddress: string): Promise<AptosMarketInfoTuple> => {\n const aptos = getAptosClient()\n const result = await aptos.view({\n payload: {\n function: APTOS_VIEW_MARKET_INFO,\n functionArguments: [marketAddress],\n },\n })\n return result as unknown as AptosMarketInfoTuple\n}\n\n/**\n * `bond::bill_info(market, bill_id)` — returns the Bill struct for a\n * specific bill. The contract takes `bill_id: u64` (NOT the object address).\n * Uses REST directly to avoid SDK remoteAbi serialisation bug with u64 args.\n */\nexport const readAptosBillInfo = async (marketAddress: string, billId: string): Promise<AptosBillInfo> => {\n const result = await viewViaRest(APTOS_VIEW_BILL_INFO, [marketAddress, billId])\n return result[0] as unknown as AptosBillInfo\n}\n\n/**\n * `bond_nft::user_bills(user)` — returns all bill_ids owned by the user\n * across every deployed market. The view returns `[bill_ids]` (the inner\n * array is at index 0), so we unwrap it here.\n */\nexport const readUserBillIds = async (user: string): Promise<string[]> => {\n const aptos = getAptosClient()\n const result = await aptos.view({\n payload: {\n function: APTOS_VIEW_USER_BILLS,\n functionArguments: [user],\n },\n })\n const ids = (result[0] ?? []) as unknown as Array<string | number>\n return ids.map((id) => String(id))\n}\n\n/**\n * `bond::vesting_progress(market, bill_id)` — returns `[vested_amount, claimable_amount]`.\n * Note: this is in `bond` module (not `bond_nft`), and takes the market as first argument.\n * Uses REST directly to avoid SDK remoteAbi serialisation bug with u64 args.\n */\nexport const readVestingProgress = async (\n marketAddress: string,\n billId: string,\n): Promise<AptosVestingProgressTuple> => {\n const result = await viewViaRest(APTOS_VIEW_VESTING_PROGRESS, [marketAddress, billId])\n return [String(result[0] ?? '0'), String(result[1] ?? '0')] as const\n}\n\n// ---------------------------------------------------------------------------\n// REST-based view call — bypasses the SDK's remoteAbi serialisation which\n// has a bug in v6.x where u64 arguments to functions returning `address` get\n// mis-serialised. The REST endpoint handles string arguments natively.\n// ---------------------------------------------------------------------------\nconst viewViaRest = async (fn: string, args: string[], typeArgs: string[] = []): Promise<unknown[]> => {\n const apiKey = getRandomAptosApiKey()\n const res = await fetch(`${APTOS_FULLNODE_URL}/view`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey && { Authorization: `Bearer ${apiKey}` }),\n },\n body: JSON.stringify({\n function: fn,\n type_arguments: typeArgs,\n arguments: args,\n }),\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`View call ${fn} failed: ${res.status} ${text}`)\n }\n return (await res.json()) as unknown[]\n}\n\n/**\n * `bond_nft::bond_market_by_id(bill_id)` — which market owns this bill.\n * Uses REST directly — SDK's aptos.view() mis-serialises u64→address functions.\n */\nexport const readBondMarketByBillId = async (billId: string): Promise<string> => {\n const result = await viewViaRest(APTOS_VIEW_BOND_MARKET_BY_ID, [billId])\n return String(result[0] ?? '')\n}\n\n/**\n * `bond_nft::nft_address(bill_id)` — resolve the scalar bill_id to the\n * on-chain NFT object address (aka `bill_addr`). Needed to then call\n * `bond::bill_info(market, bill_addr)` or `bond::claim(market, bill_addr)`.\n * Uses REST directly — SDK's aptos.view() mis-serialises u64→address functions.\n */\nexport const readNftAddressByBillId = async (billId: string): Promise<string> => {\n const result = await viewViaRest(APTOS_VIEW_NFT_ADDRESS, [billId])\n return String(result[0] ?? '')\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aptosHelpers.js","sources":["../../src/utils/aptosHelpers.ts"],"sourcesContent":["import { Aptos, AptosConfig } from '@aptos-labs/ts-sdk'\nimport BigNumber from 'bignumber.js'\nimport {\n APTOS_CLAIM_FUNCTION,\n APTOS_DEPOSIT_FUNCTION,\n APTOS_FULLNODE_URL,\n APTOS_NETWORK,\n APTOS_VIEW_BILL_INFO,\n APTOS_VIEW_BOND_MARKET_BY_ID,\n APTOS_VIEW_MARKET_INFO,\n APTOS_VIEW_NFT_ADDRESS,\n APTOS_VIEW_USER_BILLS,\n APTOS_VIEW_VESTING_PROGRESS,\n getRandomAptosApiKey,\n} from '../constants/aptosConstants'\nimport type {\n AptosBillInfo,\n AptosClaimPayload,\n AptosDepositPayload,\n AptosMarketInfoTuple,\n AptosVestingProgressTuple,\n} from '../types/aptosBonds'\n\n// ---------------------------------------------------------------------------\n// Aptos client — lazy singleton\n// ---------------------------------------------------------------------------\n// We keep one client per (network, api key) pair. The API key is picked with\n// `getRandomAptosApiKey()` on first call, so different sessions hit a\n// different key (very simple load balancing). If the pool is empty we fall\n// back to unauthenticated requests.\nlet _aptosClient: Aptos | undefined\n\nexport const getAptosClient = (): Aptos => {\n if (_aptosClient) return _aptosClient\n const apiKey = getRandomAptosApiKey()\n // The Aptos ts-sdk exposes `API_KEY` directly on `clientConfig`; it adds\n // the `Authorization: Bearer <key>` header internally. This is the\n // idiomatic way to authenticate — no manual header wiring needed.\n const config = new AptosConfig({\n network: APTOS_NETWORK,\n ...(apiKey && { clientConfig: { API_KEY: apiKey } }),\n })\n _aptosClient = new Aptos(config)\n return _aptosClient\n}\n\n// ---------------------------------------------------------------------------\n// Address normalisation\n// ---------------------------------------------------------------------------\n// Aptos wallet adapters can surface account addresses in several shapes:\n// plain string, `{ data: Uint8Array }`, `Uint8Array`, or an `AccountAddress`\n// instance. This helper returns a canonical `0x`-prefixed hex string, or\n// `undefined` if the input cannot be decoded. We keep the interface loose so\n// callers can pass whatever the SDK hands them.\nexport const safeExtractAddress = (input: unknown): string | undefined => {\n if (input == null) return undefined\n\n // Already a string\n if (typeof input === 'string') {\n return input.startsWith('0x') ? input : `0x${input}`\n }\n\n // AccountAddress instance (has `toString()` that returns `0x...`)\n if (typeof input === 'object' && typeof (input as { toString?: () => string }).toString === 'function') {\n const asObject = input as { data?: Uint8Array | number[]; toString: () => string }\n // Prefer `.data` if it looks like a byte array — `.toString()` on\n // AccountAddress already returns the canonical hex, but some wallets\n // return `{ data: Uint8Array }` without the method.\n if (asObject.data instanceof Uint8Array) {\n return bytesToHex(asObject.data)\n }\n if (Array.isArray(asObject.data)) {\n return bytesToHex(new Uint8Array(asObject.data))\n }\n const stringified = asObject.toString()\n if (stringified.startsWith('0x')) return stringified\n }\n\n // Raw Uint8Array\n if (input instanceof Uint8Array) {\n return bytesToHex(input)\n }\n\n // Raw number[]\n if (Array.isArray(input)) {\n return bytesToHex(new Uint8Array(input))\n }\n\n return undefined\n}\n\nconst bytesToHex = (bytes: Uint8Array): string => {\n let out = '0x'\n for (let i = 0; i < bytes.length; i++) {\n out += bytes[i].toString(16).padStart(2, '0')\n }\n return out\n}\n\n// ---------------------------------------------------------------------------\n// Unit conversion\n// ---------------------------------------------------------------------------\n// User inputs live in \"whole tokens\" (e.g. \"1.5 USDC\"). The Aptos ts-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// ---------------------------------------------------------------------------\n// Payload builders\n// ---------------------------------------------------------------------------\n// These return the `InputEntryFunctionData` shape consumed by\n// `wallet.signAndSubmitTransaction({ sender, data: ... })`. Keeping them in\n// one place means the buy/claim components do not have to repeat the\n// function-name and argument order.\n\nexport const buildDepositPayload = (args: AptosDepositPayload) => ({\n function: APTOS_DEPOSIT_FUNCTION,\n typeArguments: [] as string[],\n functionArguments: [args.market, args.amount, args.max_price, args.depositor] as (string | number)[],\n})\n\nexport const buildClaimPayload = (args: AptosClaimPayload) => ({\n function: APTOS_CLAIM_FUNCTION,\n typeArguments: [] as string[],\n // `apebond::bond::claim(market, bill_id: u64)` — the contract takes the\n // scalar bill_id, NOT the object address.\n functionArguments: [args.market, args.bill_id] as (string | number)[],\n})\n\n// ---------------------------------------------------------------------------\n// Event extractors\n// ---------------------------------------------------------------------------\n// Parse events out of a `waitForTransaction` response. The event `type`\n// format is `${module}::${EventStruct}`. We match by `.endsWith('::Foo')` so\n// we do not have to hard-code the full module path (which contains the\n// contract address).\n\ninterface AptosTxnWithEvents {\n events?: Array<{ type: string; data: Record<string, unknown> }>\n}\n\nexport const extractEventData = <T = Record<string, unknown>>(\n txn: AptosTxnWithEvents,\n eventSuffix: string,\n): T | undefined => {\n const event = txn?.events?.find((e) => e.type.includes(eventSuffix))\n return event?.data as T | undefined\n}\n\n/**\n * `bond::DepositEvent` — emitted by the deposit entry function.\n * Contains bill_id, bill_address, deposit_amount, payout_amount, price.\n */\nexport const extractDepositEvent = (\n txn: AptosTxnWithEvents,\n):\n | {\n bill_id: string\n bill_address: string\n market: string\n deposit_amount: string\n payout_amount: string\n price: string\n depositor: string\n recipient: string\n }\n | undefined => extractEventData(txn, '::DepositEvent')\n\n/**\n * Extract bill_id from transaction events. Checks both `BillMinted` (from bond_nft)\n * and `DepositEvent` (from bond) since the contract emits both.\n */\nexport const extractBillMinted = (txn: AptosTxnWithEvents): { bill_id: string; owner: string } | undefined => {\n // Try BillMinted event first (emitted by bond_nft module)\n const billMinted = extractEventData<{ bill_id: string; owner: string }>(txn, '::BillMinted')\n if (billMinted) return billMinted\n // Fall back to DepositEvent (emitted by bond module)\n const deposit = extractDepositEvent(txn)\n if (!deposit) return undefined\n return { bill_id: deposit.bill_id, owner: deposit.depositor }\n}\n\n// ---------------------------------------------------------------------------\n// Fetch helper — GraphQL to the Aptos indexer\n// ---------------------------------------------------------------------------\n// Uses the same API key that is configured on the Aptos client. Everything\n// else on the HTTP layer goes through the Aptos ts-sdk, but the indexer\n// needs a raw `fetch` call because GraphQL is not part of the SDK surface.\nexport const aptosIndexerFetch = async <T = unknown>(\n url: string,\n query: string,\n variables?: Record<string, unknown>,\n): Promise<T> => {\n const apiKey = getRandomAptosApiKey()\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey && { Authorization: `Bearer ${apiKey}` }),\n },\n body: JSON.stringify({ query, variables }),\n })\n if (!response.ok) {\n throw new Error(`Aptos indexer request failed: ${response.status}`)\n }\n const json = (await response.json()) as { data: T; errors?: Array<{ message: string }> }\n if (json.errors?.length) {\n throw new Error(`Aptos indexer returned errors: ${json.errors.map((e) => e.message).join(', ')}`)\n }\n return json.data\n}\n\n// ---------------------------------------------------------------------------\n// View function readers (PLAN_001 Etapa 5b — real read skeleton)\n// ---------------------------------------------------------------------------\n// Thin wrappers around `aptos.view(...)` for the specific view functions the\n// SDK needs. Every reader throws on RPC / contract error — callers should\n// wrap in try/catch so a missing deployment degrades gracefully rather than\n// crashing the UI.\n//\n// Shapes are verified against apebond-move-playground pages. See the matching\n// `AptosMarketInfoTuple` / `AptosBillInfo` / `AptosVestingProgressTuple`\n// types for documentation on each field.\n\n/**\n * `bond::market_info(market)` — returns the 27-element market info tuple.\n */\nexport const readAptosMarketInfo = async (marketAddress: string): Promise<AptosMarketInfoTuple> => {\n const aptos = getAptosClient()\n const result = await aptos.view({\n payload: {\n function: APTOS_VIEW_MARKET_INFO,\n functionArguments: [marketAddress],\n },\n })\n return result as unknown as AptosMarketInfoTuple\n}\n\n/**\n * `bond::bill_info(market, bill_id)` — returns the Bill struct for a\n * specific bill. The contract takes `bill_id: u64` (NOT the object address).\n * Uses REST directly to avoid SDK remoteAbi serialisation bug with u64 args.\n */\nexport const readAptosBillInfo = async (marketAddress: string, billId: string): Promise<AptosBillInfo> => {\n const result = await viewViaRest(APTOS_VIEW_BILL_INFO, [marketAddress, billId])\n return result[0] as unknown as AptosBillInfo\n}\n\n/**\n * `bond_nft::user_bills(user)` — returns all bill_ids owned by the user\n * across every deployed market. The view returns `[bill_ids]` (the inner\n * array is at index 0), so we unwrap it here.\n */\nexport const readUserBillIds = async (user: string): Promise<string[]> => {\n const aptos = getAptosClient()\n const result = await aptos.view({\n payload: {\n function: APTOS_VIEW_USER_BILLS,\n functionArguments: [user],\n },\n })\n const ids = (result[0] ?? []) as unknown as Array<string | number>\n return ids.map((id) => String(id))\n}\n\n/**\n * `bond::vesting_progress(market, bill_id)` — returns `[vested_amount, claimable_amount]`.\n * Note: this is in `bond` module (not `bond_nft`), and takes the market as first argument.\n * Uses REST directly to avoid SDK remoteAbi serialisation bug with u64 args.\n */\nexport const readVestingProgress = async (\n marketAddress: string,\n billId: string,\n): Promise<AptosVestingProgressTuple> => {\n const result = await viewViaRest(APTOS_VIEW_VESTING_PROGRESS, [marketAddress, billId])\n return [String(result[0] ?? '0'), String(result[1] ?? '0')] as const\n}\n\n// ---------------------------------------------------------------------------\n// REST-based view call — bypasses the SDK's remoteAbi serialisation which\n// has a bug in v6.x where u64 arguments to functions returning `address` get\n// mis-serialised. The REST endpoint handles string arguments natively.\n// ---------------------------------------------------------------------------\nconst viewViaRest = async (fn: string, args: string[], typeArgs: string[] = []): Promise<unknown[]> => {\n const apiKey = getRandomAptosApiKey()\n const res = await fetch(`${APTOS_FULLNODE_URL}/view`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey && { Authorization: `Bearer ${apiKey}` }),\n },\n body: JSON.stringify({\n function: fn,\n type_arguments: typeArgs,\n arguments: args,\n }),\n })\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`View call ${fn} failed: ${res.status} ${text}`)\n }\n return (await res.json()) as unknown[]\n}\n\n/**\n * `bond_nft::bond_market_by_id(bill_id)` — which market owns this bill.\n * Uses REST directly — SDK's aptos.view() mis-serialises u64→address functions.\n */\nexport const readBondMarketByBillId = async (billId: string): Promise<string> => {\n const result = await viewViaRest(APTOS_VIEW_BOND_MARKET_BY_ID, [billId])\n return String(result[0] ?? '')\n}\n\n/**\n * `bond_nft::nft_address(bill_id)` — resolve the scalar bill_id to the\n * on-chain NFT object address (aka `bill_addr`). Needed to then call\n * `bond::bill_info(market, bill_addr)` or `bond::claim(market, bill_addr)`.\n * Uses REST directly — SDK's aptos.view() mis-serialises u64→address functions.\n */\nexport const readNftAddressByBillId = async (billId: string): Promise<string> => {\n const result = await viewViaRest(APTOS_VIEW_NFT_ADDRESS, [billId])\n return String(result[0] ?? '')\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,23 +2,12 @@ import { jsx } from 'theme-ui/jsx-runtime';
|
|
|
2
2
|
import Modal from '../../components/uikit-sdk/Modal/index.js';
|
|
3
3
|
import BuyComponent from './BuyComponent.js';
|
|
4
4
|
import BuyComponentSolana from './BuyComponentSolana.js';
|
|
5
|
+
import BuyComponentAptos from './BuyComponentAptos.js';
|
|
6
|
+
import BuyComponentSui from './BuyComponentSui.js';
|
|
5
7
|
import { ChainId } from '@ape.swap/apeswap-lists';
|
|
6
8
|
|
|
7
9
|
const BuyBondModal = ({ onDismiss, bondAddress, bondChain, handlePurchasedBond, setBillId, }) => {
|
|
8
|
-
return (jsx(Modal, { className: "modal", onDismiss: onDismiss, sx: { width: '400px', maxWidth: '700px', mt: '-10px', p: '0px' }, children: bondChain === ChainId.SOL ? (jsx(BuyComponentSolana, { onDismiss: onDismiss, bondAddress: bondAddress, bondChain: bondChain, setBillId: setBillId })) : (
|
|
9
|
-
// TODO: uncomment this
|
|
10
|
-
//
|
|
11
|
-
// : bondChain === ChainId.APTOS ? (
|
|
12
|
-
// <BuyComponentAptos
|
|
13
|
-
// onDismiss={onDismiss}
|
|
14
|
-
// bondAddress={bondAddress}
|
|
15
|
-
// bondChain={bondChain}
|
|
16
|
-
// setBillId={setBillId}
|
|
17
|
-
// />
|
|
18
|
-
// ) : bondChain === ChainId.SUI ? (
|
|
19
|
-
// <BuyComponentSui onDismiss={onDismiss} bondAddress={bondAddress} bondChain={bondChain} setBillId={setBillId} />
|
|
20
|
-
// )
|
|
21
|
-
jsx(BuyComponent //evm
|
|
10
|
+
return (jsx(Modal, { className: "modal", onDismiss: onDismiss, sx: { width: '400px', maxWidth: '700px', mt: '-10px', p: '0px' }, children: bondChain === ChainId.SOL ? (jsx(BuyComponentSolana, { onDismiss: onDismiss, bondAddress: bondAddress, bondChain: bondChain, setBillId: setBillId })) : bondChain === ChainId.APTOS ? (jsx(BuyComponentAptos, { onDismiss: onDismiss, bondAddress: bondAddress, bondChain: bondChain, setBillId: setBillId })) : bondChain === ChainId.SUI ? (jsx(BuyComponentSui, { onDismiss: onDismiss, bondAddress: bondAddress, bondChain: bondChain, setBillId: setBillId })) : (jsx(BuyComponent //evm
|
|
22
11
|
, { onDismiss: onDismiss, bondAddress: bondAddress, bondChain: bondChain, handlePurchasedBond: handlePurchasedBond })) }));
|
|
23
12
|
};
|
|
24
13
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuyBondModal.js","sources":["../../../src/views/BuyBond/BuyBondModal.tsx"],"sourcesContent":["import React from 'react'\nimport Modal from '../../components/uikit-sdk/Modal'\nimport BuyBond from './BuyComponent'\nimport BuyComponentSolana from './BuyComponentSolana'\
|
|
1
|
+
{"version":3,"file":"BuyBondModal.js","sources":["../../../src/views/BuyBond/BuyBondModal.tsx"],"sourcesContent":["import React from 'react'\nimport Modal from '../../components/uikit-sdk/Modal'\nimport BuyBond from './BuyComponent'\nimport BuyComponentSolana from './BuyComponentSolana'\nimport BuyComponentAptos from './BuyComponentAptos'\nimport BuyComponentSui from './BuyComponentSui'\nimport { ChainId } from '@ape.swap/apeswap-lists'\n\nexport interface PurchasedBondData {\n buyTxHash: string\n lostProfit?: string\n}\n\nexport interface BuyBondProps {\n onDismiss?: () => void // this is only the modal\n bondAddress?: string\n bondChain?: number\n handlePurchasedBond?: (data: PurchasedBondData) => void\n setBillId?: (id: string) => void\n isProjectView?: boolean\n}\n\nconst BuyBondModal: React.FC<BuyBondProps> = ({\n onDismiss,\n bondAddress,\n bondChain,\n handlePurchasedBond,\n setBillId,\n}) => {\n return (\n <Modal className=\"modal\" onDismiss={onDismiss} sx={{ width: '400px', maxWidth: '700px', mt: '-10px', p: '0px' }}>\n {bondChain === ChainId.SOL ? (\n <BuyComponentSolana\n onDismiss={onDismiss}\n bondAddress={bondAddress}\n bondChain={bondChain}\n setBillId={setBillId}\n />\n ) : bondChain === ChainId.APTOS ? (\n <BuyComponentAptos\n onDismiss={onDismiss}\n bondAddress={bondAddress}\n bondChain={bondChain}\n setBillId={setBillId}\n />\n ) : bondChain === ChainId.SUI ? (\n <BuyComponentSui onDismiss={onDismiss} bondAddress={bondAddress} bondChain={bondChain} setBillId={setBillId} />\n ) : (\n <BuyBond //evm\n onDismiss={onDismiss}\n bondAddress={bondAddress}\n bondChain={bondChain}\n handlePurchasedBond={handlePurchasedBond}\n />\n )}\n </Modal>\n )\n}\n\nexport default BuyBondModal\n"],"names":["_jsx","BuyBond"],"mappings":";;;;;;;;AAsBA,MAAM,YAAY,GAA2B,CAAC,EAC5C,SAAS,EACT,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,SAAS,GACV,KAAI;AACH,IAAA,QACEA,GAAA,CAAC,KAAK,EAAA,EAAC,SAAS,EAAC,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAA,QAAA,EAC5G,SAAS,KAAK,OAAO,CAAC,GAAG,IACxBA,GAAA,CAAC,kBAAkB,EAAA,EACjB,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EAAA,CACpB,IACA,SAAS,KAAK,OAAO,CAAC,KAAK,IAC7BA,GAAA,CAAC,iBAAiB,EAAA,EAChB,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,SAAS,EAAA,CACpB,IACA,SAAS,KAAK,OAAO,CAAC,GAAG,IAC3BA,GAAA,CAAC,eAAe,EAAA,EAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAA,CAAI,KAE/GA,GAAA,CAACC,YAAO;AACN,UAAA,EAAA,SAAS,EAAE,SAAS,EACpB,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,SAAS,EACpB,mBAAmB,EAAE,mBAAmB,GACxC,CACH,EAAA,CACK;AAEZ;;;;"}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'theme-ui/jsx-runtime';
|
|
2
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import BigNumber from 'bignumber.js';
|
|
4
|
+
import { ChainId } from '@ape.swap/apeswap-lists';
|
|
5
|
+
import useBondsData from '../../state/bonds/useBondsData.js';
|
|
6
|
+
import { useSDKConfig } from '../../state/useSDKConfig.js';
|
|
7
|
+
import { usePopups } from '../../state/popups/usePopups.js';
|
|
8
|
+
import { useSlippage } from '../../state/slippage/useSlippage.js';
|
|
9
|
+
import { formatUSDNumber, getMaxBuy, getPayoutAmount, getPrincipalAmount } from '../../utils/displayHelpers.js';
|
|
10
|
+
import { formatNumberSI } from '../../utils/formatNumber.js';
|
|
11
|
+
import { findHighestTrueBondPrice } from '../../utils/bondPriceHelpers.js';
|
|
12
|
+
import { reportError } from '../../utils/reportError.js';
|
|
13
|
+
import { MCBuyComponentStyles } from '../../utils/campaignStyles.js';
|
|
14
|
+
import track from '../../utils/track.js';
|
|
15
|
+
import { remove0xPrefix } from '../../utils/remove0xPrefix.js';
|
|
16
|
+
import { getSymbol } from '../../utils/getNativeTicker.js';
|
|
17
|
+
import useAPTAccount from '../../hooks/accounts/useAPTAccount.js';
|
|
18
|
+
import useTokenFromZapList from '../../hooks/useTokenFromZapList.js';
|
|
19
|
+
import useSortedZapList from '../../hooks/useSortedZapList.js';
|
|
20
|
+
import useAptosZapQuote from '../../state/zap/useAptosZapQuote.js';
|
|
21
|
+
import { findAptosZapToken } from '../../config/constants/aptosZapTokens.js';
|
|
22
|
+
import ConnectAptosWalletButton from '../../components/ConnectAptosWalletButton/ConnectAptosWalletButton.js';
|
|
23
|
+
import TokenSelectorPanel from '../../components/TokenSelectorPanel/index.js';
|
|
24
|
+
import BondModalHeader from './components/BondModalHeader.js';
|
|
25
|
+
import BondCards from './components/BondCards/BondCards.js';
|
|
26
|
+
import Estimations from './components/Estimations.js';
|
|
27
|
+
import ProjectDescription from './components/ProjectDescription.js';
|
|
28
|
+
import Flex from '../../components/uikit-sdk/Flex/index.js';
|
|
29
|
+
import Text from '../../components/uikit-sdk/Text/index.js';
|
|
30
|
+
import Button from '../../components/uikit-sdk/Button/Button.js';
|
|
31
|
+
import SafeHTMLComponent from '../../components/SafeHTMLComponent/index.js';
|
|
32
|
+
import { APTOS_DEPOSIT_FUNCTION } from '../../constants/aptosConstants.js';
|
|
33
|
+
import { getAptosClient, readAptosMarketInfo, extractDepositEvent, extractBillMinted } from '../../utils/aptosHelpers.js';
|
|
34
|
+
|
|
35
|
+
const BuyComponentAptos = ({ onDismiss, bondAddress, bondChain, isProjectView, setBillId, }) => {
|
|
36
|
+
const SDKConfig = useSDKConfig();
|
|
37
|
+
// Hooks
|
|
38
|
+
const { address: aptosAccount, signAndSubmitTransaction } = useAPTAccount();
|
|
39
|
+
const { data: bonds } = useBondsData();
|
|
40
|
+
const { addToastError, addToastSuccess } = usePopups();
|
|
41
|
+
const { solSlippage: slippage } = useSlippage(); // reuse Sol slippage bucket for Aptos
|
|
42
|
+
const bondData = bonds?.find((bond) => bond?.contractAddress?.[bond?.chainId]?.toLowerCase() === bondAddress?.toLowerCase());
|
|
43
|
+
// Derived pricing (same path the Solana no-tier branch uses).
|
|
44
|
+
const trueBondPriceData = findHighestTrueBondPrice('0', bondData?.trueBondPrices);
|
|
45
|
+
// State
|
|
46
|
+
const [inputValue, setInputValue] = useState('');
|
|
47
|
+
const [inputTokenString, setInputTokenString] = useState(bondData?.lpToken?.address?.[bondData?.chainId]);
|
|
48
|
+
const [loadingTx, setLoadingTx] = useState(false);
|
|
49
|
+
// Token selection — reuses the same infrastructure as Solana
|
|
50
|
+
const inputToken = useTokenFromZapList(inputTokenString, bondData?.chainId, bondData?.lpToken);
|
|
51
|
+
const principalAddress = bondData?.lpToken?.address?.[bondData?.chainId];
|
|
52
|
+
// APT native ('NATIVE') and APT metadata ('0xa') are the same token — not a zap
|
|
53
|
+
const isNativeMatchingPrincipal = inputTokenString === 'NATIVE' && principalAddress?.toLowerCase() === '0xa';
|
|
54
|
+
const isZap = !!inputTokenString &&
|
|
55
|
+
!!principalAddress &&
|
|
56
|
+
!isNativeMatchingPrincipal &&
|
|
57
|
+
inputTokenString.toLowerCase() !== principalAddress.toLowerCase();
|
|
58
|
+
// Zap quote from Panora DEX aggregator
|
|
59
|
+
const aptosZapToken = findAptosZapToken(inputToken && inputToken !== 'NATIVE'
|
|
60
|
+
? inputToken?.address?.[ChainId.APTOS]
|
|
61
|
+
: inputToken === 'NATIVE'
|
|
62
|
+
? '0xa'
|
|
63
|
+
: undefined);
|
|
64
|
+
const [fetchingZapQuote, panoraQuote, zapDepositAmount, zapError] = useAptosZapQuote(inputValue, aptosZapToken, bondData, aptosAccount, slippage);
|
|
65
|
+
// Sorted zap list (balances + prices)
|
|
66
|
+
const { sortedZapList, isFetched: zapListFetched } = useSortedZapList(bondChain, bondData?.lpToken);
|
|
67
|
+
const enrichedTokenData = sortedZapList.find((item) => item.token === 'NATIVE' || inputToken === 'NATIVE'
|
|
68
|
+
? item.token === inputToken
|
|
69
|
+
: item?.token?.address?.[bondChain]?.toLowerCase() ===
|
|
70
|
+
inputToken?.address?.[bondChain]?.toLowerCase());
|
|
71
|
+
const inputCurrencyBalance = enrichedTokenData?.balance;
|
|
72
|
+
const inputTokenPrice = enrichedTokenData?.price;
|
|
73
|
+
// Estimations — use zapDepositAmount if swapping
|
|
74
|
+
const depositAmount = isZap ? zapDepositAmount : inputValue;
|
|
75
|
+
const youSpendString = `${formatNumberSI(inputValue)} ${getSymbol(inputToken, bondData?.chainId)} = $${formatUSDNumber((parseFloat(inputValue || '0') * (inputTokenPrice ?? 0)).toString())}`;
|
|
76
|
+
const exceedsRealMaxBuy = getMaxBuy(bondData, true).lte(getPayoutAmount(bondData, depositAmount, '0'));
|
|
77
|
+
const exceedsSafeMaxBuy = getMaxBuy(bondData, false)
|
|
78
|
+
.times(isZap ? 0.995 : 0.9995)
|
|
79
|
+
.lte(getPayoutAmount(bondData, depositAmount, '0'));
|
|
80
|
+
const exceedsBalance = new BigNumber(inputCurrencyBalance ?? '0').lt(inputValue);
|
|
81
|
+
const load = loadingTx || (fetchingZapQuote && !!inputValue);
|
|
82
|
+
// Handlers
|
|
83
|
+
const handleCurrencySelect = useCallback((newInputToken) => {
|
|
84
|
+
setInputValue('');
|
|
85
|
+
setInputTokenString(newInputToken);
|
|
86
|
+
}, []);
|
|
87
|
+
const handleMaxButton = () => {
|
|
88
|
+
const balance = new BigNumber(inputCurrencyBalance ?? '0');
|
|
89
|
+
const principalDecimals = bondData?.lpToken?.decimals?.[bondData?.chainId] ?? 6;
|
|
90
|
+
const maxBuyAmount = getMaxBuy(bondData, SDKConfig.showLowValueBonds);
|
|
91
|
+
if (!isZap) {
|
|
92
|
+
const maxPossibleUserPurchase = getPayoutAmount(bondData, balance.toString(), '0');
|
|
93
|
+
if (maxPossibleUserPurchase > maxBuyAmount.toNumber()) {
|
|
94
|
+
const principalForMaxBuy = getPrincipalAmount(bondData, maxBuyAmount.toString(), '0');
|
|
95
|
+
setInputValue(new BigNumber(principalForMaxBuy).toFixed(principalDecimals));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
setInputValue(balance.toFixed(principalDecimals));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const principalForMaxBuy = getPrincipalAmount(bondData, maxBuyAmount.toString(), '0');
|
|
103
|
+
const principalForMaxBuyUSD = new BigNumber(principalForMaxBuy).times(bondData?.principalTokenPrice ?? 0);
|
|
104
|
+
const maxBuyAmountInSelectedToken = principalForMaxBuyUSD.div(inputTokenPrice ?? 0);
|
|
105
|
+
const decimals = enrichedTokenData?.token === 'NATIVE' ? 8 : (enrichedTokenData?.token.decimals[bondData?.chainId] ?? 8);
|
|
106
|
+
if (balance.gte(maxBuyAmountInSelectedToken)) {
|
|
107
|
+
setInputValue(maxBuyAmountInSelectedToken.toFixed(decimals));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
setInputValue(balance.toFixed(decimals));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const handleBuyCallback = async () => {
|
|
115
|
+
if (!aptosAccount || !bondData || !inputValue)
|
|
116
|
+
return;
|
|
117
|
+
track({
|
|
118
|
+
event: 'buyBondClick',
|
|
119
|
+
chain: bondChain,
|
|
120
|
+
data: {
|
|
121
|
+
cat: bondData?.billType,
|
|
122
|
+
bond: bondData?.earnToken.symbol,
|
|
123
|
+
value: parseFloat(inputValue) * (inputTokenPrice ?? 0),
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
try {
|
|
127
|
+
setLoadingTx(true);
|
|
128
|
+
const principalDecimals = bondData.lpToken.decimals[bondData.chainId] ?? 6;
|
|
129
|
+
const atomicAmount = new BigNumber(inputValue)
|
|
130
|
+
.times(new BigNumber(10).pow(principalDecimals))
|
|
131
|
+
.integerValue(BigNumber.ROUND_FLOOR)
|
|
132
|
+
.toFixed(0);
|
|
133
|
+
const market = bondData.contractAddress[bondData.chainId] ?? '';
|
|
134
|
+
// Read fresh pricing from the contract
|
|
135
|
+
const marketInfo = await readAptosMarketInfo(market);
|
|
136
|
+
const trueBillPriceU64 = marketInfo[17]; // u64 scaled 1e8
|
|
137
|
+
const atomicMaxPrice = new BigNumber(trueBillPriceU64)
|
|
138
|
+
.times(new BigNumber(100 + slippage))
|
|
139
|
+
.dividedBy(new BigNumber(100))
|
|
140
|
+
.integerValue(BigNumber.ROUND_CEIL)
|
|
141
|
+
.toFixed(0);
|
|
142
|
+
const response = await signAndSubmitTransaction?.({
|
|
143
|
+
data: {
|
|
144
|
+
function: APTOS_DEPOSIT_FUNCTION,
|
|
145
|
+
typeArguments: [],
|
|
146
|
+
functionArguments: [market, atomicAmount, atomicMaxPrice, aptosAccount],
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
if (!response?.hash)
|
|
150
|
+
throw new Error('No tx hash returned by wallet');
|
|
151
|
+
const aptos = getAptosClient();
|
|
152
|
+
await aptos.waitForTransaction({ transactionHash: response.hash });
|
|
153
|
+
// waitForTransaction may not include events — fetch full tx details separately
|
|
154
|
+
const txDetails = await aptos.getTransactionByHash({ transactionHash: response.hash });
|
|
155
|
+
const txnWithEvents = txDetails;
|
|
156
|
+
const depositEvent = extractDepositEvent(txnWithEvents);
|
|
157
|
+
const newBillId = depositEvent?.bill_id ?? extractBillMinted(txnWithEvents)?.bill_id ?? '';
|
|
158
|
+
track({
|
|
159
|
+
event: 'bond',
|
|
160
|
+
chain: bondChain,
|
|
161
|
+
data: {
|
|
162
|
+
cat: 'lp-buy',
|
|
163
|
+
type: bondData?.billType ?? '',
|
|
164
|
+
typedValue: inputValue,
|
|
165
|
+
principalToken: bondData?.lpToken.symbol ?? '',
|
|
166
|
+
earnToken: bondData?.earnToken.symbol ?? '',
|
|
167
|
+
address: remove0xPrefix(market),
|
|
168
|
+
usdAmount: parseFloat(inputValue) * (inputTokenPrice ?? 0),
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
addToastSuccess(response.hash, bondChain);
|
|
172
|
+
setBillId?.(newBillId);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
console.error('Error during Aptos bond purchase:', error);
|
|
176
|
+
addToastError(error?.message || 'Unknown error');
|
|
177
|
+
reportError({
|
|
178
|
+
apiUrl: SDKConfig?.urls?.apiV2,
|
|
179
|
+
error: error?.message,
|
|
180
|
+
extraInfo: { type: 'aptosBuyBond', error: error?.message },
|
|
181
|
+
chainId: bondChain,
|
|
182
|
+
account: aptosAccount,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
setLoadingTx(false);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const handleZapCallback = async () => {
|
|
190
|
+
if (loadingTx || !aptosAccount || !bondData || !panoraQuote || !signAndSubmitTransaction)
|
|
191
|
+
return;
|
|
192
|
+
track({
|
|
193
|
+
event: 'buyBondClick',
|
|
194
|
+
chain: bondChain,
|
|
195
|
+
data: {
|
|
196
|
+
cat: bondData?.billType,
|
|
197
|
+
bond: bondData?.earnToken.symbol,
|
|
198
|
+
value: parseFloat(inputValue) * (inputTokenPrice ?? 0),
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
try {
|
|
202
|
+
setLoadingTx(true);
|
|
203
|
+
const aptos = getAptosClient();
|
|
204
|
+
// TX1: Execute Panora swap — the quote's txData contains the entry
|
|
205
|
+
// function payload ready for the wallet adapter.
|
|
206
|
+
const swapTxData = panoraQuote.txData;
|
|
207
|
+
if (!swapTxData || !swapTxData.function) {
|
|
208
|
+
throw new Error('Panora quote missing txData');
|
|
209
|
+
}
|
|
210
|
+
const swapResponse = await signAndSubmitTransaction({
|
|
211
|
+
data: swapTxData,
|
|
212
|
+
});
|
|
213
|
+
if (!swapResponse?.hash)
|
|
214
|
+
throw new Error('No tx hash returned for swap');
|
|
215
|
+
await aptos.waitForTransaction({ transactionHash: swapResponse.hash });
|
|
216
|
+
// TX2: Deposit into bond with the swapped principal tokens.
|
|
217
|
+
// Use the deposit amount from the quote (what we received from the swap).
|
|
218
|
+
const principalDecimals = bondData.lpToken.decimals[bondData.chainId] ?? 6;
|
|
219
|
+
const depositAmountAtomic = new BigNumber(zapDepositAmount)
|
|
220
|
+
.times(new BigNumber(10).pow(principalDecimals))
|
|
221
|
+
.integerValue(BigNumber.ROUND_FLOOR)
|
|
222
|
+
.toFixed(0);
|
|
223
|
+
const market = bondData.contractAddress[bondData.chainId] ?? '';
|
|
224
|
+
// Read fresh pricing
|
|
225
|
+
const marketInfo = await readAptosMarketInfo(market);
|
|
226
|
+
const trueBillPriceU64 = marketInfo[17];
|
|
227
|
+
const atomicMaxPrice = new BigNumber(trueBillPriceU64)
|
|
228
|
+
.times(new BigNumber(100 + slippage))
|
|
229
|
+
.dividedBy(new BigNumber(100))
|
|
230
|
+
.integerValue(BigNumber.ROUND_CEIL)
|
|
231
|
+
.toFixed(0);
|
|
232
|
+
const depositResponse = await signAndSubmitTransaction({
|
|
233
|
+
data: {
|
|
234
|
+
function: APTOS_DEPOSIT_FUNCTION,
|
|
235
|
+
typeArguments: [],
|
|
236
|
+
functionArguments: [market, depositAmountAtomic, atomicMaxPrice, aptosAccount],
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
if (!depositResponse?.hash)
|
|
240
|
+
throw new Error('No tx hash returned for deposit');
|
|
241
|
+
await aptos.waitForTransaction({ transactionHash: depositResponse.hash });
|
|
242
|
+
const txDetails = await aptos.getTransactionByHash({ transactionHash: depositResponse.hash });
|
|
243
|
+
const txnWithEvents = txDetails;
|
|
244
|
+
const depositEvent = extractDepositEvent(txnWithEvents);
|
|
245
|
+
const newBillId = depositEvent?.bill_id ?? extractBillMinted(txnWithEvents)?.bill_id ?? '';
|
|
246
|
+
// Track zap events
|
|
247
|
+
track({
|
|
248
|
+
event: 'zap',
|
|
249
|
+
chain: bondChain,
|
|
250
|
+
data: {
|
|
251
|
+
cat: 'bond',
|
|
252
|
+
token1: getSymbol(inputToken),
|
|
253
|
+
token2: bondData?.lpToken.symbol ?? '',
|
|
254
|
+
amount: parseFloat(inputValue) * (inputTokenPrice ?? 0),
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
track({
|
|
258
|
+
event: 'bond',
|
|
259
|
+
chain: bondChain,
|
|
260
|
+
data: {
|
|
261
|
+
cat: 'zap-buy-two-tx',
|
|
262
|
+
type: bondData?.billType ?? '',
|
|
263
|
+
typedValue: inputValue,
|
|
264
|
+
principalToken: bondData?.lpToken.symbol ?? '',
|
|
265
|
+
earnToken: bondData?.earnToken.symbol ?? '',
|
|
266
|
+
address: remove0xPrefix(market),
|
|
267
|
+
usdAmount: parseFloat(inputValue) * (inputTokenPrice ?? 0),
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
addToastSuccess(depositResponse.hash, bondChain);
|
|
271
|
+
setBillId?.(newBillId);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
console.error('Error during Aptos zap + bond purchase:', error);
|
|
275
|
+
addToastError(error?.message || 'Unknown error during swap and bond');
|
|
276
|
+
reportError({
|
|
277
|
+
apiUrl: SDKConfig?.urls?.apiV2,
|
|
278
|
+
error: error?.message,
|
|
279
|
+
extraInfo: { type: 'aptosPanoraZapBuyBond', error: error?.message, quote: panoraQuote },
|
|
280
|
+
chainId: bondChain,
|
|
281
|
+
account: aptosAccount,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
setLoadingTx(false);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
const handleBuy = () => {
|
|
289
|
+
if (loadingTx)
|
|
290
|
+
return;
|
|
291
|
+
if (isZap) {
|
|
292
|
+
handleZapCallback();
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
handleBuyCallback();
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
// Sync inputTokenString when bondData loads after initial render
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
const addr = bondData?.lpToken?.address?.[bondData?.chainId];
|
|
301
|
+
if (addr && !inputTokenString) {
|
|
302
|
+
setInputTokenString(addr);
|
|
303
|
+
}
|
|
304
|
+
}, [bondData?.lpToken?.address, bondData?.chainId]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
305
|
+
// Auto-select highest balance token when zap list loads
|
|
306
|
+
const [hasChecked, setHasChecked] = useState(false);
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
if (zapListFetched && !hasChecked) {
|
|
309
|
+
if (parseFloat(inputCurrencyBalance ?? '0') < 0.0001) {
|
|
310
|
+
setInputTokenString(sortedZapList[0]?.token === 'NATIVE' ? 'NATIVE' : sortedZapList[0]?.token.address[bondChain]);
|
|
311
|
+
}
|
|
312
|
+
setHasChecked(true);
|
|
313
|
+
}
|
|
314
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
315
|
+
}, [inputCurrencyBalance, zapListFetched]);
|
|
316
|
+
const hasPositiveBonus = (trueBondPriceData?.bonusWithFee ?? 0) > 0;
|
|
317
|
+
const canBuy = !load &&
|
|
318
|
+
!!aptosAccount &&
|
|
319
|
+
!bondData?.soldOut &&
|
|
320
|
+
!!inputValue &&
|
|
321
|
+
parseFloat(inputValue) > 0 &&
|
|
322
|
+
!exceedsRealMaxBuy &&
|
|
323
|
+
!exceedsBalance &&
|
|
324
|
+
hasPositiveBonus &&
|
|
325
|
+
!SDKConfig.blockSales &&
|
|
326
|
+
!zapError &&
|
|
327
|
+
!(isZap && !panoraQuote);
|
|
328
|
+
return bondData ? (jsx(Flex, { className: "modal-content", sx: { ...MCBuyComponentStyles[bondData?.marketingCampaign] }, children: jsxs(Flex, { className: "modaltable-container", children: [jsx(BondModalHeader, { bondData: bondData, onDismiss: onDismiss, hidePromotionUi: true }), jsx(ProjectDescription, { description: bondData.shortDescription, isProjectView: true }), jsx(Flex, { sx: { width: '100%', display: isProjectView ? ['flex', 'flex', 'flex', 'none'] : 'flex' }, children: jsx(BondCards, { bondData: bondData, isDoingMaxBuy: exceedsSafeMaxBuy && !exceedsRealMaxBuy }) }), jsx(Estimations, { depositAmount: depositAmount, inputValue: inputValue, inputTokenPrice: inputTokenPrice, bondData: bondData, youSpendString: youSpendString, isZap: isZap, fetchingZapQuote: fetchingZapQuote, zapError: zapError }), jsx(TokenSelectorPanel, { typedValue: inputValue, setTypedValue: setInputValue, selectedToken: inputToken, handleValueBtn: handleMaxButton, handleCurrencySelect: handleCurrencySelect, bondChainId: ChainId.APTOS, enableZap: true, bondPrincipalToken: bondData?.lpToken, tokenBalance: inputCurrencyBalance ?? undefined, selectedTokenPrice: inputTokenPrice, isConnected: !!aptosAccount }), bondData?.warningCard && (jsx(Flex, { sx: {
|
|
329
|
+
width: '100%',
|
|
330
|
+
background: '#DE62F366',
|
|
331
|
+
justifyContent: 'center',
|
|
332
|
+
mt: '10px',
|
|
333
|
+
borderRadius: 'normal',
|
|
334
|
+
p: '2px 10px',
|
|
335
|
+
}, children: jsx(Text, { sx: { fontSize: '12px', fontWeight: 400, display: 'flex', alignItems: 'center', gap: '10px' }, children: jsx(SafeHTMLComponent, { html: bondData?.warningCard }) }) })), jsx(Flex, { className: "modaltable-container button-container", children: jsx(Flex, { className: "button-container buy", children: !aptosAccount ? (jsx(ConnectAptosWalletButton, {})) : (jsx(Button, { className: "action-button", load: load, disabled: !canBuy, onClick: handleBuy, sx: { width: '100%', fontSize: ['14px', '14px', '14px', '16px'] }, children: SDKConfig.blockSales
|
|
336
|
+
? 'Complete KYC to Buy'
|
|
337
|
+
: exceedsRealMaxBuy
|
|
338
|
+
? 'Exceeds Max Buy. Reduce amount'
|
|
339
|
+
: exceedsBalance
|
|
340
|
+
? 'Insufficient balance'
|
|
341
|
+
: zapError
|
|
342
|
+
? 'Swap quote failed'
|
|
343
|
+
: !inputValue || parseFloat(inputValue) === 0
|
|
344
|
+
? 'Insert amount'
|
|
345
|
+
: 'buy' })) }) })] }) })) : (jsx(Fragment, {}));
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
export { BuyComponentAptos as default };
|
|
349
|
+
//# sourceMappingURL=BuyComponentAptos.js.map
|