@b3dotfun/sdk 0.1.65-alpha.2 → 0.1.65-alpha.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.
- package/dist/cjs/anyspend/react/components/QRDeposit.js +3 -1
- package/dist/cjs/anyspend/utils/chain.d.ts +1 -1
- package/dist/cjs/anyspend/utils/chain.js +72 -62
- package/dist/esm/anyspend/react/components/QRDeposit.js +4 -2
- package/dist/esm/anyspend/utils/chain.d.ts +1 -1
- package/dist/esm/anyspend/utils/chain.js +72 -62
- package/dist/types/anyspend/utils/chain.d.ts +1 -1
- package/package.json +1 -1
- package/src/anyspend/react/components/QRDeposit.tsx +17 -2
- package/src/anyspend/utils/chain.ts +81 -65
|
@@ -131,6 +131,8 @@ function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenP
|
|
|
131
131
|
(0, useOnOrderSuccess_1.useOnOrderSuccess)({ orderData: oat, orderId, onSuccess });
|
|
132
132
|
// For pure transfers, always use recipient address; for orders, use global address
|
|
133
133
|
const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
|
|
134
|
+
// Generate EIP-681 payment URI for the QR code so wallets know which chain/token to use
|
|
135
|
+
const qrValue = (0, anyspend_1.getPaymentUrl)(displayAddress, undefined, sourceToken.address === anyspend_1.ZERO_ADDRESS ? "ETH" : sourceToken.address, sourceChainId, sourceToken.decimals);
|
|
134
136
|
const handleCopyAddress = async () => {
|
|
135
137
|
if (displayAddress) {
|
|
136
138
|
await navigator.clipboard.writeText(displayAddress);
|
|
@@ -163,7 +165,7 @@ function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenP
|
|
|
163
165
|
}
|
|
164
166
|
return ((0, jsx_runtime_1.jsx)("div", { className: classes?.container ||
|
|
165
167
|
(0, cn_1.cn)("anyspend-container anyspend-qr-deposit font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: (0, jsx_runtime_1.jsxs)("div", { className: classes?.content || "anyspend-qr-deposit-content flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: classes?.header || "anyspend-qr-header flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("button", { onClick: handleBack, className: classes?.backButton || "anyspend-qr-back-button text-as-secondary hover:text-as-primary", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), (0, jsx_runtime_1.jsx)("h2", { className: classes?.title || "anyspend-qr-title text-as-primary text-base font-semibold", children: "Deposit" }), onClose ? ((0, jsx_runtime_1.jsx)("button", { onClick: handleClose, className: classes?.closeButton || "anyspend-qr-close-button text-as-secondary hover:text-as-primary", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-5" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.tokenSelectorContainer || "anyspend-qr-token-selector flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsx)("label", { className: classes?.tokenSelectorLabel || "anyspend-qr-token-label text-as-secondary text-sm", children: "Send" }), (0, jsx_runtime_1.jsx)(relay_kit_ui_1.TokenSelector, { chainIdsFilter: (0, anyspend_1.getAvailableChainIds)("from"), context: "from", fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: (0, anyspend_1.getAvailableChainIds)("from"), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm"], token: undefined, trigger: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", role: "combobox", className: classes?.tokenSelectorTrigger ||
|
|
166
|
-
"anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: (0, jsx_runtime_1.jsx)(qrcode_react_1.QRCodeSVG, { value:
|
|
168
|
+
"anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: (0, jsx_runtime_1.jsx)(qrcode_react_1.QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), (0, jsx_runtime_1.jsxs)("span", { className: classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", (0, jsx_runtime_1.jsx)("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.addressContainer || "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: classes?.addressLabel || "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.addressRow || "anyspend-qr-address-row flex items-start gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: classes?.address || "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), (0, jsx_runtime_1.jsx)("button", { onClick: handleCopyAddress, className: classes?.addressCopyIcon ||
|
|
167
169
|
"anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "h-4 w-4" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "h-4 w-4" }) })] })] })] }), (0, jsx_runtime_1.jsx)(WarningText_1.ChainWarningText, { chainId: destinationChainId }), (0, jsx_runtime_1.jsxs)(WarningText_1.WarningText, { children: ["Only send ", sourceToken.symbol, " on ", anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] }), isPureTransfer && isWatchingTransfer && ((0, jsx_runtime_1.jsxs)("div", { className: classes?.watchingIndicator ||
|
|
168
170
|
"anyspend-qr-watching flex items-center justify-center gap-2 rounded-lg bg-blue-500/10 p-3", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin text-blue-500" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-blue-500", children: "Watching for incoming transfer..." })] })), (0, jsx_runtime_1.jsx)("button", { onClick: handleCopyAddress, className: classes?.copyButton ||
|
|
169
171
|
"anyspend-qr-copy-button flex w-full items-center justify-center gap-2 rounded-xl bg-blue-500 py-3.5 font-medium text-white transition-all hover:bg-blue-600", children: "Copy deposit address" })] }) }));
|
|
@@ -82,7 +82,7 @@ export declare function isTestnet(chainId: number): boolean;
|
|
|
82
82
|
export declare function getDefaultToken(chainId: number): components["schemas"]["Token"];
|
|
83
83
|
export declare function getChainName(chainId: number): string;
|
|
84
84
|
export declare function getCoingeckoName(chainId: number): string | null;
|
|
85
|
-
export declare function getPaymentUrl(address: string, amount: bigint, currency: string, chainId: number, decimals?: number): string;
|
|
85
|
+
export declare function getPaymentUrl(address: string, amount: bigint | undefined, currency: string, chainId: number, decimals?: number): string;
|
|
86
86
|
export declare function getExplorerTxUrl(chainId: number, txHash: string): string;
|
|
87
87
|
export declare function getExplorerAddressUrl(chainId: number, address: string): string;
|
|
88
88
|
export declare function getMulticall3Address(chainId: number): string;
|
|
@@ -392,8 +392,8 @@ function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
392
392
|
// For EVM chains, follow EIP-681 format
|
|
393
393
|
// Format: ethereum:[address]@[chainId]?value=[amount]&symbol=[symbol]
|
|
394
394
|
const params = new URLSearchParams();
|
|
395
|
-
// Add value for native token transfers
|
|
396
|
-
if (currency === chain.nativeToken.symbol) {
|
|
395
|
+
// Add value for native token transfers (skip if amount not provided, e.g. deposit_first)
|
|
396
|
+
if (currency === chain.nativeToken.symbol && amount !== undefined) {
|
|
397
397
|
params.append("value", amount.toString());
|
|
398
398
|
}
|
|
399
399
|
// Handle token transfers differently from native transfers
|
|
@@ -408,28 +408,31 @@ function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
408
408
|
}
|
|
409
409
|
// For ERC20 tokens, convert from smallest unit to display units using decimals
|
|
410
410
|
// For example: 2400623 (raw) with 6 decimals becomes "2.400623"
|
|
411
|
-
|
|
412
|
-
if (
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
411
|
+
// Skip amount if not provided (e.g. deposit_first orders)
|
|
412
|
+
if (amount !== undefined) {
|
|
413
|
+
let displayAmount;
|
|
414
|
+
if (decimals !== undefined && currency !== chain.nativeToken.symbol) {
|
|
415
|
+
// Convert from smallest unit to display unit for ERC20 tokens
|
|
416
|
+
const divisor = BigInt(10 ** decimals);
|
|
417
|
+
const wholePart = amount / divisor;
|
|
418
|
+
const fractionalPart = amount % divisor;
|
|
419
|
+
if (fractionalPart === BigInt(0)) {
|
|
420
|
+
displayAmount = wholePart.toString();
|
|
421
|
+
}
|
|
422
|
+
else {
|
|
423
|
+
// Format fractional part with leading zeros if needed
|
|
424
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
425
|
+
// Remove trailing zeros
|
|
426
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
427
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
428
|
+
}
|
|
419
429
|
}
|
|
420
430
|
else {
|
|
421
|
-
//
|
|
422
|
-
|
|
423
|
-
// Remove trailing zeros
|
|
424
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
425
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
431
|
+
// For native tokens or when decimals not provided, use raw amount
|
|
432
|
+
displayAmount = amount.toString();
|
|
426
433
|
}
|
|
434
|
+
tokenParams.append("amount", displayAmount);
|
|
427
435
|
}
|
|
428
|
-
else {
|
|
429
|
-
// For native tokens or when decimals not provided, use raw amount
|
|
430
|
-
displayAmount = amount.toString();
|
|
431
|
-
}
|
|
432
|
-
tokenParams.append("amount", displayAmount);
|
|
433
436
|
tokenParams.append("address", address); // recipient address
|
|
434
437
|
// For Arbitrum and other L2s, try a more explicit format
|
|
435
438
|
if (chainId !== chains_1.mainnet.id) {
|
|
@@ -449,7 +452,9 @@ function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
449
452
|
// to make sure wallets recognize the correct chain
|
|
450
453
|
const nativeParams = new URLSearchParams();
|
|
451
454
|
nativeParams.append("chainId", chainId.toString());
|
|
452
|
-
|
|
455
|
+
if (amount !== undefined) {
|
|
456
|
+
nativeParams.append("value", amount.toString());
|
|
457
|
+
}
|
|
453
458
|
const url = `ethereum:${address}@${chainId}?${nativeParams.toString()}`;
|
|
454
459
|
return url;
|
|
455
460
|
}
|
|
@@ -468,60 +473,65 @@ function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
468
473
|
const isNativeSOL = currency === chain.nativeToken.symbol || currency === "SOL" || currency === "11111111111111111111111111111111";
|
|
469
474
|
if (isNativeSOL) {
|
|
470
475
|
// Native SOL transfers - convert from lamports to SOL
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
else {
|
|
486
|
-
// Fallback: assume SOL has 9 decimals
|
|
487
|
-
const divisor = BigInt(1000000000); // 1e9
|
|
488
|
-
const wholePart = amount / divisor;
|
|
489
|
-
const fractionalPart = amount % divisor;
|
|
490
|
-
if (fractionalPart === BigInt(0)) {
|
|
491
|
-
displayAmount = wholePart.toString();
|
|
476
|
+
if (amount !== undefined) {
|
|
477
|
+
let displayAmount;
|
|
478
|
+
if (decimals !== undefined) {
|
|
479
|
+
const divisor = BigInt(10 ** decimals);
|
|
480
|
+
const wholePart = amount / divisor;
|
|
481
|
+
const fractionalPart = amount % divisor;
|
|
482
|
+
if (fractionalPart === BigInt(0)) {
|
|
483
|
+
displayAmount = wholePart.toString();
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
487
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
488
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
489
|
+
}
|
|
492
490
|
}
|
|
493
491
|
else {
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
492
|
+
// Fallback: assume SOL has 9 decimals
|
|
493
|
+
const divisor = BigInt(1000000000); // 1e9
|
|
494
|
+
const wholePart = amount / divisor;
|
|
495
|
+
const fractionalPart = amount % divisor;
|
|
496
|
+
if (fractionalPart === BigInt(0)) {
|
|
497
|
+
displayAmount = wholePart.toString();
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
const fractionalStr = fractionalPart.toString().padStart(9, "0");
|
|
501
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
502
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
503
|
+
}
|
|
497
504
|
}
|
|
505
|
+
// For native SOL, use simple format without spl-token parameter
|
|
506
|
+
params.append("amount", displayAmount);
|
|
498
507
|
}
|
|
499
|
-
// For native SOL, use simple format without spl-token parameter
|
|
500
|
-
params.append("amount", displayAmount);
|
|
501
508
|
}
|
|
502
509
|
else {
|
|
503
510
|
// SPL token transfers
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
+
if (amount !== undefined) {
|
|
512
|
+
let displayAmount;
|
|
513
|
+
if (decimals !== undefined) {
|
|
514
|
+
const divisor = BigInt(10 ** decimals);
|
|
515
|
+
const wholePart = amount / divisor;
|
|
516
|
+
const fractionalPart = amount % divisor;
|
|
517
|
+
if (fractionalPart === BigInt(0)) {
|
|
518
|
+
displayAmount = wholePart.toString();
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
522
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
523
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
524
|
+
}
|
|
511
525
|
}
|
|
512
526
|
else {
|
|
513
|
-
|
|
514
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
515
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
527
|
+
displayAmount = amount.toString();
|
|
516
528
|
}
|
|
529
|
+
params.append("amount", displayAmount);
|
|
517
530
|
}
|
|
518
|
-
else {
|
|
519
|
-
displayAmount = amount.toString();
|
|
520
|
-
}
|
|
521
|
-
params.append("amount", displayAmount);
|
|
522
531
|
params.append("spl-token", currency); // token mint address
|
|
523
532
|
}
|
|
524
|
-
const
|
|
533
|
+
const queryString = params.toString();
|
|
534
|
+
const url = queryString ? `solana:${address}?${queryString}` : `solana:${address}`;
|
|
525
535
|
console.log("Solana URL (isNativeSOL:", isNativeSOL, "):", url);
|
|
526
536
|
return url;
|
|
527
537
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ALL_CHAINS, getAvailableChainIds, isSameChainAndToken } from "../../../anyspend/index.js";
|
|
2
|
+
import { ALL_CHAINS, getAvailableChainIds, getPaymentUrl, isSameChainAndToken, ZERO_ADDRESS, } from "../../../anyspend/index.js";
|
|
3
3
|
import { Button, toast } from "../../../global-account/react/index.js";
|
|
4
4
|
import { cn } from "../../../shared/utils/cn.js";
|
|
5
5
|
import { TokenSelector } from "@relayprotocol/relay-kit-ui";
|
|
@@ -128,6 +128,8 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
|
|
|
128
128
|
useOnOrderSuccess({ orderData: oat, orderId, onSuccess });
|
|
129
129
|
// For pure transfers, always use recipient address; for orders, use global address
|
|
130
130
|
const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
|
|
131
|
+
// Generate EIP-681 payment URI for the QR code so wallets know which chain/token to use
|
|
132
|
+
const qrValue = getPaymentUrl(displayAddress, undefined, sourceToken.address === ZERO_ADDRESS ? "ETH" : sourceToken.address, sourceChainId, sourceToken.decimals);
|
|
131
133
|
const handleCopyAddress = async () => {
|
|
132
134
|
if (displayAddress) {
|
|
133
135
|
await navigator.clipboard.writeText(displayAddress);
|
|
@@ -160,7 +162,7 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
|
|
|
160
162
|
}
|
|
161
163
|
return (_jsx("div", { className: classes?.container ||
|
|
162
164
|
cn("anyspend-container anyspend-qr-deposit font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: classes?.content || "anyspend-qr-deposit-content flex flex-col gap-4", children: [_jsxs("div", { className: classes?.header || "anyspend-qr-header flex items-center justify-between", children: [_jsx("button", { onClick: handleBack, className: classes?.backButton || "anyspend-qr-back-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), _jsx("h2", { className: classes?.title || "anyspend-qr-title text-as-primary text-base font-semibold", children: "Deposit" }), onClose ? (_jsx("button", { onClick: handleClose, className: classes?.closeButton || "anyspend-qr-close-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })) : (_jsx("div", { className: "w-5" }))] }), _jsxs("div", { className: classes?.tokenSelectorContainer || "anyspend-qr-token-selector flex flex-col gap-1.5", children: [_jsx("label", { className: classes?.tokenSelectorLabel || "anyspend-qr-token-label text-as-secondary text-sm", children: "Send" }), _jsx(TokenSelector, { chainIdsFilter: getAvailableChainIds("from"), context: "from", fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: getAvailableChainIds("from"), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: classes?.tokenSelectorTrigger ||
|
|
163
|
-
"anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), _jsxs("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value:
|
|
165
|
+
"anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), _jsxs("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), _jsxs("span", { className: classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", _jsx("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), _jsxs("div", { className: classes?.addressContainer || "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [_jsx("span", { className: classes?.addressLabel || "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), _jsxs("div", { className: classes?.addressRow || "anyspend-qr-address-row flex items-start gap-1", children: [_jsx("span", { className: classes?.address || "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), _jsx("button", { onClick: handleCopyAddress, className: classes?.addressCopyIcon ||
|
|
164
166
|
"anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(ChainWarningText, { chainId: destinationChainId }), _jsxs(WarningText, { children: ["Only send ", sourceToken.symbol, " on ", ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] }), isPureTransfer && isWatchingTransfer && (_jsxs("div", { className: classes?.watchingIndicator ||
|
|
165
167
|
"anyspend-qr-watching flex items-center justify-center gap-2 rounded-lg bg-blue-500/10 p-3", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-blue-500" }), _jsx("span", { className: "text-sm text-blue-500", children: "Watching for incoming transfer..." })] })), _jsx("button", { onClick: handleCopyAddress, className: classes?.copyButton ||
|
|
166
168
|
"anyspend-qr-copy-button flex w-full items-center justify-center gap-2 rounded-xl bg-blue-500 py-3.5 font-medium text-white transition-all hover:bg-blue-600", children: "Copy deposit address" })] }) }));
|
|
@@ -82,7 +82,7 @@ export declare function isTestnet(chainId: number): boolean;
|
|
|
82
82
|
export declare function getDefaultToken(chainId: number): components["schemas"]["Token"];
|
|
83
83
|
export declare function getChainName(chainId: number): string;
|
|
84
84
|
export declare function getCoingeckoName(chainId: number): string | null;
|
|
85
|
-
export declare function getPaymentUrl(address: string, amount: bigint, currency: string, chainId: number, decimals?: number): string;
|
|
85
|
+
export declare function getPaymentUrl(address: string, amount: bigint | undefined, currency: string, chainId: number, decimals?: number): string;
|
|
86
86
|
export declare function getExplorerTxUrl(chainId: number, txHash: string): string;
|
|
87
87
|
export declare function getExplorerAddressUrl(chainId: number, address: string): string;
|
|
88
88
|
export declare function getMulticall3Address(chainId: number): string;
|
|
@@ -362,8 +362,8 @@ export function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
362
362
|
// For EVM chains, follow EIP-681 format
|
|
363
363
|
// Format: ethereum:[address]@[chainId]?value=[amount]&symbol=[symbol]
|
|
364
364
|
const params = new URLSearchParams();
|
|
365
|
-
// Add value for native token transfers
|
|
366
|
-
if (currency === chain.nativeToken.symbol) {
|
|
365
|
+
// Add value for native token transfers (skip if amount not provided, e.g. deposit_first)
|
|
366
|
+
if (currency === chain.nativeToken.symbol && amount !== undefined) {
|
|
367
367
|
params.append("value", amount.toString());
|
|
368
368
|
}
|
|
369
369
|
// Handle token transfers differently from native transfers
|
|
@@ -378,28 +378,31 @@ export function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
378
378
|
}
|
|
379
379
|
// For ERC20 tokens, convert from smallest unit to display units using decimals
|
|
380
380
|
// For example: 2400623 (raw) with 6 decimals becomes "2.400623"
|
|
381
|
-
|
|
382
|
-
if (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
381
|
+
// Skip amount if not provided (e.g. deposit_first orders)
|
|
382
|
+
if (amount !== undefined) {
|
|
383
|
+
let displayAmount;
|
|
384
|
+
if (decimals !== undefined && currency !== chain.nativeToken.symbol) {
|
|
385
|
+
// Convert from smallest unit to display unit for ERC20 tokens
|
|
386
|
+
const divisor = BigInt(10 ** decimals);
|
|
387
|
+
const wholePart = amount / divisor;
|
|
388
|
+
const fractionalPart = amount % divisor;
|
|
389
|
+
if (fractionalPart === BigInt(0)) {
|
|
390
|
+
displayAmount = wholePart.toString();
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
// Format fractional part with leading zeros if needed
|
|
394
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
395
|
+
// Remove trailing zeros
|
|
396
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
397
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
398
|
+
}
|
|
389
399
|
}
|
|
390
400
|
else {
|
|
391
|
-
//
|
|
392
|
-
|
|
393
|
-
// Remove trailing zeros
|
|
394
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
395
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
401
|
+
// For native tokens or when decimals not provided, use raw amount
|
|
402
|
+
displayAmount = amount.toString();
|
|
396
403
|
}
|
|
404
|
+
tokenParams.append("amount", displayAmount);
|
|
397
405
|
}
|
|
398
|
-
else {
|
|
399
|
-
// For native tokens or when decimals not provided, use raw amount
|
|
400
|
-
displayAmount = amount.toString();
|
|
401
|
-
}
|
|
402
|
-
tokenParams.append("amount", displayAmount);
|
|
403
406
|
tokenParams.append("address", address); // recipient address
|
|
404
407
|
// For Arbitrum and other L2s, try a more explicit format
|
|
405
408
|
if (chainId !== mainnet.id) {
|
|
@@ -419,7 +422,9 @@ export function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
419
422
|
// to make sure wallets recognize the correct chain
|
|
420
423
|
const nativeParams = new URLSearchParams();
|
|
421
424
|
nativeParams.append("chainId", chainId.toString());
|
|
422
|
-
|
|
425
|
+
if (amount !== undefined) {
|
|
426
|
+
nativeParams.append("value", amount.toString());
|
|
427
|
+
}
|
|
423
428
|
const url = `ethereum:${address}@${chainId}?${nativeParams.toString()}`;
|
|
424
429
|
return url;
|
|
425
430
|
}
|
|
@@ -438,60 +443,65 @@ export function getPaymentUrl(address, amount, currency, chainId, decimals) {
|
|
|
438
443
|
const isNativeSOL = currency === chain.nativeToken.symbol || currency === "SOL" || currency === "11111111111111111111111111111111";
|
|
439
444
|
if (isNativeSOL) {
|
|
440
445
|
// Native SOL transfers - convert from lamports to SOL
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
else {
|
|
456
|
-
// Fallback: assume SOL has 9 decimals
|
|
457
|
-
const divisor = BigInt(1000000000); // 1e9
|
|
458
|
-
const wholePart = amount / divisor;
|
|
459
|
-
const fractionalPart = amount % divisor;
|
|
460
|
-
if (fractionalPart === BigInt(0)) {
|
|
461
|
-
displayAmount = wholePart.toString();
|
|
446
|
+
if (amount !== undefined) {
|
|
447
|
+
let displayAmount;
|
|
448
|
+
if (decimals !== undefined) {
|
|
449
|
+
const divisor = BigInt(10 ** decimals);
|
|
450
|
+
const wholePart = amount / divisor;
|
|
451
|
+
const fractionalPart = amount % divisor;
|
|
452
|
+
if (fractionalPart === BigInt(0)) {
|
|
453
|
+
displayAmount = wholePart.toString();
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
457
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
458
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
459
|
+
}
|
|
462
460
|
}
|
|
463
461
|
else {
|
|
464
|
-
|
|
465
|
-
const
|
|
466
|
-
|
|
462
|
+
// Fallback: assume SOL has 9 decimals
|
|
463
|
+
const divisor = BigInt(1000000000); // 1e9
|
|
464
|
+
const wholePart = amount / divisor;
|
|
465
|
+
const fractionalPart = amount % divisor;
|
|
466
|
+
if (fractionalPart === BigInt(0)) {
|
|
467
|
+
displayAmount = wholePart.toString();
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
const fractionalStr = fractionalPart.toString().padStart(9, "0");
|
|
471
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
472
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
473
|
+
}
|
|
467
474
|
}
|
|
475
|
+
// For native SOL, use simple format without spl-token parameter
|
|
476
|
+
params.append("amount", displayAmount);
|
|
468
477
|
}
|
|
469
|
-
// For native SOL, use simple format without spl-token parameter
|
|
470
|
-
params.append("amount", displayAmount);
|
|
471
478
|
}
|
|
472
479
|
else {
|
|
473
480
|
// SPL token transfers
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
+
if (amount !== undefined) {
|
|
482
|
+
let displayAmount;
|
|
483
|
+
if (decimals !== undefined) {
|
|
484
|
+
const divisor = BigInt(10 ** decimals);
|
|
485
|
+
const wholePart = amount / divisor;
|
|
486
|
+
const fractionalPart = amount % divisor;
|
|
487
|
+
if (fractionalPart === BigInt(0)) {
|
|
488
|
+
displayAmount = wholePart.toString();
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
492
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
493
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
494
|
+
}
|
|
481
495
|
}
|
|
482
496
|
else {
|
|
483
|
-
|
|
484
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
485
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
497
|
+
displayAmount = amount.toString();
|
|
486
498
|
}
|
|
499
|
+
params.append("amount", displayAmount);
|
|
487
500
|
}
|
|
488
|
-
else {
|
|
489
|
-
displayAmount = amount.toString();
|
|
490
|
-
}
|
|
491
|
-
params.append("amount", displayAmount);
|
|
492
501
|
params.append("spl-token", currency); // token mint address
|
|
493
502
|
}
|
|
494
|
-
const
|
|
503
|
+
const queryString = params.toString();
|
|
504
|
+
const url = queryString ? `solana:${address}?${queryString}` : `solana:${address}`;
|
|
495
505
|
console.log("Solana URL (isNativeSOL:", isNativeSOL, "):", url);
|
|
496
506
|
return url;
|
|
497
507
|
}
|
|
@@ -82,7 +82,7 @@ export declare function isTestnet(chainId: number): boolean;
|
|
|
82
82
|
export declare function getDefaultToken(chainId: number): components["schemas"]["Token"];
|
|
83
83
|
export declare function getChainName(chainId: number): string;
|
|
84
84
|
export declare function getCoingeckoName(chainId: number): string | null;
|
|
85
|
-
export declare function getPaymentUrl(address: string, amount: bigint, currency: string, chainId: number, decimals?: number): string;
|
|
85
|
+
export declare function getPaymentUrl(address: string, amount: bigint | undefined, currency: string, chainId: number, decimals?: number): string;
|
|
86
86
|
export declare function getExplorerTxUrl(chainId: number, txHash: string): string;
|
|
87
87
|
export declare function getExplorerAddressUrl(chainId: number, address: string): string;
|
|
88
88
|
export declare function getMulticall3Address(chainId: number): string;
|
package/package.json
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ALL_CHAINS,
|
|
3
|
+
getAvailableChainIds,
|
|
4
|
+
getPaymentUrl,
|
|
5
|
+
isSameChainAndToken,
|
|
6
|
+
ZERO_ADDRESS,
|
|
7
|
+
} from "@b3dotfun/sdk/anyspend";
|
|
2
8
|
import { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
3
9
|
import { Button, toast } from "@b3dotfun/sdk/global-account/react";
|
|
4
10
|
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
@@ -194,6 +200,15 @@ export function QRDeposit({
|
|
|
194
200
|
// For pure transfers, always use recipient address; for orders, use global address
|
|
195
201
|
const displayAddress = isPureTransfer ? recipientAddress : globalAddress || recipientAddress;
|
|
196
202
|
|
|
203
|
+
// Generate EIP-681 payment URI for the QR code so wallets know which chain/token to use
|
|
204
|
+
const qrValue = getPaymentUrl(
|
|
205
|
+
displayAddress,
|
|
206
|
+
undefined,
|
|
207
|
+
sourceToken.address === ZERO_ADDRESS ? "ETH" : sourceToken.address,
|
|
208
|
+
sourceChainId,
|
|
209
|
+
sourceToken.decimals,
|
|
210
|
+
);
|
|
211
|
+
|
|
197
212
|
const handleCopyAddress = async () => {
|
|
198
213
|
if (displayAddress) {
|
|
199
214
|
await navigator.clipboard.writeText(displayAddress);
|
|
@@ -376,7 +391,7 @@ export function QRDeposit({
|
|
|
376
391
|
{/* QR Code */}
|
|
377
392
|
<div className={classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2"}>
|
|
378
393
|
<div className={classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2"}>
|
|
379
|
-
<QRCodeSVG value={
|
|
394
|
+
<QRCodeSVG value={qrValue} size={120} level="M" marginSize={0} />
|
|
380
395
|
</div>
|
|
381
396
|
<span className={classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs"}>
|
|
382
397
|
SCAN WITH <span className="inline-block">🦊</span>
|
|
@@ -421,7 +421,13 @@ export function getCoingeckoName(chainId: number): string | null {
|
|
|
421
421
|
return ALL_CHAINS[chainId].coingeckoName;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
export function getPaymentUrl(
|
|
424
|
+
export function getPaymentUrl(
|
|
425
|
+
address: string,
|
|
426
|
+
amount: bigint | undefined,
|
|
427
|
+
currency: string,
|
|
428
|
+
chainId: number,
|
|
429
|
+
decimals?: number,
|
|
430
|
+
) {
|
|
425
431
|
// Get chain type to determine URL format
|
|
426
432
|
const chainType = getChainType(chainId);
|
|
427
433
|
const chain = ALL_CHAINS[chainId];
|
|
@@ -433,8 +439,8 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
433
439
|
// Format: ethereum:[address]@[chainId]?value=[amount]&symbol=[symbol]
|
|
434
440
|
const params = new URLSearchParams();
|
|
435
441
|
|
|
436
|
-
// Add value for native token transfers
|
|
437
|
-
if (currency === chain.nativeToken.symbol) {
|
|
442
|
+
// Add value for native token transfers (skip if amount not provided, e.g. deposit_first)
|
|
443
|
+
if (currency === chain.nativeToken.symbol && amount !== undefined) {
|
|
438
444
|
params.append("value", amount.toString());
|
|
439
445
|
}
|
|
440
446
|
|
|
@@ -453,28 +459,31 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
453
459
|
|
|
454
460
|
// For ERC20 tokens, convert from smallest unit to display units using decimals
|
|
455
461
|
// For example: 2400623 (raw) with 6 decimals becomes "2.400623"
|
|
456
|
-
|
|
457
|
-
if (
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
462
|
+
// Skip amount if not provided (e.g. deposit_first orders)
|
|
463
|
+
if (amount !== undefined) {
|
|
464
|
+
let displayAmount: string;
|
|
465
|
+
if (decimals !== undefined && currency !== chain.nativeToken.symbol) {
|
|
466
|
+
// Convert from smallest unit to display unit for ERC20 tokens
|
|
467
|
+
const divisor = BigInt(10 ** decimals);
|
|
468
|
+
const wholePart = amount / divisor;
|
|
469
|
+
const fractionalPart = amount % divisor;
|
|
470
|
+
|
|
471
|
+
if (fractionalPart === BigInt(0)) {
|
|
472
|
+
displayAmount = wholePart.toString();
|
|
473
|
+
} else {
|
|
474
|
+
// Format fractional part with leading zeros if needed
|
|
475
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
476
|
+
// Remove trailing zeros
|
|
477
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
478
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
479
|
+
}
|
|
465
480
|
} else {
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
// Remove trailing zeros
|
|
469
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
470
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
481
|
+
// For native tokens or when decimals not provided, use raw amount
|
|
482
|
+
displayAmount = amount.toString();
|
|
471
483
|
}
|
|
472
|
-
} else {
|
|
473
|
-
// For native tokens or when decimals not provided, use raw amount
|
|
474
|
-
displayAmount = amount.toString();
|
|
475
|
-
}
|
|
476
484
|
|
|
477
|
-
|
|
485
|
+
tokenParams.append("amount", displayAmount);
|
|
486
|
+
}
|
|
478
487
|
tokenParams.append("address", address); // recipient address
|
|
479
488
|
|
|
480
489
|
// For Arbitrum and other L2s, try a more explicit format
|
|
@@ -495,7 +504,9 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
495
504
|
// to make sure wallets recognize the correct chain
|
|
496
505
|
const nativeParams = new URLSearchParams();
|
|
497
506
|
nativeParams.append("chainId", chainId.toString());
|
|
498
|
-
|
|
507
|
+
if (amount !== undefined) {
|
|
508
|
+
nativeParams.append("value", amount.toString());
|
|
509
|
+
}
|
|
499
510
|
const url = `ethereum:${address}@${chainId}?${nativeParams.toString()}`;
|
|
500
511
|
return url;
|
|
501
512
|
} else {
|
|
@@ -517,60 +528,65 @@ export function getPaymentUrl(address: string, amount: bigint, currency: string,
|
|
|
517
528
|
|
|
518
529
|
if (isNativeSOL) {
|
|
519
530
|
// Native SOL transfers - convert from lamports to SOL
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
531
|
+
if (amount !== undefined) {
|
|
532
|
+
let displayAmount: string;
|
|
533
|
+
if (decimals !== undefined) {
|
|
534
|
+
const divisor = BigInt(10 ** decimals);
|
|
535
|
+
const wholePart = amount / divisor;
|
|
536
|
+
const fractionalPart = amount % divisor;
|
|
537
|
+
|
|
538
|
+
if (fractionalPart === BigInt(0)) {
|
|
539
|
+
displayAmount = wholePart.toString();
|
|
540
|
+
} else {
|
|
541
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
542
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
543
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
544
|
+
}
|
|
528
545
|
} else {
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
|
|
546
|
+
// Fallback: assume SOL has 9 decimals
|
|
547
|
+
const divisor = BigInt(1000000000); // 1e9
|
|
548
|
+
const wholePart = amount / divisor;
|
|
549
|
+
const fractionalPart = amount % divisor;
|
|
550
|
+
|
|
551
|
+
if (fractionalPart === BigInt(0)) {
|
|
552
|
+
displayAmount = wholePart.toString();
|
|
553
|
+
} else {
|
|
554
|
+
const fractionalStr = fractionalPart.toString().padStart(9, "0");
|
|
555
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
556
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
557
|
+
}
|
|
532
558
|
}
|
|
533
|
-
} else {
|
|
534
|
-
// Fallback: assume SOL has 9 decimals
|
|
535
|
-
const divisor = BigInt(1000000000); // 1e9
|
|
536
|
-
const wholePart = amount / divisor;
|
|
537
|
-
const fractionalPart = amount % divisor;
|
|
538
559
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
} else {
|
|
542
|
-
const fractionalStr = fractionalPart.toString().padStart(9, "0");
|
|
543
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
544
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
545
|
-
}
|
|
560
|
+
// For native SOL, use simple format without spl-token parameter
|
|
561
|
+
params.append("amount", displayAmount);
|
|
546
562
|
}
|
|
547
|
-
|
|
548
|
-
// For native SOL, use simple format without spl-token parameter
|
|
549
|
-
params.append("amount", displayAmount);
|
|
550
563
|
} else {
|
|
551
564
|
// SPL token transfers
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
565
|
+
if (amount !== undefined) {
|
|
566
|
+
let displayAmount: string;
|
|
567
|
+
if (decimals !== undefined) {
|
|
568
|
+
const divisor = BigInt(10 ** decimals);
|
|
569
|
+
const wholePart = amount / divisor;
|
|
570
|
+
const fractionalPart = amount % divisor;
|
|
571
|
+
|
|
572
|
+
if (fractionalPart === BigInt(0)) {
|
|
573
|
+
displayAmount = wholePart.toString();
|
|
574
|
+
} else {
|
|
575
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, "0");
|
|
576
|
+
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
577
|
+
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
578
|
+
}
|
|
560
579
|
} else {
|
|
561
|
-
|
|
562
|
-
const trimmedFractional = fractionalStr.replace(/0+$/, "");
|
|
563
|
-
displayAmount = trimmedFractional ? `${wholePart}.${trimmedFractional}` : wholePart.toString();
|
|
580
|
+
displayAmount = amount.toString();
|
|
564
581
|
}
|
|
565
|
-
} else {
|
|
566
|
-
displayAmount = amount.toString();
|
|
567
|
-
}
|
|
568
582
|
|
|
569
|
-
|
|
583
|
+
params.append("amount", displayAmount);
|
|
584
|
+
}
|
|
570
585
|
params.append("spl-token", currency); // token mint address
|
|
571
586
|
}
|
|
572
587
|
|
|
573
|
-
const
|
|
588
|
+
const queryString = params.toString();
|
|
589
|
+
const url = queryString ? `solana:${address}?${queryString}` : `solana:${address}`;
|
|
574
590
|
console.log("Solana URL (isNativeSOL:", isNativeSOL, "):", url);
|
|
575
591
|
return url;
|
|
576
592
|
}
|