Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions apps/telegram-miniapp/frontend/components/onboarding.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,25 @@
<span class="confirm-value demo-mode">Demo Mode</span>
</div>
</div>
<div class="risk-warning-box">
<div class="risk-warning-title">&#9888;&#65039; Risk Disclosure</div>
<ul class="risk-warning-list">
<li>AI agents can and do lose money</li>
<li>Past simulation results do not guarantee live performance</li>
<li>Only invest what you can afford to lose entirely</li>
</ul>
</div>
<p class="demo-notice">
Your agent will run in demo mode with simulated trades. No real funds required.
Your agent will start in <strong>simulation mode</strong> &mdash; no real funds required.
You can switch to live trading later after reviewing the
<a href="#" onclick="return false;" class="demo-notice-link">security guide</a>.
</p>
<div class="onboarding-nav">
<button class="onboarding-btn secondary" onclick="Onboarding.goToStep('strategy')">
Back
</button>
<button class="onboarding-btn primary" id="start-agent-btn">
Start Agent
Start Agent in Simulation
</button>
</div>
</div>
Expand Down
165 changes: 165 additions & 0 deletions apps/telegram-miniapp/frontend/components/security.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/**
* TON AI Agent – Security Component
*
* Manages:
* - Simulation mode banner (prominent indicator on agent dashboard)
* - Live trading confirmation modal with mandatory acknowledgment checklist
* - Safe defaults: simulation is always the default; live trading requires
* explicit multi-step opt-in
*
* @see Issue #314 - User-Facing Security Documentation and Safe Defaults
*/
(function () {
'use strict';

const { el, TG } = window.App;

// ============================================================================
// Constants
// ============================================================================

const STORAGE_KEY_LIVE = 'tonai_live_trading_enabled';

// ============================================================================
// Simulation Mode Banner
// ============================================================================

const SimulationBanner = {
/**
* Returns true when the user has explicitly enabled live trading this
* session and confirmed the risk acknowledgment checklist.
*/
isLiveMode() {
return localStorage.getItem(STORAGE_KEY_LIVE) === 'true';
},

/** Update the banner to reflect the current trading mode. */
update() {
const banner = el('simulation-mode-banner');
const switchBtn = el('switch-to-live-btn');
if (!banner) return;

if (this.isLiveMode()) {
banner.className = 'simulation-banner live-mode-banner';
banner.querySelector('.simulation-banner-text').textContent =
'LIVE TRADING — Real funds in use';
if (switchBtn) {
switchBtn.textContent = 'Back to Simulation';
switchBtn.className = 'simulation-switch-btn switch-to-sim-btn';
}
} else {
banner.className = 'simulation-banner';
banner.querySelector('.simulation-banner-text').textContent =
'SIMULATION MODE \u2014 No real funds at risk';
if (switchBtn) {
switchBtn.textContent = 'Switch to Live';
switchBtn.className = 'simulation-switch-btn';
}
}
},
};

// ============================================================================
// Live Trading Confirmation Modal
// ============================================================================

const LiveTradingModal = {
open() {
const modal = el('live-trading-modal');
if (!modal) return;

// Reset checklist state
['live-check-1', 'live-check-2', 'live-check-3'].forEach((id) => {
const checkbox = el(id);
if (checkbox) checkbox.checked = false;
});
this._updateConfirmButton();

modal.classList.remove('hidden');
TG.haptic.impact('medium');
},

close() {
const modal = el('live-trading-modal');
if (modal) modal.classList.add('hidden');
},

_updateConfirmButton() {
const allChecked = ['live-check-1', 'live-check-2', 'live-check-3'].every((id) => {
const cb = el(id);
return cb && cb.checked;
});
const confirmBtn = el('confirm-live-trading-btn');
if (confirmBtn) confirmBtn.disabled = !allChecked;
},

_enableLiveTrading() {
localStorage.setItem(STORAGE_KEY_LIVE, 'true');
this.close();
SimulationBanner.update();
TG.haptic.notify('success');
// Dispatch event so other components can react
window.dispatchEvent(new CustomEvent('tonai:live_trading_enabled'));
},

_switchBackToSimulation() {
TG.confirm(
'Switch back to Simulation Mode?\nYour agents will stop executing real trades.',
(ok) => {
if (!ok) return;
localStorage.removeItem(STORAGE_KEY_LIVE);
SimulationBanner.update();
TG.haptic.notify('success');
window.dispatchEvent(new CustomEvent('tonai:simulation_mode_enabled'));
}
);
},
};

// ============================================================================
// Wire Up Events
// ============================================================================

function setup() {
// Switch to Live button in the banner
const switchBtn = el('switch-to-live-btn');
if (switchBtn) {
switchBtn.addEventListener('click', () => {
if (SimulationBanner.isLiveMode()) {
LiveTradingModal._switchBackToSimulation();
} else {
LiveTradingModal.open();
}
});
}

// Close modal on overlay click or cancel
el('live-trading-modal-overlay')?.addEventListener('click', () => LiveTradingModal.close());
el('cancel-live-trading-btn')?.addEventListener('click', () => LiveTradingModal.close());

// Enable confirm button only when all checkboxes are ticked
['live-check-1', 'live-check-2', 'live-check-3'].forEach((id) => {
el(id)?.addEventListener('change', () => LiveTradingModal._updateConfirmButton());
});

// Confirm live trading
el('confirm-live-trading-btn')?.addEventListener('click', () => {
LiveTradingModal._enableLiveTrading();
});

// Apply initial banner state
SimulationBanner.update();
}

// ============================================================================
// Export
// ============================================================================

window.Security = { SimulationBanner, LiveTradingModal };

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setup);
} else {
setup();
}
})();
44 changes: 44 additions & 0 deletions apps/telegram-miniapp/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@
</button>
</header>

