Compare commits

..

No commits in common. "2cea7d3908c9de7fdd66b756248cba4ed3b9c3f3" and "537f3030e70b30d8016fa004719c0f5bb135fc79" have entirely different histories.

3 changed files with 40 additions and 110 deletions

View File

@ -15,7 +15,7 @@ export interface ClobSubmitOrderRequest {
/** 价格(整数,已乘 10000 */
price: number
side: number
/** 数量(micro units1_000_000 = 1 share支持 6 位小数份额) */
/** 数量(份额) */
size: number
taker: boolean
tokenID: string

View File

@ -3,8 +3,8 @@
<div class="footer-inner">
<div class="footer-top">
<div class="footer-social">
<a href="mailto:futurepulse@futurepulse.cc" class="social-icon" aria-label="Email"><v-icon size="24">mdi-email-outline</v-icon></a>
<a href="https://x.com/FuturePulse_" class="social-icon" aria-label="X (Twitter)"><v-icon size="24">mdi-twitter</v-icon></a>
<a href="#" class="social-icon" aria-label="Email"><v-icon size="24">mdi-email-outline</v-icon></a>
<a href="#" class="social-icon" aria-label="X (Twitter)"><v-icon size="24">mdi-twitter</v-icon></a>
<a href="#" class="social-icon" aria-label="Instagram"><v-icon size="24">mdi-instagram</v-icon></a>
<a href="#" class="social-icon" aria-label="Discord"><v-icon size="24">mdi-discord</v-icon></a>
<a href="#" class="social-icon" aria-label="TikTok"><v-icon size="24">mdi-music-note</v-icon></a>

View File

@ -97,15 +97,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
variant="outlined"
@ -247,15 +246,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
variant="outlined"
class="shares-input-field"
hide-details
@ -359,15 +357,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span v-if="activeTab === 'sell'" class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
variant="outlined"
class="shares-input-field"
hide-details
@ -392,7 +389,7 @@
<v-btn class="share-btn" @click="setMaxShares">{{ t('trade.max') }}</v-btn>
</div>
<p v-if="sellSharesExceedsMax" class="shares-exceeds-max-hint">
{{ t('trade.sharesExceedsMax', { max: maxAvailableSharesDisplay }) }}
{{ t('trade.sharesExceedsMax', { max: maxAvailableShares }) }}
</p>
<div v-if="activeTab === 'buy'" class="matching-info">
<v-icon size="14">mdi-information</v-icon>
@ -551,15 +548,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
density="compact"
@ -689,15 +685,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
density="compact"
@ -791,15 +786,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span v-if="activeTab === 'sell'" class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
density="compact"
@ -996,15 +990,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
density="compact"
@ -1134,15 +1127,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
variant="outlined"
@ -1236,15 +1228,14 @@
<div class="shares-header sell-shares-header">
<span class="label">{{ t('trade.shares') }}</span>
<span v-if="activeTab === 'sell'" class="max-shares-inline"
>{{ t('trade.maxShares') }}: {{ maxAvailableSharesDisplay }}</span
>{{ t('trade.maxShares') }}: {{ maxAvailableShares }}</span
>
</div>
<div class="shares-input-wrapper">
<v-text-field
:model-value="shares"
type="number"
:min="sharesInputMin"
:step="sharesInputStep"
min="1"
class="shares-input-field"
hide-details
density="compact"
@ -1721,9 +1712,7 @@ const limitType = ref<'Market' | 'Limit'>(prefs.limitType)
const expirationEnabled = ref(false)
const selectedOption = ref<'yes' | 'no'>(props.initialOption ?? 'no')
const limitPrice = ref(0.82) // 01
const shares = ref(20) // Buy Sell 6
const ORDER_SIZE_SCALE = 1_000_000
const ORDER_SIZE_MIN = 1 / ORDER_SIZE_SCALE
const shares = ref(20) //
const expirationTime = ref('5m') //
const EXPIRATION_VALUES = [
'5m',
@ -1847,44 +1836,21 @@ const currentOptionPositionShares = computed(() => {
return totalShares
})
/** 最大可卖出份额(保留 6 位小数) */
/** 最大可卖出份额 */
const maxAvailableShares = computed(() => {
return clampSellShares(currentOptionPositionShares.value)
return Math.floor(currentOptionPositionShares.value)
})
/** 展示用最大可卖份额 */
const maxAvailableSharesDisplay = computed(() =>
trimTrailingZeros(maxAvailableShares.value.toFixed(6)),
)
const sharesInputMin = computed(() => (activeTab.value === 'sell' ? ORDER_SIZE_MIN : 1))
const sharesInputStep = computed(() => (activeTab.value === 'sell' ? ORDER_SIZE_MIN : 1))
/** 将 shares 限制为正整数(>= 1Buy 模式使用 */
/** 将 shares 限制为正整数(>= 1 */
function clampShares(v: number): number {
const n = Math.floor(Number.isFinite(v) ? v : 1)
return Math.max(1, n)
}
/** Sell 模式:保留 6 位小数 */
function clampSellShares(v: number): number {
if (!Number.isFinite(v) || v <= 0) return 0
const scaled = Math.round(v * ORDER_SIZE_SCALE) / ORDER_SIZE_SCALE
return scaled < ORDER_SIZE_MIN ? 0 : scaled
}
function sharesToMicro(v: number): number {
return Math.round(v * ORDER_SIZE_SCALE)
}
//
// Sell
const setMaxShares = () => {
const maxShares = currentOptionPositionShares.value
if (activeTab.value === 'sell') {
shares.value = maxShares > 0 ? clampSellShares(maxShares) : 0
} else {
shares.value = maxShares > 0 ? clampShares(maxShares) : 0
}
}
function applyInitialOption(option: 'yes' | 'no') {
@ -2029,23 +1995,15 @@ const increasePrice = () => {
limitPrice.value = ALLOWED_LIMIT_PRICES[nextIdx] ?? limitPrice.value
}
/** 更新 sharesBuy 仅正整数Sell 支持 6 位小数 */
/** 仅在值为正整数时更新 shares */
function onSharesInput(v: unknown) {
const num = v == null ? NaN : Number(v)
if (!Number.isFinite(num)) return
if (activeTab.value === 'sell') {
if (num <= 0) return
const scaled = clampSellShares(num)
if (scaled <= 0) return
shares.value = scaled
return
}
const n = Math.floor(num)
if (n < 1 || num !== n) return
if (!Number.isFinite(num) || n < 1 || num !== n) return
shares.value = n
}
/** 键盘输入Sell 允许小数点 */
/** 只允许数字输入Shares 为正整数) */
function onSharesKeydown(e: KeyboardEvent) {
const key = e.key
const allowed = ['Backspace', 'Delete', 'Tab', 'ArrowLeft', 'ArrowRight', 'Home', 'End']
@ -2054,24 +2012,12 @@ function onSharesKeydown(e: KeyboardEvent) {
if (['a', 'c', 'v', 'x'].includes(key.toLowerCase())) return
}
if (key >= '0' && key <= '9') return
if (
activeTab.value === 'sell' &&
key === '.' &&
!String((e.target as HTMLInputElement)?.value ?? '').includes('.')
) {
return
}
e.preventDefault()
}
/** 粘贴Sell 接受小数Buy 仅正整数 */
/** 粘贴时只接受正整数 */
function onSharesPaste(e: ClipboardEvent) {
const text = e.clipboardData?.getData('text') ?? ''
if (activeTab.value === 'sell') {
const num = parseFloat(text)
if (!Number.isFinite(num) || num <= 0) e.preventDefault()
return
}
const num = parseInt(text, 10)
if (!Number.isFinite(num) || num < 1) e.preventDefault()
}
@ -2085,15 +2031,14 @@ const adjustShares = (amount: number) => {
const sellSharesExceedsMax = computed(
() =>
activeTab.value === 'sell' &&
maxAvailableShares.value > 0 &&
sharesToMicro(shares.value) > sharesToMicro(maxAvailableShares.value),
maxAvailableShares.value >= 0 &&
shares.value > maxAvailableShares.value,
)
// Sell使
const setSharesPercentage = (percentage: number) => {
const maxShares = currentOptionPositionShares.value
if (!Number.isFinite(maxShares) || maxShares <= 0) return
shares.value = clampSellShares((maxShares * percentage) / 100)
const maxShares = currentOptionPositionShares.value || 100
shares.value = clampShares(Math.round((maxShares * percentage) / 100))
}
// Market mode methods
@ -2215,8 +2160,8 @@ async function submitOrder() {
isNoAvailableSharesError.value = true
return
}
if (sharesToMicro(shares.value) > sharesToMicro(maxShares)) {
orderError.value = t('trade.sharesExceedsMax', { max: maxAvailableSharesDisplay.value })
if (shares.value > maxShares) {
orderError.value = t('trade.sharesExceedsMax', { max: maxShares })
isNoAvailableSharesError.value = false
return
}
@ -2234,23 +2179,8 @@ async function submitOrder() {
? parseExpirationTimestamp(expirationTime.value)
: 0
const rawSize =
isMarket && activeTab.value === 'buy'
? amount.value
: activeTab.value === 'sell'
? clampSellShares(shares.value)
: clampShares(shares.value)
if (activeTab.value === 'sell' && rawSize <= 0) {
orderError.value = t('trade.orderFailed')
isNoAvailableSharesError.value = false
return
}
const sizeValue = sharesToMicro(rawSize)
if (sizeValue <= 0) {
orderError.value = t('trade.orderFailed')
isNoAvailableSharesError.value = false
return
}
const rawSize = isMarket && activeTab.value === 'buy' ? amount.value : clampShares(shares.value)
const sizeValue = Math.round(rawSize * 1_000_000)
orderLoading.value = true
orderError.value = ''