ADD - expand VaultX fictional markets

Add 24 server-defined markets with unique symbols and broad price and supply ranges. Update the market DTO, UI, browser mocks, and contract coverage.
This commit is contained in:
smx.pusha
2026-08-18 09:32:02 +02:00
parent 7799ababae
commit 386a8d50eb
7 changed files with 572 additions and 34 deletions
+1
View File
@@ -7,6 +7,7 @@ export type CryptoMarket = {
high24h: string
id: string
issuedSupply: string
logo: string
low24h: string
name: string
price: string
@@ -14,6 +14,10 @@ const passwordProvider = readFileSync(
),
'utf8',
)
const config = readFileSync(
new URL('../../../../sky_phone/config/config.lua', import.meta.url),
'utf8',
)
describe('VaultX crypto app contracts', () => {
it('uses Sky UI without introducing Konsta components', () => {
@@ -56,6 +60,17 @@ describe('VaultX crypto app contracts', () => {
expect(source).not.toContain(':aria-label="t(\'refresh\')"')
})
it('exposes a broad fictional market with dedicated logo marks', () => {
const cryptoConfig = config.slice(
config.indexOf('Config.Crypto = {'),
config.indexOf('-- Server-only configuration'),
)
expect(cryptoConfig.match(/\n\s+Id = "/g)).toHaveLength(24)
expect(cryptoConfig.match(/\n\s+Logo = "/g)).toHaveLength(24)
expect(server).toContain('logo = config.Logo')
expect(source).toContain('detail.logo')
})
it('keeps all consequential calculations and state transitions on the server', () => {
expect(server).toContain(
'Bridge.Callbacks.Register("sky_phone:crypto:quote"',
+9 -9
View File
@@ -354,7 +354,7 @@ onMounted(() => void crypto.load())
<SkyScrollArea v-else-if="detail" with-tabbar padded>
<section class="detail-head">
<span class="coin large" :style="{ background: detail.color }">{{
detail.symbol[0]
detail.logo
}}</span>
<p>{{ detail.symbol }} · {{ detail.name }}</p>
<strong>{{ money(detail.price) }}</strong
@@ -406,7 +406,7 @@ onMounted(() => void crypto.load())
><strong>{{ privateMoney(detailHolding?.value ?? '0') }}</strong>
<div>
<span class="coin" :style="{ background: detail.color }">{{
detail.symbol[0]
detail.logo
}}</span
><span
><b>{{ detail.name }}</b
@@ -554,7 +554,7 @@ onMounted(() => void crypto.load())
<span
class="coin"
:style="{ background: market(holding.assetId)?.color }"
>{{ market(holding.assetId)?.symbol[0] }}</span
>{{ market(holding.assetId)?.logo }}</span
><span
><b>{{ market(holding.assetId)?.name }}</b
><small
@@ -597,7 +597,7 @@ onMounted(() => void crypto.load())
>
<div class="featured-market__top">
<span class="coin large" :style="{ background: topMover.color }">{{
topMover.symbol[0]
topMover.logo
}}</span>
<span>
<small>{{ t('markets.movers') }}</small>
@@ -639,7 +639,7 @@ onMounted(() => void crypto.load())
>
<div class="market-tile__top">
<span class="coin" :style="{ background: item.color }">{{
item.symbol[0]
item.logo
}}</span>
<span
><b>{{ item.symbol }}</b
@@ -665,14 +665,14 @@ onMounted(() => void crypto.load())
<em>{{ t('markets.today') }}</em>
</div>
<button
v-for="item in [...markets].sort(
(a, b) => b.changePercent - a.changePercent,
)"
v-for="item in [...markets]
.sort((a, b) => b.changePercent - a.changePercent)
.slice(0, 5)"
:key="item.id"
@click="openMarket(item)"
>
<span class="coin" :style="{ background: item.color }">{{
item.symbol[0]
item.logo
}}</span
><span
><b>{{ item.name }}</b
+261 -24
View File
@@ -202,49 +202,286 @@ let cryptoCashBalance = 18420
let cryptoQuote = null
let nextCryptoActivityId = 5
const cryptoPassword = 'VaultX123!'
function createCryptoMarket({
changePercent,
color,
id,
logo,
name,
price,
supply,
symbol,
}) {
const numericPrice = Number(price)
const direction = changePercent >= 0 ? 1 : -1
const rawSparkline = Array.from({ length: 12 }, (_, index) => {
const wave = Math.sin((index + id.length) * 1.17) * 0.13
return 0.28 + index * 0.045 * direction + wave
})
const minimum = Math.min(...rawSparkline)
const maximum = Math.max(...rawSparkline)
const span = Math.max(0.01, maximum - minimum)
return {
changePercent,
color,
enabled: true,
high24h: (numericPrice * (1.025 + Math.abs(changePercent) / 100)).toFixed(
2,
),
id,
issuedSupply: String(supply),
logo,
low24h: (
numericPrice * Math.max(0.1, 0.975 - Math.abs(changePercent) / 100)
).toFixed(2),
name,
price,
sparkline: rawSparkline.map((value) => (value - minimum) / span),
symbol,
treasuryAvailable: String(Math.floor(supply * 0.82)),
}
}
const cryptoMarkets = [
{
createCryptoMarket({
id: 'aurora',
symbol: 'AUR',
name: 'Aurora',
logo: '◈',
color: '#25d9ad',
price: '128.50',
changePercent: 4.82,
enabled: true,
high24h: '132.80',
low24h: '119.40',
issuedSupply: '1000000',
treasuryAvailable: '849882.5',
sparkline: [0.12, 0.24, 0.2, 0.38, 0.51, 0.46, 0.68, 0.62, 0.81, 0.92],
},
{
supply: 1000000,
}),
createCryptoMarket({
id: 'vertex',
symbol: 'VTX',
name: 'Vertex',
logo: '◆',
color: '#4d8cff',
price: '42.75',
changePercent: -1.37,
enabled: true,
high24h: '46.30',
low24h: '40.90',
issuedSupply: '2500000',
treasuryAvailable: '2099914.75',
sparkline: [0.84, 0.72, 0.76, 0.61, 0.69, 0.52, 0.46, 0.41, 0.35, 0.39],
},
{
supply: 2500000,
}),
createCryptoMarket({
id: 'ember',
symbol: 'EMB',
name: 'Ember',
logo: '✦',
color: '#ff9d54',
price: '9.80',
changePercent: 7.21,
enabled: true,
high24h: '10.24',
low24h: '8.76',
issuedSupply: '8000000',
treasuryAvailable: '6800000',
sparkline: [0.08, 0.11, 0.18, 0.26, 0.23, 0.4, 0.55, 0.64, 0.79, 0.93],
},
supply: 8000000,
}),
createCryptoMarket({
id: 'titan',
symbol: 'TTN',
name: 'Titan',
logo: '⬢',
color: '#8f7cff',
price: '4250000.00',
changePercent: 1.18,
supply: 21000,
}),
createCryptoMarket({
id: 'crown',
symbol: 'CRN',
name: 'Crown',
logo: '♛',
color: '#f6c453',
price: '1280000.00',
changePercent: -0.42,
supply: 64000,
}),
createCryptoMarket({
id: 'atlas',
symbol: 'ATL',
name: 'Atlas',
logo: '▲',
color: '#e66f9e',
price: '120000.00',
changePercent: 2.66,
supply: 250000,
}),
createCryptoMarket({
id: 'zenith',
symbol: 'ZNT',
name: 'Zenith',
logo: '✥',
color: '#6b8cff',
price: '680000.00',
changePercent: 0.84,
supply: 125000,
}),
createCryptoMarket({
id: 'celestium',
symbol: 'CLS',
name: 'Celestium',
logo: '✧',
color: '#8ad8ff',
price: '84500.00',
changePercent: -2.14,
supply: 420000,
}),
createCryptoMarket({
id: 'orbit',
symbol: 'ORB',
name: 'Orbit',
logo: '◎',
color: '#57c7ff',
price: '12440.00',
changePercent: 3.06,
supply: 900000,
}),
createCryptoMarket({
id: 'helix',
symbol: 'HLX',
name: 'Helix',
logo: '⌁',
color: '#c47cff',
price: '5820.00',
changePercent: 5.42,
supply: 1400000,
}),
createCryptoMarket({
id: 'nova',
symbol: 'NVA',
name: 'Nova',
logo: '✺',
color: '#ff6f91',
price: '3250.00',
changePercent: -4.72,
supply: 2200000,
}),
createCryptoMarket({
id: 'prism',
symbol: 'PRM',
name: 'Prism',
logo: '◇',
color: '#5ee1c4',
price: '745.00',
changePercent: 6.18,
supply: 3800000,
}),
createCryptoMarket({
id: 'quantum',
symbol: 'QTM',
name: 'Quantum',
logo: '◉',
color: '#7a9cff',
price: '214.00',
changePercent: -3.22,
supply: 5200000,
}),
createCryptoMarket({
id: 'drift',
symbol: 'DRF',
name: 'Drift',
logo: '≋',
color: '#4ecdc4',
price: '76.00',
changePercent: 2.47,
supply: 12000000,
}),
createCryptoMarket({
id: 'pulse',
symbol: 'PLS',
name: 'Pulse',
logo: '◍',
color: '#ff5f7f',
price: '18.40',
changePercent: 8.84,
supply: 18000000,
}),
createCryptoMarket({
id: 'moss',
symbol: 'MOS',
name: 'Moss',
logo: '♧',
color: '#64c987',
price: '4.60',
changePercent: -5.06,
supply: 36000000,
}),
createCryptoMarket({
id: 'flux',
symbol: 'FLX',
name: 'Flux',
logo: 'ϟ',
color: '#a777e3',
price: '2.38',
changePercent: 11.24,
supply: 44000000,
}),
createCryptoMarket({
id: 'tide',
symbol: 'TDE',
name: 'Tide',
logo: '≈',
color: '#46a6ff',
price: '1.25',
changePercent: 3.78,
supply: 60000000,
}),
createCryptoMarket({
id: 'spark',
symbol: 'SPK',
name: 'Spark',
logo: '✹',
color: '#ffb84d',
price: '0.74',
changePercent: 14.62,
supply: 85000000,
}),
createCryptoMarket({
id: 'pebble',
symbol: 'PBL',
name: 'Pebble',
logo: '●',
color: '#9aa8b8',
price: '0.22',
changePercent: -7.36,
supply: 150000000,
}),
createCryptoMarket({
id: 'nano',
symbol: 'NNO',
name: 'Nano',
logo: 'η',
color: '#5bd6ff',
price: '0.09',
changePercent: 18.44,
supply: 260000000,
}),
createCryptoMarket({
id: 'shard',
symbol: 'SHD',
name: 'Shard',
logo: '△',
color: '#70e0d0',
price: '0.05',
changePercent: -12.18,
supply: 420000000,
}),
createCryptoMarket({
id: 'pixel',
symbol: 'PXL',
name: 'Pixel',
logo: '▦',
color: '#ff78c8',
price: '0.03',
changePercent: 21.52,
supply: 700000000,
}),
createCryptoMarket({
id: 'dust',
symbol: 'DST',
name: 'Dust',
logo: '·',
color: '#c4b5a5',
price: '0.01',
changePercent: -9.74,
supply: 1200000000,
}),
]
let cryptoProfile = {
createdAt: Date.now() - 42 * 86400000,
+9 -1
View File
@@ -115,9 +115,17 @@ function verifyBrowserTestData(dataByEndpoint) {
expectItems(dataByEndpoint.get('health:overview').days, 'health history', 7)
const crypto = dataByEndpoint.get('crypto:bootstrap')
expectItems(crypto.markets, 'crypto markets', 3)
expectItems(crypto.markets, 'crypto markets', 24)
assert.equal(typeof crypto.profile.priceAlerts, 'boolean')
assert.equal(typeof crypto.markets[0].issuedSupply, 'string')
assert(crypto.markets.every((market) => typeof market.logo === 'string'))
assert(
Math.max(...crypto.markets.map((market) => Number(market.price))) >=
1000000,
)
assert(
Math.min(...crypto.markets.map((market) => Number(market.price))) <= 0.01,
)
expectItems(
dataByEndpoint.get('billing:list').invoices,
'billing invoices',
+276
View File
@@ -681,6 +681,7 @@ Config.Crypto = {
Symbol = "AUR",
Name = "Aurora",
Color = "#25d9ad",
Logo = "",
InitialPrice = 12850,
MinimumPrice = 3000,
MaximumPrice = 80000,
@@ -693,6 +694,7 @@ Config.Crypto = {
Symbol = "VTX",
Name = "Vertex",
Color = "#4d8cff",
Logo = "",
InitialPrice = 4275,
MinimumPrice = 800,
MaximumPrice = 30000,
@@ -705,6 +707,7 @@ Config.Crypto = {
Symbol = "EMB",
Name = "Ember",
Color = "#ff9d54",
Logo = "",
InitialPrice = 980,
MinimumPrice = 100,
MaximumPrice = 12000,
@@ -712,6 +715,279 @@ Config.Crypto = {
IssuedSupply = 8000000,
TreasuryInventory = 6800000,
},
{
Id = "titan",
Symbol = "TTN",
Name = "Titan",
Color = "#8f7cff",
Logo = "",
InitialPrice = 425000000,
MinimumPrice = 100000000,
MaximumPrice = 900000000,
VolatilityBasisPoints = 65,
IssuedSupply = 21000,
TreasuryInventory = 17500,
},
{
Id = "crown",
Symbol = "CRN",
Name = "Crown",
Color = "#f6c453",
Logo = "",
InitialPrice = 128000000,
MinimumPrice = 25000000,
MaximumPrice = 350000000,
VolatilityBasisPoints = 85,
IssuedSupply = 64000,
TreasuryInventory = 51000,
},
{
Id = "atlas",
Symbol = "ATL",
Name = "Atlas",
Color = "#e66f9e",
Logo = "",
InitialPrice = 12000000,
MinimumPrice = 3000000,
MaximumPrice = 30000000,
VolatilityBasisPoints = 110,
IssuedSupply = 250000,
TreasuryInventory = 205000,
},
{
Id = "zenith",
Symbol = "ZNT",
Name = "Zenith",
Color = "#6b8cff",
Logo = "",
InitialPrice = 68000000,
MinimumPrice = 18000000,
MaximumPrice = 160000000,
VolatilityBasisPoints = 95,
IssuedSupply = 125000,
TreasuryInventory = 99000,
},
{
Id = "celestium",
Symbol = "CLS",
Name = "Celestium",
Color = "#8ad8ff",
Logo = "",
InitialPrice = 8450000,
MinimumPrice = 1500000,
MaximumPrice = 22000000,
VolatilityBasisPoints = 125,
IssuedSupply = 420000,
TreasuryInventory = 340000,
},
{
Id = "orbit",
Symbol = "ORB",
Name = "Orbit",
Color = "#57c7ff",
Logo = "",
InitialPrice = 1244000,
MinimumPrice = 250000,
MaximumPrice = 3500000,
VolatilityBasisPoints = 145,
IssuedSupply = 900000,
TreasuryInventory = 735000,
},
{
Id = "helix",
Symbol = "HLX",
Name = "Helix",
Color = "#c47cff",
Logo = "",
InitialPrice = 582000,
MinimumPrice = 90000,
MaximumPrice = 1600000,
VolatilityBasisPoints = 175,
IssuedSupply = 1400000,
TreasuryInventory = 1140000,
},
{
Id = "nova",
Symbol = "NVA",
Name = "Nova",
Color = "#ff6f91",
Logo = "",
InitialPrice = 325000,
MinimumPrice = 50000,
MaximumPrice = 900000,
VolatilityBasisPoints = 210,
IssuedSupply = 2200000,
TreasuryInventory = 1800000,
},
{
Id = "prism",
Symbol = "PRM",
Name = "Prism",
Color = "#5ee1c4",
Logo = "",
InitialPrice = 74500,
MinimumPrice = 12000,
MaximumPrice = 240000,
VolatilityBasisPoints = 195,
IssuedSupply = 3800000,
TreasuryInventory = 3100000,
},
{
Id = "quantum",
Symbol = "QTM",
Name = "Quantum",
Color = "#7a9cff",
Logo = "",
InitialPrice = 21400,
MinimumPrice = 3500,
MaximumPrice = 85000,
VolatilityBasisPoints = 230,
IssuedSupply = 5200000,
TreasuryInventory = 4250000,
},
{
Id = "drift",
Symbol = "DRF",
Name = "Drift",
Color = "#4ecdc4",
Logo = "",
InitialPrice = 7600,
MinimumPrice = 900,
MaximumPrice = 30000,
VolatilityBasisPoints = 280,
IssuedSupply = 12000000,
TreasuryInventory = 9800000,
},
{
Id = "pulse",
Symbol = "PLS",
Name = "Pulse",
Color = "#ff5f7f",
Logo = "",
InitialPrice = 1840,
MinimumPrice = 200,
MaximumPrice = 9000,
VolatilityBasisPoints = 320,
IssuedSupply = 18000000,
TreasuryInventory = 14800000,
},
{
Id = "moss",
Symbol = "MOS",
Name = "Moss",
Color = "#64c987",
Logo = "",
InitialPrice = 460,
MinimumPrice = 60,
MaximumPrice = 2400,
VolatilityBasisPoints = 360,
IssuedSupply = 36000000,
TreasuryInventory = 29500000,
},
{
Id = "flux",
Symbol = "FLX",
Name = "Flux",
Color = "#a777e3",
Logo = "ϟ",
InitialPrice = 238,
MinimumPrice = 35,
MaximumPrice = 1400,
VolatilityBasisPoints = 390,
IssuedSupply = 44000000,
TreasuryInventory = 36000000,
},
{
Id = "tide",
Symbol = "TDE",
Name = "Tide",
Color = "#46a6ff",
Logo = "",
InitialPrice = 125,
MinimumPrice = 20,
MaximumPrice = 700,
VolatilityBasisPoints = 410,
IssuedSupply = 60000000,
TreasuryInventory = 49000000,
},
{
Id = "spark",
Symbol = "SPK",
Name = "Spark",
Color = "#ffb84d",
Logo = "",
InitialPrice = 74,
MinimumPrice = 12,
MaximumPrice = 420,
VolatilityBasisPoints = 430,
IssuedSupply = 85000000,
TreasuryInventory = 70000000,
},
{
Id = "pebble",
Symbol = "PBL",
Name = "Pebble",
Color = "#9aa8b8",
Logo = "",
InitialPrice = 22,
MinimumPrice = 4,
MaximumPrice = 160,
VolatilityBasisPoints = 470,
IssuedSupply = 150000000,
TreasuryInventory = 124000000,
},
{
Id = "nano",
Symbol = "NNO",
Name = "Nano",
Color = "#5bd6ff",
Logo = "η",
InitialPrice = 9,
MinimumPrice = 1,
MaximumPrice = 85,
VolatilityBasisPoints = 520,
IssuedSupply = 260000000,
TreasuryInventory = 214000000,
},
{
Id = "shard",
Symbol = "SHD",
Name = "Shard",
Color = "#70e0d0",
Logo = "",
InitialPrice = 5,
MinimumPrice = 1,
MaximumPrice = 55,
VolatilityBasisPoints = 560,
IssuedSupply = 420000000,
TreasuryInventory = 345000000,
},
{
Id = "pixel",
Symbol = "PXL",
Name = "Pixel",
Color = "#ff78c8",
Logo = "",
InitialPrice = 3,
MinimumPrice = 1,
MaximumPrice = 32,
VolatilityBasisPoints = 610,
IssuedSupply = 700000000,
TreasuryInventory = 580000000,
},
{
Id = "dust",
Symbol = "DST",
Name = "Dust",
Color = "#c4b5a5",
Logo = "·",
InitialPrice = 1,
MinimumPrice = 1,
MaximumPrice = 18,
VolatilityBasisPoints = 680,
IssuedSupply = 1200000000,
TreasuryInventory = 990000000,
},
},
}
+1
View File
@@ -390,6 +390,7 @@ local function market_dtos()
symbol = config.Symbol,
name = config.Name,
color = config.Color,
logo = config.Logo,
price = decimal_string(price, Config.Crypto.PriceScale),
changePercent = first > 0 and ((price - first) / first) * 100 or 0,
enabled = row.status == "active",