<!-- Simulation Mode Banner -->
<div id="simulation-mode-banner" class="simulation-banner">
<span class="simulation-banner-icon">&#9888;&#65039;</span>
<span class="simulation-banner-text">SIMULATION MODE &mdash; No real funds at risk</span>
<button class="simulation-switch-btn" id="switch-to-live-btn">Switch to Live</button>
</div>

<!-- Portfolio Value Card -->
<div class="portfolio-hero">
<div class="portfolio-label">Total Portfolio Value</div>
Expand Down Expand Up @@ -946,6 +953,41 @@ <h2 class="modal-title">Run Backtest</h2>
</div>
</div>

<!-- ============================== -->
<!-- Live Trading Confirmation Modal -->
<!-- ============================== -->
<div id="live-trading-modal" class="modal hidden">
<div class="modal-overlay" id="live-trading-modal-overlay"></div>
<div class="modal-sheet live-trading-modal-sheet">
<div class="modal-handle"></div>
<div class="modal-header">
<h2 class="modal-title live-trading-title">&#9888;&#65039; Enable Live Trading</h2>
</div>
<p class="live-trading-warning">
You are about to enable <strong>LIVE TRADING</strong>.<br>
Real funds will be used. Transactions on the blockchain are <strong>irreversible</strong>.
</p>
<div class="live-trading-checklist">
<label class="live-check-item">
<input type="checkbox" id="live-check-1" class="live-checkbox" />
<span>I understand I may lose money, including my entire investment</span>
</label>
<label class="live-check-item">
<input type="checkbox" id="live-check-2" class="live-checkbox" />
<span>I have verified my wallet address is correct</span>
</label>
<label class="live-check-item">
<input type="checkbox" id="live-check-3" class="live-checkbox" />
<span>I have set appropriate risk limits and reviewed the security guide</span>
</label>
</div>
<div class="live-trading-actions">
<button class="secondary-btn" id="cancel-live-trading-btn">Cancel</button>
<button class="danger-btn" id="confirm-live-trading-btn" disabled>Enable Live Trading</button>
</div>
</div>
</div>

</main>
</div>

Expand All @@ -962,6 +1004,8 @@ <h2 class="modal-title">Run Backtest</h2>
<script src="components/marketplace.js"></script>
<script src="components/backtesting.js"></script>
<script src="components/analytics.js"></script>
<!-- Security: simulation mode banner, live trading confirmation, risk warnings -->
<script src="components/security.js"></script>
<!-- Production extensions: i18n, wallet, safe-area, demo/live mode -->
<script src="production.js"></script>
</body>
Expand Down
Loading
Loading