mirror of
https://github.com/basicswap/basicswap.git
synced 2025-12-31 09:31:39 +01:00
* AMM: use_balance_bidding + BCH (USD) price fix + Various fixes. * AMM: Fixed NMC, DOGE, DCR (USD) price.
2039 lines
131 KiB
HTML
2039 lines
131 KiB
HTML
{% include 'header.html' %}
|
||
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, input_arrow_down_svg, arrow_right_svg, input_time_svg %}
|
||
|
||
<script>
|
||
window.ammTablesConfig = {
|
||
stateData: {{ state_data|tojson|safe }},
|
||
configData: {{ config_data|tojson|safe }},
|
||
configContent: {{ config_content|tojson|safe }}
|
||
};
|
||
</script>
|
||
|
||
<section class="py-3 px-4 mt-6">
|
||
<div class="lg:container lg:px-4 mx-auto">
|
||
<div class="relative py-8 px-16 bg-coolGray-900 dark:bg-gray-800 rounded-md overflow-hidden">
|
||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||
<div class="w-full md:w-1/2 p-3">
|
||
<h2 class="text-2xl font-bold text-white tracking-tighter">Automated Market Maker</h2>
|
||
<p class="hidden lg:flex mt-3 font-normal text-coolGray-200 dark:text-white">
|
||
Automatically create offers and bids based on your configuration.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<div class="xl:container mx-auto">
|
||
{% include 'inc_messages.html' %}
|
||
|
||
<section>
|
||
<div class="lg:container px-4 mx-auto">
|
||
<div class="flex flex-wrap -mx-4">
|
||
<div class="w-full px-4 mb-8">
|
||
<div class="p-6 px-0 lg:px-6 bg-white dark:bg-gray-800 rounded-xl shadow-md">
|
||
<div class="flex sm:pr-6 lg:pr-0 justify-end items-center">
|
||
<div class="flex space-x-2">
|
||
<button id="add-new-offer-btn" class="flex items-center px-4 py-2.5 bg-green-600 hover:bg-green-700 border-green-600 font-medium text-sm text-white border rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
||
</svg>
|
||
Add Offer
|
||
</button>
|
||
{% if debug_ui_mode %}
|
||
<button id="add-new-bid-btn" class="flex items-center px-4 py-2.5 bg-green-600 hover:bg-green-700 border-green-600 font-medium text-sm text-white border rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
||
</svg>
|
||
Add Bid
|
||
</button>
|
||
{% endif %}
|
||
<button id="refreshAmmTables" class="flex items-center px-4 py-2.5 bg-blue-600 hover:bg-blue-600 border-blue-500 font-medium text-sm text-white border rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||
</svg>
|
||
Refresh
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="border-b pb-5 border-gray-200 dark:border-gray-500">
|
||
<ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400" id="ammTabs" role="tablist">
|
||
<li class="mr-2" role="presentation">
|
||
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="offers-tab" data-tabs-target="#offers-content" type="button" role="tab" aria-controls="offers" aria-selected="true">
|
||
Offers <span class="text-gray-500 dark:text-gray-400" id="offers-count">(0)</span>
|
||
</button>
|
||
</li>
|
||
{% if debug_ui_mode %}
|
||
<li class="mr-2" role="presentation">
|
||
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="bids-tab" data-tabs-target="#bids-content" type="button" role="tab" aria-controls="bids" aria-selected="false">
|
||
Bids <span class="text-gray-500 dark:text-gray-400" id="bids-count">(0)</span>
|
||
</button>
|
||
</li>
|
||
{% endif %}
|
||
</ul>
|
||
</div>
|
||
|
||
<div id="ammTabContent">
|
||
<div class="block" id="offers-content" role="tabpanel" aria-labelledby="offers-tab">
|
||
<div class="overflow-x-auto">
|
||
<table class="w-full min-w-max">
|
||
<thead class="uppercase">
|
||
<tr>
|
||
<th class="p-0">
|
||
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Name</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Amount & Min</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Rate & Receive</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Settings</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||
</div>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="amm-offers-body">
|
||
<tr>
|
||
<td colspan="7" class="py-4 px-4 text-center text-gray-500 dark:text-gray-400">
|
||
Loading offers data...
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="hidden" id="bids-content" role="tabpanel" aria-labelledby="bids-tab">
|
||
<div class="overflow-x-auto">
|
||
<table class="w-full min-w-max">
|
||
<thead class="uppercase">
|
||
<tr>
|
||
<th class="p-0">
|
||
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Name</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Amount & Balance</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Rate & Send</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Settings</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||
</div>
|
||
</th>
|
||
<th class="p-0">
|
||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||
</div>
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="amm-bids-body">
|
||
<tr>
|
||
<td colspan="7" class="py-4 px-4 text-center text-gray-500 dark:text-gray-400">
|
||
Loading bids data...
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="lg:container px-4 mx-auto">
|
||
<div class="flex flex-wrap -mx-4">
|
||
<div class="w-full lg:w-1/2 px-4 mb-8">
|
||
<div class="p-6 bg-white dark:bg-gray-800 rounded-xl shadow-md">
|
||
<h3 class="mb-4 text-xl font-bold text-coolGray-900 dark:text-white">Control</h3>
|
||
<form method="post" autocomplete="off">
|
||
<div class="mb-4">
|
||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Status</label>
|
||
<div class="flex items-center">
|
||
<span class="inline-flex items-center px-3 py-2 text-sm font-medium rounded-lg
|
||
{% if current_status == 'running' %}
|
||
bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300
|
||
{% else %}
|
||
bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300
|
||
{% endif %}
|
||
">
|
||
{{ current_status|capitalize }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="startup-feedback" class="mb-4 hidden">
|
||
<div class="bg-blue-50 dark:bg-blue-900 border border-blue-200 dark:border-blue-700 rounded-lg p-4">
|
||
<div class="flex items-center">
|
||
<svg id="startup-spinner" class="animate-spin -ml-1 mr-3 h-5 w-5 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||
</svg>
|
||
<div>
|
||
<h4 class="text-sm font-medium text-blue-800 dark:text-blue-200" id="startup-title">Starting AMM...</h4>
|
||
<p class="text-sm text-blue-700 dark:text-blue-300 mt-1" id="startup-message">Validating configuration and checking requirements...</p>
|
||
</div>
|
||
</div>
|
||
<div id="startup-progress" class="mt-3">
|
||
<div class="bg-blue-200 dark:bg-blue-800 rounded-full h-2">
|
||
<div id="startup-progress-bar" class="bg-blue-600 h-2 rounded-full transition-all duration-300" style="width: 0%"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="mb-4">
|
||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Host</label>
|
||
<input type="text" name="host" value="{{ amm_host }}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Port</label>
|
||
<input type="number" name="port" value="{{ amm_port }}" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
{% else %}
|
||
<input type="hidden" name="host" value="{{ amm_host }}">
|
||
<input type="hidden" name="port" value="{{ amm_port }}">
|
||
{% endif %}
|
||
|
||
<div class="mb-4">
|
||
<label class="flex items-center">
|
||
<input type="checkbox" id="autostart-amm" name="autostart" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" {% if amm_autostart %}checked{% endif %}>
|
||
<span class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Auto-start with BasicSwap</span>
|
||
</label>
|
||
<small class="text-xs text-gray-500 dark:text-gray-400 ml-6">Automatically start AMM when BasicSwap starts</small>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="mb-4">
|
||
<label class="flex items-center">
|
||
<input type="checkbox" id="debug-mode" name="debug" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" {% if amm_debug %}checked{% endif %}>
|
||
<span class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Debug Mode</span>
|
||
</label>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="flex flex-wrap gap-2">
|
||
<button type="submit" name="start" value="1" class="px-4 py-2.5 bg-green-600 hover:bg-green-700 font-medium text-sm text-white border border-green-600 rounded-md shadow-button focus:ring-0 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed"
|
||
{% if current_status == 'running' %}disabled{% endif %}>
|
||
Start AMM
|
||
</button>
|
||
<button type="submit" name="stop" value="1" class="px-4 py-2.5 bg-red-900 hover:bg-red-700 font-medium text-sm text-white border border-red-800 rounded-md shadow-button focus:ring-0 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed"
|
||
{% if current_status == 'stopped' %}disabled{% endif %}>
|
||
Stop
|
||
</button>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600">
|
||
<h4 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Process Management</h4>
|
||
<div class="flex flex-wrap gap-2">
|
||
<button type="submit" name="force_start" value="1" class="px-4 py-2.5 bg-yellow-500 hover:bg-yellow-600 font-medium text-sm text-white border border-yellow-500 rounded-md shadow-button focus:ring-0 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed" title="Kill existing AMM processes and start fresh">
|
||
Force Start
|
||
</button>
|
||
<button type="submit" name="check_processes" value="1" class="inline-flex items-center px-3 py-1.5 bg-blue-500 hover:bg-blue-600 text-white text-xs font-medium rounded-md shadow-sm focus:ring-0 focus:outline-none">
|
||
Check Processes
|
||
</button>
|
||
<button type="submit" name="kill_orphans" value="1" class="inline-flex items-center px-3 py-1.5 bg-red-500 hover:bg-red-600 text-white text-xs font-medium rounded-md shadow-sm focus:ring-0 focus:outline-none" title="Kill orphaned AMM processes">
|
||
Kill Orphans
|
||
</button>
|
||
</div>
|
||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
||
Use "Check Processes" to see running AMM processes. Use "Kill Orphans" to clean up duplicate processes. Use "Force Start" to automatically clean up and start fresh.
|
||
</p>
|
||
</div>
|
||
{% endif %}
|
||
</form>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="mt-6 p-6 bg-white dark:bg-gray-800 rounded-xl shadow-md">
|
||
<h3 class="mb-4 text-xl font-bold text-coolGray-900 dark:text-white">Files</h3>
|
||
<div class="mb-4">
|
||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||
<strong>AMM Directory:</strong> {{ amm_dir }}
|
||
</p>
|
||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||
<strong>Config File:</strong> {{ config_path }}
|
||
{% if config_exists %}
|
||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300">Exists</span>
|
||
{% else %}
|
||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300">Missing</span>
|
||
{% endif %}
|
||
</p>
|
||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||
<strong>State File:</strong> {{ state_path }}
|
||
{% if state_exists %}
|
||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300">Exists</span>
|
||
{% else %}
|
||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300">Will be created</span>
|
||
{% endif %}
|
||
</p>
|
||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||
<strong>Script Module:</strong> {{ script_path }}
|
||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium rounded-full bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300">Integrated</span>
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="w-full lg:w-1/2 px-4 mb-8">
|
||
<div class="p-6 bg-white dark:bg-gray-800 rounded-xl shadow-md">
|
||
<h3 class="mb-4 text-xl font-bold text-coolGray-900 dark:text-white">Configuration</h3>
|
||
|
||
<div class="mb-4 border-b pb-5 border-gray-200 dark:border-gray-500">
|
||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400" role="tablist">
|
||
<li class="mr-2" role="presentation">
|
||
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="json-tab" type="button" role="tab" aria-controls="json-content" aria-selected="true">JSON View</button>
|
||
</li>
|
||
<li class="mr-2" role="presentation">
|
||
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="settings-tab" type="button" role="tab" aria-controls="settings-content" aria-selected="false">Global Settings</button>
|
||
</li>
|
||
<li class="mr-2" role="presentation">
|
||
<button class="{% if not debug_ui_mode %}hidden{% endif %} inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="overview-tab" type="button" role="tab" aria-controls="overview-content" aria-selected="false">Settings FAQ</button>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<form method="post" autocomplete="off">
|
||
|
||
<div id="configTabContent">
|
||
<div class="block" id="json-content" role="tabpanel" aria-labelledby="json-tab">
|
||
<div class="mb-4">
|
||
<label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Config File (JSON)</label>
|
||
<textarea name="config_content" id="config_content" rows="20" class="font-mono bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" {% if not debug_ui_mode %}readonly{% endif %}>{{ config_content }}</textarea>
|
||
|
||
|
||
<div class="flex flex-col space-y-4 mt-4">
|
||
{% if debug_mode %}
|
||
<div class="flex items-center">
|
||
<input type="checkbox" id="include_bids" name="include_bids" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600">
|
||
<label for="include_bids" class="ml-2 block text-sm text-gray-900 dark:text-gray-300">Include bids in default config</label>
|
||
</div>
|
||
{% endif %}
|
||
<div class="flex justify-between">
|
||
<button type="submit" name="create_default" value="true" id="create_default_btn" class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Create Default Config</button>
|
||
{% if debug_ui_mode %}
|
||
<button type="submit" name="save_config" value="true" id="save_config_btn" class="px-4 py-2.5 bg-green-600 hover:bg-green-700 font-medium text-sm text-white border border-green-600 rounded-md shadow-button focus:ring-0 focus:outline-none">Save Config</button>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="hidden" id="settings-content" role="tabpanel" aria-labelledby="settings-tab">
|
||
<form method="post" id="global-settings-form">
|
||
<div class="space-y-6">
|
||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Global Settings</h4>
|
||
|
||
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
||
<h5 class="mb-3 text-base font-medium text-gray-900 dark:text-white">Timing Settings</h5>
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="min_seconds_between_offers" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Min Seconds Between Offers</label>
|
||
<input type="number" id="min_seconds_between_offers" name="min_seconds_between_offers" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
<div>
|
||
<label for="max_seconds_between_offers" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Max Seconds Between Offers</label>
|
||
<input type="number" id="max_seconds_between_offers" name="max_seconds_between_offers" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
<div>
|
||
<label for="main_loop_delay" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Main Loop Delay (seconds)</label>
|
||
<input type="number" id="main_loop_delay" name="main_loop_delay" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
{% if debug_ui_mode %}
|
||
<div>
|
||
<label for="min_seconds_between_bids" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Min Seconds Between Bids</label>
|
||
<input type="number" id="min_seconds_between_bids" name="min_seconds_between_bids" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
<div>
|
||
<label for="max_seconds_between_bids" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Max Seconds Between Bids</label>
|
||
<input type="number" id="max_seconds_between_bids" name="max_seconds_between_bids" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
<div>
|
||
<label for="prune_state_delay" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Prune State Delay (seconds)</label>
|
||
<input type="number" id="prune_state_delay" name="prune_state_delay" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
<div>
|
||
<label for="prune_state_after_seconds" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Prune State After (seconds)</label>
|
||
<input type="number" id="prune_state_after_seconds" name="prune_state_after_seconds" min="1" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
{% else %}
|
||
<input type="hidden" id="min_seconds_between_bids" name="min_seconds_between_bids">
|
||
<input type="hidden" id="max_seconds_between_bids" name="max_seconds_between_bids">
|
||
<input type="hidden" id="prune_state_delay" name="prune_state_delay">
|
||
<input type="hidden" id="prune_state_after_seconds" name="prune_state_after_seconds">
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
||
<h5 class="mb-3 text-base font-medium text-gray-900 dark:text-white">Settings</h5>
|
||
<div class="grid grid-cols-1 gap-4">
|
||
<div>
|
||
<label for="auth" class="block mb-2 text-sm font-medium text-gray-700 dark:text-gray-300">Authentication</label>
|
||
<input type="text" id="auth" name="auth" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex justify-end">
|
||
<button type="submit" name="save_global_settings" value="true" class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Save Settings</button>
|
||
</div>
|
||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="hidden" id="overview-content" role="tabpanel" aria-labelledby="overview-tab">
|
||
{% if debug_ui_mode %}
|
||
<div class="mb-4">
|
||
<button type="button" class="collapsible-header flex items-center justify-between w-full p-3 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none" data-target="general-settings-content">
|
||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Settings</h4>
|
||
<svg class="w-5 h-5 transition-transform transform text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
</button>
|
||
<div id="general-settings-content" class="collapsible-content mt-2 bg-gray-50 dark:bg-gray-700 p-4 rounded-lg hidden overflow-hidden">
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Offer Timing</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">min_seconds_between_offers:</span> Minimum delay between creating offers (default: 15)</li>
|
||
<li class="break-words"><span class="font-semibold">max_seconds_between_offers:</span> Maximum delay between creating offers (default: 60)</li>
|
||
</ul>
|
||
</div>
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Bid Timing</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">min_seconds_between_bids:</span> Minimum delay between creating bids (default: 15)</li>
|
||
<li class="break-words"><span class="font-semibold">max_seconds_between_bids:</span> Maximum delay between creating bids (default: 60)</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-4 grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4">
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Script Behavior</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">main_loop_delay:</span> Seconds between main loop iterations (10-1000)</li>
|
||
<li class="break-words"><span class="font-semibold">auth:</span> Basicswap API auth string, e.g., "admin:password". Ignored if client auth is not enabled.</li>
|
||
<li class="break-words"><span class="font-semibold">wallet_port_override:</span> If needed, uncomment and set to override wallet API port (for testing only)</li>
|
||
</ul>
|
||
</div>
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>State Management</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">prune_state_delay:</span> Seconds between pruning old state data (0 to disable)</li>
|
||
<li class="break-words"><span class="font-semibold">prune_state_after_seconds:</span> How long to keep old state data (default: 7 days)</li>
|
||
</ul>
|
||
</div>
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Automation Strategies</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">accept_all:</span> Auto-accept all bids (default)</li>
|
||
<li class="break-words"><span class="font-semibold">accept_known:</span> Only accept bids from known identities</li>
|
||
<li class="break-words"><span class="font-semibold">none:</span> Manual acceptance required</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<button type="button" class="collapsible-header flex items-center justify-between w-full p-3 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none" data-target="offer-templates-content">
|
||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Offer Templates</h4>
|
||
<svg class="w-5 h-5 transition-transform transform text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
</button>
|
||
<div id="offer-templates-content" class="collapsible-content mt-2 bg-gray-50 dark:bg-gray-700 p-4 rounded-lg hidden overflow-hidden">
|
||
<p class="text-sm font-bold text-gray-600 dark:text-gray-400 mb-3 break-words">
|
||
Offer templates define how the AMM creates offers. Each template can have the following settings:
|
||
</p>
|
||
|
||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Required Settings</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">name:</span> Template name, must be unique</li>
|
||
<li class="break-words"><span class="font-semibold">enabled:</span> Set to true to enable this offer</li>
|
||
<li class="break-words"><span class="font-semibold">coin_from:</span> Coin you send</li>
|
||
<li class="break-words"><span class="font-semibold">coin_to:</span> Coin you receive</li>
|
||
<li class="break-words"><span class="font-semibold">amount:</span> Amount to create the offer for</li>
|
||
<li class="break-words"><span class="font-semibold">minrate:</span> Rate below which the offer won't drop</li>
|
||
<li class="break-words"><span class="font-semibold">min_coin_from_amt:</span> Won't generate offers if wallet balance would drop below this (default: 0)</li>
|
||
<li class="break-words">
|
||
<span class="font-semibold">adjust_rates_based_on_market:</span> Controls how AMM determines competitive rates. See detailed explanation below.
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="break-words">
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Optional Settings</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li class="break-words"><span class="font-semibold">ratetweakpercent:</span> Modify the offer rate from the fetched value (can be negative)</li>
|
||
<li class="break-words"><span class="font-semibold">amount_variable:</span> Whether bidder can set a different amount</li>
|
||
<li class="break-words"><span class="font-semibold">address:</span> Address offer is sent from (auto = generate new address per offer)</li>
|
||
<li class="break-words"><span class="font-semibold">offer_valid_seconds:</span> How long generated offers will be valid for (minimum 600 seconds)</li>
|
||
<li class="break-words"><span class="font-semibold">swap_type:</span> Type of swap, defaults to "adaptor_sig"</li>
|
||
<li class="break-words"><span class="font-semibold">min_swap_amount:</span> Minimum purchase quantity when offer amount is variable</li>
|
||
<li class="break-words"><span class="font-semibold">amount_step:</span> <strong>Mandatory privacy feature</strong> that sets the offer size increment. Prevents revealing exact wallet balance by creating stepped offers. Must be >= 0.001 and <= your offer amount. Default: 0.001</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<button type="button" class="collapsible-header flex items-center justify-between w-full p-3 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none" data-target="bid-templates-content">
|
||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Bid Templates</h4>
|
||
<svg class="w-5 h-5 transition-transform transform text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
</button>
|
||
<div id="bid-templates-content" class="collapsible-content mt-2 bg-gray-50 dark:bg-gray-700 p-4 rounded-lg hidden">
|
||
<p class="text-sm font-bold text-gray-600 dark:text-gray-400 mb-3">
|
||
Bid templates define how the AMM creates bids. Each template can have the following settings:
|
||
</p>
|
||
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Required Settings</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li><span class="font-semibold">name:</span> Template name, must be unique</li>
|
||
<li><span class="font-semibold">enabled:</span> Set to true to enable this bid</li>
|
||
<li><span class="font-semibold">coin_from:</span> Coin you receive</li>
|
||
<li><span class="font-semibold">coin_to:</span> Coin you send</li>
|
||
<li><span class="font-semibold">amount:</span> Amount to bid (minimum: 0.001)</li>
|
||
<li><span class="font-semibold">max_rate:</span> Maximum rate for bids</li>
|
||
<li><span class="font-semibold">min_coin_to_balance:</span> Won't send bids if wallet amount would drop below this</li>
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm font-medium text-gray-700 dark:text-gray-300"><strong>Optional Settings</strong></p>
|
||
<ul class="mt-2 space-y-2 text-sm text-gray-600 dark:text-gray-400">
|
||
<li><span class="font-semibold">max_concurrent:</span> Maximum number of bids to have active at once (default: 1)</li>
|
||
<li><span class="font-semibold">amount_variable:</span> Can send bids below the set amount where possible</li>
|
||
<li><span class="font-semibold">max_coin_from_balance:</span> If needed, uncomment and set to limit bids when wallet amount is above this</li>
|
||
<li><span class="font-semibold">address:</span> Address bid is sent from (auto = generate new address per bid)</li>
|
||
<li><span class="font-semibold">min_swap_amount:</span> Minimum swap amount for variable amount bids</li>
|
||
<li><span class="font-semibold">use_balance_bidding:</span> If true, calculates bid amount as (wallet_balance - offer_min_amount) instead of using template amount</li>
|
||
<li><span class="font-semibold">offers_to_bid_on:</span> Filter offers to bid on: <strong>"auto_accept_only"</strong> (only auto-accept offers, default), <strong>"all"</strong> (any offer), <strong>"known_only"</strong> (known identities only)</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="mb-4">
|
||
<button type="button" class="collapsible-header flex items-center justify-between w-full p-3 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none" data-target="rate-adjustment-faq-content">
|
||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Rate Adjustment Settings FAQ</h4>
|
||
<svg class="w-5 h-5 transition-transform transform text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
</button>
|
||
<div id="rate-adjustment-faq-content" class="collapsible-content mt-2 bg-gray-50 dark:bg-gray-700 p-4 rounded-lg hidden">
|
||
<p class="text-sm font-bold text-gray-600 dark:text-gray-400 mb-4">
|
||
The <strong>adjust_rates_based_on_market</strong> setting controls how your AMM determines competitive rates for offers. Here are all available options:
|
||
</p>
|
||
|
||
<div class="space-y-4">
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Coingecko</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>Value:</strong> <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded text-gray-900 dark:text-gray-200">false</code><br>
|
||
<strong>Behavior:</strong> Uses only CoinGecko exchange rates. Fails if CoinGecko data is unavailable.<br>
|
||
<strong>Best for:</strong> Stable, external market pricing without local market influence.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Orderbook</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>Value:</strong> <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded text-gray-900 dark:text-gray-200">"only"</code><br>
|
||
<strong>Behavior:</strong> Uses only Orderbook offers that are as or more permissive than your automation_strategy. Fails if no matches.<br>
|
||
<strong>Best for:</strong> Competing only with other market makers.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Either</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>Value:</strong> <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded text-gray-900 dark:text-gray-200">true</code><br>
|
||
<strong>Behavior:</strong> Uses the higher rate between CoinGecko and market offers. Fails only if BOTH sources are unavailable.<br>
|
||
<strong>Best for:</strong> Balanced approach with fallback protection.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Both</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>Value:</strong> <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded text-gray-900 dark:text-gray-200">"all"</code><br>
|
||
<strong>Behavior:</strong> Uses the higher rate between CoinGecko and market offers. Fails if EITHER source is unavailable.<br>
|
||
<strong>Best for:</strong> Strict validation requiring both external and market data.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Orderbook - Fallback</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>Value:</strong> <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded text-gray-900 dark:text-gray-200">"minrate"</code><br>
|
||
<strong>Behavior:</strong> Uses market offers if available, otherwise falls back to your minimum rate.<br>
|
||
<strong>Best for:</strong> Market-responsive pricing with guaranteed operation.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Static</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>Value:</strong> <code class="bg-gray-200 dark:bg-gray-600 px-1 rounded text-gray-900 dark:text-gray-200">"static"</code><br>
|
||
<strong>Behavior:</strong> Uses only your minimum rate + rate tweak percentage. Ignores all external data.<br>
|
||
<strong>Best for:</strong> Fixed pricing strategies or testing.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-6 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
||
<h6 class="font-semibold text-gray-900 dark:text-gray-200 mb-2">Rate Calculation Formula</h6>
|
||
<p class="text-sm text-gray-700 dark:text-gray-300">
|
||
<strong>Final Rate = Base Rate × (ratetweakpercent / 100 + 1)</strong><br>
|
||
Where Base Rate comes from your selected adjustment method above.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="mt-4 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
||
<h6 class="font-semibold text-gray-900 dark:text-gray-200 mb-2">Important Notes</h6>
|
||
<ul class="text-sm text-gray-700 dark:text-gray-300 space-y-1">
|
||
<li>• Final rate will never go below your <strong>minrate</strong> setting</li>
|
||
<li>• Strategies which use the orderbook will only consider offers with an automation strategy as permissive or better than your config</li>
|
||
<li>• If your chosen method fails to get rates, the offer will be skipped for that cycle (Exception: "Static" and "Orderbook (Fallback)")</li>
|
||
<li>• Rate tweak percentage can be negative to offer below market rates</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<button type="button" class="collapsible-header flex items-center justify-between w-full p-3 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none" data-target="bidding-strategy-faq-content">
|
||
<h4 class="text-lg font-semibold text-gray-900 dark:text-white">Bidding Strategy Settings FAQ</h4>
|
||
<svg class="w-5 h-5 transition-transform transform text-gray-700 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
</button>
|
||
<div id="bidding-strategy-faq-content" class="collapsible-content mt-2 bg-gray-50 dark:bg-gray-700 p-4 rounded-lg hidden">
|
||
<p class="text-sm font-bold text-gray-600 dark:text-gray-400 mb-4">
|
||
<strong>Attempt Bids First</strong> allows your AMM to fill existing offers before creating new ones.
|
||
</p>
|
||
|
||
<div class="space-y-4">
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">How it works</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
<strong>1.</strong> AMM wants to create a 1 BTC → XMR offer at rate 150<br>
|
||
<strong>2.</strong> Checks for existing XMR → BTC offers that would fill our intended offer<br>
|
||
<strong>3.</strong> Places bids on compatible offers to fill what we can<br>
|
||
<strong>4.</strong> Creates AMM offer only for the remaining amount<br>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Bidding Strategies</h5>
|
||
<div class="space-y-2 mt-2">
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Aggressive:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Bid on all compatible offers to fill as much as possible</span>
|
||
</div>
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Balanced:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Mix of bidding and offer creation (default)</span>
|
||
</div>
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Conservative:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Only bid on offers that match your automation strategy</span>
|
||
</div>
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Best rates from auto-accept offers only:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Only bid on offers that match your automation strategy, prioritizing best rates</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Automation Strategy Compatibility</h5>
|
||
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||
Bidding strategies consider your AMM's automation strategy when selecting offers to bid on:<br>
|
||
<strong>• Accept All:</strong> Only bids on offers with "Accept All" automation<br>
|
||
<strong>• Accept Known:</strong> Bids on offers with "Accept All" or "Accept Known" automation<br>
|
||
<strong>• None:</strong> Bids on all offers regardless of automation strategy<br>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="border-l-4 border-gray-300 dark:border-gray-600 pl-4">
|
||
<h5 class="font-semibold text-gray-900 dark:text-white">Configuration Options</h5>
|
||
<div class="space-y-2 mt-2">
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Max Bid Percentage:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Maximum % of your offer amount to use for bidding (0-100)</span>
|
||
</div>
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Rate Tolerance %:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Bid on offers up to this % better than your intended rate</span>
|
||
</div>
|
||
<div>
|
||
<strong class="text-gray-900 dark:text-white">Min Remaining Offer:</strong>
|
||
<span class="text-sm text-gray-600 dark:text-gray-400">Don't create AMM offer if remainder is below this amount</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
|
||
const configForm = document.querySelector('form[method="post"]');
|
||
const saveConfigBtn = document.getElementById('save_config_btn');
|
||
|
||
const jsonTab = document.getElementById('json-tab');
|
||
const settingsTab = document.getElementById('settings-tab');
|
||
const overviewTab = document.getElementById('overview-tab');
|
||
const jsonContent = document.getElementById('json-content');
|
||
const settingsContent = document.getElementById('settings-content');
|
||
const overviewContent = document.getElementById('overview-content');
|
||
|
||
const addOfferTab = document.getElementById('add-offer-tab');
|
||
const addBidTab = document.getElementById('add-bid-tab');
|
||
const addOfferContent = document.getElementById('add-offer-content');
|
||
const addBidContent = document.getElementById('add-bid-content');
|
||
|
||
if (jsonTab && settingsTab && overviewTab && jsonContent && settingsContent && overviewContent) {
|
||
const activeConfigTab = localStorage.getItem('amm_active_config_tab');
|
||
|
||
function switchConfigTab(tabId) {
|
||
jsonContent.classList.add('hidden');
|
||
jsonContent.classList.remove('block');
|
||
settingsContent.classList.add('hidden');
|
||
settingsContent.classList.remove('block');
|
||
overviewContent.classList.add('hidden');
|
||
overviewContent.classList.remove('block');
|
||
|
||
jsonTab.classList.remove('bg-gray-100', 'text-gray-900', 'dark:bg-gray-600', 'dark:text-white');
|
||
settingsTab.classList.remove('bg-gray-100', 'text-gray-900', 'dark:bg-gray-600', 'dark:text-white');
|
||
overviewTab.classList.remove('bg-gray-100', 'text-gray-900', 'dark:bg-gray-600', 'dark:text-white');
|
||
|
||
if (tabId === 'json-tab') {
|
||
jsonContent.classList.remove('hidden');
|
||
jsonContent.classList.add('block');
|
||
jsonTab.classList.add('bg-gray-100', 'text-gray-900', 'dark:bg-gray-600', 'dark:text-white');
|
||
localStorage.setItem('amm_active_config_tab', 'json-tab');
|
||
} else if (tabId === 'settings-tab') {
|
||
settingsContent.classList.remove('hidden');
|
||
settingsContent.classList.add('block');
|
||
settingsTab.classList.add('bg-gray-100', 'text-gray-900', 'dark:bg-gray-600', 'dark:text-white');
|
||
localStorage.setItem('amm_active_config_tab', 'settings-tab');
|
||
|
||
loadSettingsFromJson();
|
||
} else if (tabId === 'overview-tab') {
|
||
overviewContent.classList.remove('hidden');
|
||
overviewContent.classList.add('block');
|
||
overviewTab.classList.add('bg-gray-100', 'text-gray-900', 'dark:bg-gray-600', 'dark:text-white');
|
||
localStorage.setItem('amm_active_config_tab', 'overview-tab');
|
||
}
|
||
}
|
||
|
||
function loadSettingsFromJson() {
|
||
const configTextarea = document.querySelector('textarea[name="config_content"]');
|
||
if (!configTextarea) return;
|
||
|
||
try {
|
||
const configText = configTextarea.value.trim();
|
||
if (!configText) return;
|
||
|
||
const config = JSON.parse(configText);
|
||
|
||
document.getElementById('min_seconds_between_offers').value = config.min_seconds_between_offers || 15;
|
||
document.getElementById('max_seconds_between_offers').value = config.max_seconds_between_offers || 60;
|
||
document.getElementById('main_loop_delay').value = config.main_loop_delay || 60;
|
||
|
||
const minSecondsBetweenBidsEl = document.getElementById('min_seconds_between_bids');
|
||
const maxSecondsBetweenBidsEl = document.getElementById('max_seconds_between_bids');
|
||
const pruneStateDelayEl = document.getElementById('prune_state_delay');
|
||
const pruneStateAfterSecondsEl = document.getElementById('prune_state_after_seconds');
|
||
|
||
if (minSecondsBetweenBidsEl) minSecondsBetweenBidsEl.value = config.min_seconds_between_bids || 15;
|
||
if (maxSecondsBetweenBidsEl) maxSecondsBetweenBidsEl.value = config.max_seconds_between_bids || 60;
|
||
if (pruneStateDelayEl) pruneStateDelayEl.value = config.prune_state_delay || 120;
|
||
if (pruneStateAfterSecondsEl) pruneStateAfterSecondsEl.value = config.prune_state_after_seconds || 604800;
|
||
document.getElementById('auth').value = config.auth || '';
|
||
} catch (error) {
|
||
console.error('Error loading settings from JSON:', error);
|
||
}
|
||
}
|
||
|
||
jsonTab.addEventListener('click', function() {
|
||
switchConfigTab('json-tab');
|
||
});
|
||
|
||
settingsTab.addEventListener('click', function() {
|
||
switchConfigTab('settings-tab');
|
||
});
|
||
|
||
overviewTab.addEventListener('click', function() {
|
||
switchConfigTab('overview-tab');
|
||
});
|
||
|
||
const returnToTab = localStorage.getItem('amm_return_to_tab');
|
||
if (returnToTab && (returnToTab === 'json-tab' || returnToTab === 'settings-tab' || returnToTab === 'overview-tab')) {
|
||
localStorage.removeItem('amm_return_to_tab');
|
||
switchConfigTab(returnToTab);
|
||
} else if (activeConfigTab === 'settings-tab') {
|
||
switchConfigTab('settings-tab');
|
||
} else if (activeConfigTab === 'overview-tab') {
|
||
switchConfigTab('overview-tab');
|
||
} else {
|
||
switchConfigTab('json-tab');
|
||
}
|
||
|
||
const globalSettingsForm = document.getElementById('global-settings-form');
|
||
if (globalSettingsForm) {
|
||
globalSettingsForm.addEventListener('submit', function(e) {
|
||
updateJsonFromSettings();
|
||
});
|
||
}
|
||
|
||
function updateJsonFromSettings() {
|
||
const configTextarea = document.querySelector('textarea[name="config_content"]');
|
||
if (!configTextarea) return;
|
||
|
||
try {
|
||
const configText = configTextarea.value.trim();
|
||
if (!configText) return;
|
||
|
||
const config = JSON.parse(configText);
|
||
|
||
config.min_seconds_between_offers = parseInt(document.getElementById('min_seconds_between_offers').value) || 15;
|
||
config.max_seconds_between_offers = parseInt(document.getElementById('max_seconds_between_offers').value) || 60;
|
||
config.main_loop_delay = parseInt(document.getElementById('main_loop_delay').value) || 60;
|
||
|
||
const minSecondsBetweenBidsEl = document.getElementById('min_seconds_between_bids');
|
||
const maxSecondsBetweenBidsEl = document.getElementById('max_seconds_between_bids');
|
||
const pruneStateDelayEl = document.getElementById('prune_state_delay');
|
||
const pruneStateAfterSecondsEl = document.getElementById('prune_state_after_seconds');
|
||
|
||
config.min_seconds_between_bids = minSecondsBetweenBidsEl ? parseInt(minSecondsBetweenBidsEl.value) || 15 : (config.min_seconds_between_bids || 15);
|
||
config.max_seconds_between_bids = maxSecondsBetweenBidsEl ? parseInt(maxSecondsBetweenBidsEl.value) || 60 : (config.max_seconds_between_bids || 60);
|
||
config.prune_state_delay = pruneStateDelayEl ? parseInt(pruneStateDelayEl.value) || 120 : (config.prune_state_delay || 120);
|
||
config.prune_state_after_seconds = pruneStateAfterSecondsEl ? parseInt(pruneStateAfterSecondsEl.value) || 604800 : (config.prune_state_after_seconds || 604800);
|
||
config.auth = document.getElementById('auth').value;
|
||
|
||
delete config.adjust_rates_based_on_market;
|
||
|
||
configTextarea.value = JSON.stringify(config, null, 4);
|
||
} catch (error) {
|
||
console.error('Error updating JSON from settings:', error);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
const collapsibleHeaders = document.querySelectorAll('.collapsible-header');
|
||
|
||
if (collapsibleHeaders.length > 0) {
|
||
let collapsibleStates = {};
|
||
try {
|
||
const storedStates = localStorage.getItem('amm_collapsible_states');
|
||
if (storedStates) {
|
||
collapsibleStates = JSON.parse(storedStates);
|
||
}
|
||
} catch (e) {
|
||
console.error('Error parsing stored collapsible states:', e);
|
||
collapsibleStates = {};
|
||
}
|
||
|
||
function toggleCollapsible(header) {
|
||
const targetId = header.getAttribute('data-target');
|
||
const content = document.getElementById(targetId);
|
||
const arrow = header.querySelector('svg');
|
||
|
||
if (content) {
|
||
if (content.classList.contains('hidden')) {
|
||
content.classList.remove('hidden');
|
||
arrow.classList.add('rotate-180');
|
||
collapsibleStates[targetId] = 'open';
|
||
} else {
|
||
content.classList.add('hidden');
|
||
arrow.classList.remove('rotate-180');
|
||
collapsibleStates[targetId] = 'closed';
|
||
}
|
||
|
||
localStorage.setItem('amm_collapsible_states', JSON.stringify(collapsibleStates));
|
||
}
|
||
}
|
||
|
||
collapsibleHeaders.forEach(header => {
|
||
const targetId = header.getAttribute('data-target');
|
||
const content = document.getElementById(targetId);
|
||
const arrow = header.querySelector('svg');
|
||
|
||
if (content) {
|
||
if (collapsibleStates[targetId] === 'open') {
|
||
content.classList.remove('hidden');
|
||
arrow.classList.add('rotate-180');
|
||
} else {
|
||
content.classList.add('hidden');
|
||
arrow.classList.remove('rotate-180');
|
||
collapsibleStates[targetId] = 'closed';
|
||
}
|
||
}
|
||
|
||
header.addEventListener('click', function() {
|
||
toggleCollapsible(header);
|
||
});
|
||
});
|
||
|
||
localStorage.setItem('amm_collapsible_states', JSON.stringify(collapsibleStates));
|
||
}
|
||
|
||
if (configForm && saveConfigBtn) {
|
||
configForm.addEventListener('submit', function(e) {
|
||
if (e.submitter && e.submitter.name === 'save_config') {
|
||
localStorage.setItem('amm_update_tables', 'true');
|
||
}
|
||
});
|
||
|
||
if (localStorage.getItem('amm_update_tables') === 'true') {
|
||
localStorage.removeItem('amm_update_tables');
|
||
setTimeout(function() {
|
||
if (window.ammTablesManager && window.ammTablesManager.updateTables) {
|
||
window.ammTablesManager.updateTables();
|
||
}
|
||
}, 500);
|
||
}
|
||
}
|
||
|
||
if (localStorage.getItem('amm_create_default_refresh') === 'true') {
|
||
localStorage.removeItem('amm_create_default_refresh');
|
||
console.log('[AMM] Page loaded after create default config - redirecting to fresh page');
|
||
|
||
setTimeout(function() {
|
||
window.location.href = window.location.pathname + window.location.search;
|
||
}, 500);
|
||
}
|
||
|
||
const createDefaultBtn = document.getElementById('create_default_btn');
|
||
if (createDefaultBtn && configForm) {
|
||
createDefaultBtn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
|
||
const title = 'Create Default Configuration';
|
||
const message = 'This will overwrite your current configuration with a default template.\n\nAre you sure you want to continue?';
|
||
|
||
if (window.showConfirmModal) {
|
||
window.showConfirmModal(title, message, function() {
|
||
|
||
const hiddenInput = document.createElement('input');
|
||
hiddenInput.type = 'hidden';
|
||
hiddenInput.name = 'create_default';
|
||
hiddenInput.value = 'true';
|
||
configForm.appendChild(hiddenInput);
|
||
|
||
localStorage.setItem('amm_create_default_refresh', 'true');
|
||
|
||
configForm.submit();
|
||
});
|
||
} else {
|
||
if (confirm('This will overwrite your current configuration with a default template.\n\nAre you sure you want to continue?')) {
|
||
const hiddenInput = document.createElement('input');
|
||
hiddenInput.type = 'hidden';
|
||
hiddenInput.name = 'create_default';
|
||
hiddenInput.value = 'true';
|
||
configForm.appendChild(hiddenInput);
|
||
|
||
localStorage.setItem('amm_create_default_refresh', 'true');
|
||
configForm.submit();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
</form>
|
||
</div>
|
||
|
||
{% if state_exists and debug_ui_mode %}
|
||
<div class="mt-6 p-6 bg-white dark:bg-gray-800 rounded-xl shadow-md">
|
||
<h3 class="mb-4 text-xl font-bold text-coolGray-900 dark:text-white">State File (JSON)</h3>
|
||
<div class="mb-4">
|
||
<div class="font-mono bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg p-2.5 h-64 overflow-y-auto dark:bg-gray-700 dark:border-gray-600 dark:text-white">{{ state_content }}</div>
|
||
<div class="mt-4">
|
||
<form method="post">
|
||
<button type="button" id="clearStateBtn" class="px-4 py-2.5 bg-red-500 hover:bg-red-600 font-medium text-sm text-white border border-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
Clear AMM State
|
||
</button>
|
||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="mb-8">
|
||
<div class="p-6 bg-white dark:bg-gray-800 rounded-xl shadow-md">
|
||
<h3 class="mb-4 text-xl font-bold text-coolGray-900 dark:text-white">AMM Logs</h3>
|
||
<div class="bg-gray-100 dark:bg-gray-800 rounded-lg p-4 h-64 overflow-y-auto font-mono text-sm text-gray-900 dark:text-gray-200">
|
||
{% if logs %}
|
||
{% for log in logs %}
|
||
<div class="mb-1">{{ log }}</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<div class="text-gray-500 dark:text-gray-400">No logs available</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</section>
|
||
</div>
|
||
|
||
<div id="add-amm-modal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
||
<div class="absolute inset-0 bg-gray-500 opacity-75 dark:bg-gray-900 dark:opacity-90"></div>
|
||
</div>
|
||
|
||
<div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle md:max-w-2xl md:w-full">
|
||
<div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||
<div class="sm:flex sm:items-start">
|
||
<div class="mt-3 text-center sm:mt-0 sm:text-left w-full">
|
||
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white" id="add-modal-title">Add New Item</h3>
|
||
<div class="mt-4">
|
||
<form id="add-amm-form" class="space-y-4">
|
||
<input type="hidden" id="add-amm-type">
|
||
|
||
<div>
|
||
<label for="add-amm-name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Name <span class="text-red-500">*</span></label>
|
||
<input type="text" id="add-amm-name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="e.g., Offer 1" required>
|
||
</div>
|
||
|
||
<div class="flex items-center">
|
||
<input type="checkbox" id="add-amm-enabled" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded dark:bg-gray-700 dark:border-gray-600" checked>
|
||
<label for="add-amm-enabled" class="ml-2 block text-sm text-gray-900 dark:text-gray-300">Enabled</label>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-amm-coin-from" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Maker</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-amm-coin-from" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
{% for c in coins %}
|
||
<option value="{{ c[1] }}" data-symbol="{{ c[0] }}">
|
||
{{ c[1] }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-amm-coin-to" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Taker</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-amm-coin-to" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
{% for c in coins %}
|
||
<option value="{{ c[1] }}" data-symbol="{{ c[0] }}">
|
||
{{ c[1] }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-amm-amount" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offer Amount <span class="text-red-500">*</span></label>
|
||
<input type="text" id="add-amm-amount" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.0">
|
||
</div>
|
||
|
||
<div>
|
||
<label id="add-amm-rate-label" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Minimum Rate <span class="text-red-500">*</span></label>
|
||
<div class="flex items-center gap-2">
|
||
<input type="text" id="add-amm-rate" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.0">
|
||
<button type="button" id="add-get-rate-button" class="px-2 py-1.5 bg-blue-500 hover:bg-blue-600 font-medium text-xs text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Get Rate</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="add-offer-fields" class="hidden space-y-4">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
|
||
<div>
|
||
<label for="add-offer-ratetweakpercent" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Rate Tweak Percent</label>
|
||
<input type="text" id="add-offer-ratetweakpercent" pattern="-?[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="0" placeholder="0">
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-offer-min-coin-from-amt" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Minimum Balance</label>
|
||
<input type="text" id="add-offer-min-coin-from-amt" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.0">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-offer-valid-seconds" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offer Valid Seconds <span class="text-red-500">*</span></label>
|
||
<input type="text" id="add-offer-valid-seconds" pattern="[0-9]+" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="3600" placeholder="3600">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Minimum 600 seconds (10 minutes).</small>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-offer-address" class="block text-sm font-medium text-gray-700 dark:text-gray-300">SMSG Address / Identity</label>
|
||
<input type="text" id="add-offer-address" class="font-mono bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="auto" value="auto">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-offer-swap-type" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Swap Type</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-offer-swap-type" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="adaptor_sig" data-desc="Adaptor Sig">Adaptor Sig (default)</option>
|
||
<option value="seller_first" data-desc="Secret Hash">Secret Hash</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-offer-min-swap-amount" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Swap Amount</label>
|
||
<input type="text" id="add-offer-min-swap-amount" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="0.001" placeholder="0.001">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-offer-amount-step" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offer Size Increment <span class="text-red-500">*</span></label>
|
||
<input type="text" id="add-offer-amount-step" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="e.g., 5.0" required>
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">
|
||
How much to adjust offer size when partially filled. Must be between 0.001 and your offer amount.
|
||
</small>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div>
|
||
<label for="add-offer-automation-strategy" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Auto Accept Bids</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-offer-automation-strategy" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="accept_all">Accept All Bids</option>
|
||
<option value="accept_known">Accept Known Identities</option>
|
||
<option value="none">No Auto Accept</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-offer-adjust-rates" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Adjust Rates Based on Market</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-offer-adjust-rates" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="false">Coingecko - Use CoinGecko, fail if rates not found</option>
|
||
<option value="only">Orderbook - Use Orderbook auto-accept offers only, fail if rates not found</option>
|
||
<option value="true">Either - Use greater of CoinGecko + Orderbook, fail if BOTH unavailable</option>
|
||
<option value="all">Both - Use greater of CoinGecko + Orderbook, fail if EITHER unavailable</option>
|
||
<option value="minrate">Orderbook (Fallback) - Fallback to Minimum Rate</option>
|
||
<option value="static">Static - Exclusively use Minimum Rate + Tweak</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="border-t border-gray-200 dark:border-gray-600 pt-4">
|
||
<div class="flex items-center mb-3">
|
||
<input type="checkbox" id="add-offer-attempt-bids-first" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||
<label for="add-offer-attempt-bids-first" class="ml-2 block text-sm font-medium text-gray-900 dark:text-gray-300">Attempt Bids First</label>
|
||
</div>
|
||
<p class="text-xs text-gray-500 dark:text-gray-400 mb-4">Before creating an AMM offer, attempt to fill existing offers by placing bids, then create offer for remainder.</p>
|
||
|
||
<div id="add-offer-bidding-options" class="hidden space-y-4">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-offer-bid-strategy" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Bidding Strategy</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-offer-bid-strategy" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="aggressive">Aggressive - Fill as much as possible</option>
|
||
<option value="balanced" selected>Balanced - Mix of bids and offers</option>
|
||
<option value="conservative">Conservative - Only obvious wins</option>
|
||
<option value="auto_accept_only">Best rates from auto-accept offers only</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-offer-max-bid-percentage" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Max Bid Percentage</label>
|
||
<input type="text" id="add-offer-max-bid-percentage" pattern="[0-9]+" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="50" placeholder="50">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Maximum % of offer amount to use for bidding (0-100)</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-offer-bid-rate-tolerance" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Rate Tolerance %</label>
|
||
<input type="text" id="add-offer-bid-rate-tolerance" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="2.0" placeholder="2.0">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Bid on offers up to this % better than our rate</small>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-offer-min-remaining-offer" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Remaining Offer</label>
|
||
<input type="text" id="add-offer-min-remaining-offer" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="0.001" placeholder="0.001">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Don't create offer if remainder is below this amount</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div id="add-bid-fields" class="hidden space-y-4">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-bid-min-coin-to-balance" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Coin To Balance</label>
|
||
<input type="text" id="add-bid-min-coin-to-balance" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="1.0" placeholder="1.0">
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-bid-max-concurrent" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Max Concurrent</label>
|
||
<input type="text" id="add-bid-max-concurrent" pattern="[0-9]+" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="1" placeholder="1">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-bid-offers-to-bid-on" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offers to Bid On</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="add-bid-offers-to-bid-on" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="all">All Offers</option>
|
||
<option value="auto_accept_only">Auto-Accept Offers Only</option>
|
||
<option value="known_only">Known Identities Only</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="add-bid-address" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Address</label>
|
||
<input type="text" id="add-bid-address" class="font-mono bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="auto" value="auto">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="add-bid-min-swap-amount" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Swap Amount</label>
|
||
<input type="text" id="add-bid-min-swap-amount" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="0.001" placeholder="0.001">
|
||
</div>
|
||
|
||
<div class="flex items-center">
|
||
<input type="checkbox" id="add-bid-use-balance-bidding" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||
<label for="add-bid-use-balance-bidding" class="ml-2 block text-sm text-gray-900 dark:text-gray-300">Use Balance Bidding</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||
<strong>Use Balance Bidding:</strong> Calculate bid amount as (wallet_balance - offer_min_amount) instead of using template amount.
|
||
</div>
|
||
</div>
|
||
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||
<button type="button" id="add-amm-save" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:ring-0 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
|
||
Add
|
||
</button>
|
||
<button type="button" id="add-amm-cancel" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:ring-0 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm dark:bg-gray-600 dark:text-white dark:hover:bg-gray-500 dark:border-gray-500">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="edit-amm-modal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
||
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
||
<div class="absolute inset-0 bg-gray-500 opacity-75 dark:bg-gray-900 dark:opacity-90"></div>
|
||
</div>
|
||
|
||
<div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle lg:max-w-xl md:max-w-2xl md:w-full">
|
||
<div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||
<div class="sm:flex sm:items-start">
|
||
<div class="mt-3 text-center sm:mt-0 sm:text-left w-full">
|
||
<h3 class="text-lg leading-6 font-medium text-gray-900 dark:text-white" id="edit-modal-title">Edit Item</h3>
|
||
<div class="mt-4">
|
||
<form id="edit-amm-form" class="space-y-4">
|
||
<input type="hidden" id="edit-amm-type">
|
||
<input type="hidden" id="edit-amm-id">
|
||
<input type="hidden" id="edit-amm-original-name">
|
||
|
||
<div>
|
||
<label for="edit-amm-name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Name <span class="text-red-500">*</span></label>
|
||
<input type="text" id="edit-amm-name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required>
|
||
</div>
|
||
|
||
<div class="flex items-center">
|
||
<input type="checkbox" id="edit-amm-enabled" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" checked>
|
||
<label for="edit-amm-enabled" class="ml-2 block text-sm text-gray-900 dark:text-gray-300">Enabled</label>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-amm-coin-from" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Maker</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-amm-coin-from" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
{% for c in coins %}
|
||
<option value="{{ c[1] }}" data-symbol="{{ c[0] }}">
|
||
{{ c[1] }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-amm-coin-to" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Taker</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-amm-coin-to" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
{% for c in coins %}
|
||
<option value="{{ c[1] }}" data-symbol="{{ c[0] }}">
|
||
{{ c[1] }}
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-amm-amount" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offer Amount <span class="text-red-500">*</span></label>
|
||
<input type="text" id="edit-amm-amount" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.0">
|
||
</div>
|
||
|
||
<div>
|
||
<label id="edit-amm-rate-label" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Minimum Rate <span class="text-red-500">*</span></label>
|
||
<div class="flex items-center gap-2">
|
||
<input type="text" id="edit-amm-rate" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.0">
|
||
<button type="button" id="edit-get-rate-button" class="px-2 py-1.5 bg-blue-500 hover:bg-blue-600 font-medium text-xs text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Get Rate</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="edit-offer-fields" class="hidden space-y-4">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-offer-ratetweakpercent" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Rate Tweak Percent</label>
|
||
<input type="text" id="edit-offer-ratetweakpercent" pattern="-?[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0">
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-offer-min-coin-from-amt" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Minimum Balance</label>
|
||
<input type="text" id="edit-offer-min-coin-from-amt" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.0">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-offer-valid-seconds" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offer Valid Seconds <span class="text-red-500">*</span></label>
|
||
<input type="text" id="edit-offer-valid-seconds" pattern="[0-9]+" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="3600">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Minimum 600 seconds (10 minutes).</small>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-offer-address" class="block text-sm font-medium text-gray-700 dark:text-gray-300">SMSG Address / Identity</label>
|
||
<input type="text" id="edit-offer-address" class="font-mono bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="auto">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-offer-swap-type" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Swap Type</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-offer-swap-type" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="adaptor_sig" data-desc="Adaptor Sig">Adaptor Sig</option>
|
||
<option value="seller_first" data-desc="Secret Hash">Secret Hash</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-offer-min-swap-amount" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Minimum Swap Amount</label>
|
||
<input type="text" id="edit-offer-min-swap-amount" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.001">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-offer-amount-step" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offer Size Increment <span class="text-red-500">*</span></label>
|
||
<input type="text" id="edit-offer-amount-step" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="e.g., 5.0" required>
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">
|
||
How much to adjust offer size when partially filled. Must be between 0.001 and your offer amount.
|
||
</small>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div>
|
||
<label for="edit-offer-automation-strategy" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Auto Accept Bids</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-offer-automation-strategy" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="accept_all">Accept All Bids</option>
|
||
<option value="accept_known">Accept Known Identities</option>
|
||
<option value="none">No Auto Accept</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-offer-adjust-rates" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Adjust Rates Based on Market</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-offer-adjust-rates" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="false">Coingecko - Use CoinGecko, fail if rates not found</option>
|
||
<option value="only">Orderbook - Use Orderbook auto-accept offers only, fail if rates not found</option>
|
||
<option value="true">Either - Use greater of CoinGecko + Orderbook, fail if BOTH unavailable</option>
|
||
<option value="all">Both - Use greater of CoinGecko + Orderbook, fail if EITHER unavailable</option>
|
||
<option value="minrate">Orderbook (Fallback) - Fallback to Minimum Rate</option>
|
||
<option value="static">Static - Exclusively use Minimum Rate + Tweak</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{% if debug_ui_mode %}
|
||
<div class="border-t border-gray-200 dark:border-gray-600 pt-4">
|
||
<div class="flex items-center mb-3">
|
||
<input type="checkbox" id="edit-offer-attempt-bids-first" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||
<label for="edit-offer-attempt-bids-first" class="ml-2 block text-sm font-medium text-gray-900 dark:text-gray-300">Attempt Bids First</label>
|
||
</div>
|
||
<p class="text-xs text-gray-500 dark:text-gray-400 mb-4">Before creating an AMM offer, attempt to fill existing offers by placing bids, then create offer for remainder.</p>
|
||
|
||
<div id="edit-offer-bidding-options" class="hidden space-y-4">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-offer-bid-strategy" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Bidding Strategy</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-offer-bid-strategy" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="aggressive">Aggressive - Fill as much as possible</option>
|
||
<option value="balanced" selected>Balanced - Mix of bids and offers</option>
|
||
<option value="conservative">Conservative - Only obvious wins</option>
|
||
<option value="auto_accept_only">Best rates from auto-accept offers only</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-offer-max-bid-percentage" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Max Bid Percentage</label>
|
||
<input type="text" id="edit-offer-max-bid-percentage" pattern="[0-9]+" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="50" placeholder="50">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Maximum % of offer amount to use for bidding (0-100)</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-offer-bid-rate-tolerance" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Rate Tolerance %</label>
|
||
<input type="text" id="edit-offer-bid-rate-tolerance" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="2.0" placeholder="2.0">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Bid on offers up to this % better than our rate</small>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-offer-min-remaining-offer" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Remaining Offer</label>
|
||
<input type="text" id="edit-offer-min-remaining-offer" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" value="0.001" placeholder="0.001">
|
||
<small class="text-xs text-gray-500 dark:text-gray-400">Don't create offer if remainder is below this amount</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div id="edit-bid-fields" class="hidden space-y-4">
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-bid-min-coin-to-balance" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Coin To Balance</label>
|
||
<input type="text" id="edit-bid-min-coin-to-balance" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="1.0">
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-bid-max-concurrent" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Max Concurrent</label>
|
||
<input type="text" id="edit-bid-max-concurrent" pattern="[0-9]+" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="1">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-bid-offers-to-bid-on" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Offers to Bid On</label>
|
||
<div class="relative">
|
||
<svg class="absolute top-1/2 right-3 transform -translate-y-1/2 w-4 h-4 text-gray-400 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||
</svg>
|
||
<select id="edit-bid-offers-to-bid-on" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 appearance-none">
|
||
<option value="all">All Offers</option>
|
||
<option value="auto_accept_only">Auto-Accept Offers Only</option>
|
||
<option value="known_only">Known Identities Only</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label for="edit-bid-address" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Address</label>
|
||
<input type="text" id="edit-bid-address" class="font-mono bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="auto">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label for="edit-bid-min-swap-amount" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Min Swap Amount</label>
|
||
<input type="text" id="edit-bid-min-swap-amount" pattern="[0-9]*\.?[0-9]*" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="0.001">
|
||
</div>
|
||
|
||
<div class="flex items-center">
|
||
<input type="checkbox" id="edit-bid-use-balance-bidding" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||
<label for="edit-bid-use-balance-bidding" class="ml-2 block text-sm text-gray-900 dark:text-gray-300">Use Balance Bidding</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||
<strong>Use Balance Bidding:</strong> Calculate bid amount as (wallet_balance - offer_min_amount) instead of using template amount.
|
||
</div>
|
||
</div>
|
||
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||
<button type="button" id="edit-amm-save" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:ring-0 focus:outline-none sm:ml-3 sm:w-auto sm:text-sm">
|
||
Save Changes
|
||
</button>
|
||
<button type="button" id="edit-amm-cancel" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:ring-0 focus:outline-none sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm dark:bg-gray-600 dark:text-white dark:hover:bg-gray-500 dark:border-gray-500">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="errorModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-out"></div>
|
||
<div class="relative z-50 min-h-screen px-4 flex items-center justify-center">
|
||
<div class="bg-white dark:bg-gray-500 rounded-lg max-w-md w-full p-6 shadow-lg transition-opacity duration-300 ease-out">
|
||
<div class="text-center">
|
||
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 dark:bg-red-900 mb-4">
|
||
<svg class="h-6 w-6 text-red-600 dark:text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
||
</svg>
|
||
</div>
|
||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4" id="errorTitle">Error</h2>
|
||
<p class="text-gray-600 dark:text-gray-200 mb-6 whitespace-pre-line" id="errorMessage">An error occurred</p>
|
||
<div class="flex justify-center">
|
||
<button type="button" id="errorOk"
|
||
class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
OK
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="confirmModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-out"></div>
|
||
<div class="relative z-50 min-h-screen px-4 flex items-center justify-center">
|
||
<div class="bg-white dark:bg-gray-500 rounded-lg max-w-md w-full p-6 shadow-lg transition-opacity duration-300 ease-out">
|
||
<div class="text-center">
|
||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-4" id="confirmTitle">Confirm Action</h2>
|
||
<p class="text-gray-600 dark:text-gray-200 mb-6 whitespace-pre-line" id="confirmMessage">Are you sure?</p>
|
||
<div class="flex justify-center gap-4">
|
||
<button type="button" id="confirmYes"
|
||
class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
Confirm
|
||
</button>
|
||
<button type="button" id="confirmNo"
|
||
class="px-4 py-2.5 font-medium text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||
Cancel
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/static/js/amm_tables.js"></script>
|
||
<script>
|
||
function saveDebugSetting() {
|
||
const debugCheckbox = document.getElementById('debug-mode');
|
||
if (debugCheckbox) {
|
||
localStorage.setItem('amm_debug_enabled', debugCheckbox.checked);
|
||
}
|
||
}
|
||
|
||
function loadDebugSetting() {
|
||
const debugCheckbox = document.getElementById('debug-mode');
|
||
if (debugCheckbox) {
|
||
const savedSetting = localStorage.getItem('amm_debug_enabled');
|
||
if (savedSetting !== null) {
|
||
debugCheckbox.checked = savedSetting === 'true';
|
||
}
|
||
}
|
||
}
|
||
|
||
function saveAutostartSetting(checked) {
|
||
const bodyData = `autostart=${checked ? 'true' : 'false'}`;
|
||
|
||
fetch('/amm/autostart', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/x-www-form-urlencoded',
|
||
},
|
||
body: bodyData
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
localStorage.setItem('amm_autostart_enabled', checked);
|
||
|
||
if (data.autostart !== checked) {
|
||
console.warn('WARNING: API returned different autostart value than expected!', {
|
||
sent: checked,
|
||
received: data.autostart
|
||
});
|
||
}
|
||
} else {
|
||
console.error('Failed to save autostart setting:', data.error);
|
||
const autostartCheckbox = document.getElementById('autostart-amm');
|
||
if (autostartCheckbox) {
|
||
autostartCheckbox.checked = !checked;
|
||
}
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error saving autostart setting:', error);
|
||
const autostartCheckbox = document.getElementById('autostart-amm');
|
||
if (autostartCheckbox) {
|
||
autostartCheckbox.checked = !checked;
|
||
}
|
||
});
|
||
}
|
||
|
||
function setupAutostartCheckbox() {
|
||
const autostartCheckbox = document.getElementById('autostart-amm');
|
||
if (autostartCheckbox) {
|
||
autostartCheckbox.addEventListener('change', function() {
|
||
saveAutostartSetting(this.checked);
|
||
});
|
||
}
|
||
}
|
||
|
||
function showErrorModal(title, message) {
|
||
document.getElementById('errorTitle').textContent = title || 'Error';
|
||
document.getElementById('errorMessage').textContent = message || 'An error occurred';
|
||
const modal = document.getElementById('errorModal');
|
||
if (modal) {
|
||
modal.classList.remove('hidden');
|
||
}
|
||
}
|
||
|
||
function hideErrorModal() {
|
||
const modal = document.getElementById('errorModal');
|
||
if (modal) {
|
||
modal.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
function showConfirmModal(title, message, callback) {
|
||
document.getElementById('confirmTitle').textContent = title || 'Confirm Action';
|
||
document.getElementById('confirmMessage').textContent = message || 'Are you sure?';
|
||
const modal = document.getElementById('confirmModal');
|
||
if (modal) {
|
||
modal.classList.remove('hidden');
|
||
}
|
||
|
||
window.confirmCallback = callback;
|
||
}
|
||
|
||
function hideConfirmModal() {
|
||
const modal = document.getElementById('confirmModal');
|
||
if (modal) {
|
||
modal.classList.add('hidden');
|
||
}
|
||
window.confirmCallback = null;
|
||
}
|
||
|
||
function setupStartupValidation() {
|
||
const controlForm = document.querySelector('form[method="post"]');
|
||
if (!controlForm) return;
|
||
|
||
const startButton = controlForm.querySelector('input[name="start"]');
|
||
if (!startButton) return;
|
||
|
||
startButton.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
performStartupValidation();
|
||
});
|
||
}
|
||
|
||
function performStartupValidation() {
|
||
const feedbackDiv = document.getElementById('startup-feedback');
|
||
const titleEl = document.getElementById('startup-title');
|
||
const messageEl = document.getElementById('startup-message');
|
||
const progressBar = document.getElementById('startup-progress-bar');
|
||
|
||
feedbackDiv.classList.remove('hidden');
|
||
|
||
let progress = 0;
|
||
const steps = [
|
||
{ message: 'Checking configuration...', progress: 20 },
|
||
{ message: 'Validating offers and bids...', progress: 40 },
|
||
{ message: 'Checking wallet balances...', progress: 60 },
|
||
{ message: 'Verifying API connection...', progress: 80 },
|
||
{ message: 'Starting AMM process...', progress: 100 }
|
||
];
|
||
|
||
let currentStep = 0;
|
||
|
||
function runNextStep() {
|
||
if (currentStep >= steps.length) {
|
||
submitStartForm();
|
||
return;
|
||
}
|
||
|
||
const step = steps[currentStep];
|
||
messageEl.textContent = step.message;
|
||
progressBar.style.width = step.progress + '%';
|
||
|
||
setTimeout(() => {
|
||
validateStep(currentStep).then(result => {
|
||
if (result.success) {
|
||
currentStep++;
|
||
runNextStep();
|
||
} else {
|
||
showStartupError(result.error);
|
||
}
|
||
}).catch(error => {
|
||
showStartupError('Validation failed: ' + error.message);
|
||
});
|
||
}, 500);
|
||
}
|
||
|
||
runNextStep();
|
||
}
|
||
|
||
async function validateStep(stepIndex) {
|
||
try {
|
||
switch (stepIndex) {
|
||
case 0:
|
||
return await validateConfiguration();
|
||
case 1:
|
||
return await validateOffersAndBids();
|
||
case 2:
|
||
return await validateWalletBalances();
|
||
case 3:
|
||
return await validateApiConnection();
|
||
case 4:
|
||
return { success: true };
|
||
default:
|
||
return { success: true };
|
||
}
|
||
} catch (error) {
|
||
return { success: false, error: error.message };
|
||
}
|
||
}
|
||
|
||
async function validateConfiguration() {
|
||
const configData = window.ammTablesConfig?.configData;
|
||
if (!configData) {
|
||
return { success: false, error: 'No configuration found. Please save a configuration first.' };
|
||
}
|
||
|
||
if (!configData.min_seconds_between_offers || !configData.max_seconds_between_offers) {
|
||
return { success: false, error: 'Missing timing configuration. Please check your settings.' };
|
||
}
|
||
|
||
return { success: true };
|
||
}
|
||
|
||
async function validateOffersAndBids() {
|
||
const configData = window.ammTablesConfig?.configData;
|
||
if (!configData) {
|
||
return { success: false, error: 'Configuration not available for validation.' };
|
||
}
|
||
|
||
const offers = configData.offers || [];
|
||
const bids = configData.bids || [];
|
||
const enabledOffers = offers.filter(o => o.enabled);
|
||
const enabledBids = bids.filter(b => b.enabled);
|
||
|
||
if (enabledOffers.length === 0 && enabledBids.length === 0) {
|
||
return { success: false, error: 'No enabled offers or bids found. Please enable at least one offer or bid before starting.' };
|
||
}
|
||
|
||
for (const offer of enabledOffers) {
|
||
if (!offer.amount_step) {
|
||
return { success: false, error: `Offer "${offer.name}" is missing required Amount Step (privacy feature).` };
|
||
}
|
||
|
||
const amountStep = parseFloat(offer.amount_step);
|
||
const amount = parseFloat(offer.amount);
|
||
|
||
if (amountStep <= 0 || amountStep < 0.001) {
|
||
return { success: false, error: `Offer "${offer.name}" has invalid Amount Step. Must be >= 0.001.` };
|
||
}
|
||
|
||
if (amountStep > amount) {
|
||
return { success: false, error: `Offer "${offer.name}" Amount Step (${amountStep}) cannot be greater than offer amount (${amount}).` };
|
||
}
|
||
}
|
||
|
||
return { success: true };
|
||
}
|
||
|
||
async function validateWalletBalances() {
|
||
const configData = window.ammTablesConfig?.configData;
|
||
if (!configData) return { success: true };
|
||
|
||
const offers = configData.offers || [];
|
||
const enabledOffers = offers.filter(o => o.enabled);
|
||
|
||
for (const offer of enabledOffers) {
|
||
if (!offer.min_coin_from_amt || parseFloat(offer.min_coin_from_amt) <= 0) {
|
||
return { success: false, error: `Offer "${offer.name}" needs a minimum coin amount to protect your wallet balance.` };
|
||
}
|
||
}
|
||
|
||
return { success: true };
|
||
}
|
||
|
||
async function validateApiConnection() {
|
||
return { success: true };
|
||
}
|
||
|
||
function showStartupError(errorMessage) {
|
||
const feedbackDiv = document.getElementById('startup-feedback');
|
||
feedbackDiv.classList.add('hidden');
|
||
|
||
if (window.showErrorModal) {
|
||
window.showErrorModal('AMM Startup Failed', errorMessage);
|
||
} else {
|
||
alert('AMM Startup Failed: ' + errorMessage);
|
||
}
|
||
}
|
||
|
||
function submitStartForm() {
|
||
const feedbackDiv = document.getElementById('startup-feedback');
|
||
const titleEl = document.getElementById('startup-title');
|
||
const messageEl = document.getElementById('startup-message');
|
||
|
||
titleEl.textContent = 'Starting AMM...';
|
||
messageEl.textContent = 'AMM process is starting. Please wait...';
|
||
|
||
const controlForm = document.querySelector('form[method="post"]');
|
||
if (controlForm) {
|
||
const formData = new FormData(controlForm);
|
||
formData.append('start', 'Start');
|
||
|
||
fetch(window.location.pathname, {
|
||
method: 'POST',
|
||
body: formData
|
||
}).then(response => {
|
||
if (response.ok) {
|
||
window.location.reload();
|
||
} else {
|
||
throw new Error('Failed to start AMM');
|
||
}
|
||
}).catch(error => {
|
||
showStartupError('Failed to start AMM: ' + error.message);
|
||
});
|
||
}
|
||
}
|
||
|
||
window.showErrorModal = showErrorModal;
|
||
window.hideErrorModal = hideErrorModal;
|
||
window.showConfirmModal = showConfirmModal;
|
||
window.hideConfirmModal = hideConfirmModal;
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadDebugSetting();
|
||
setupAutostartCheckbox();
|
||
|
||
setupStartupValidation();
|
||
|
||
const debugCheckbox = document.getElementById('debug-mode');
|
||
if (debugCheckbox) {
|
||
debugCheckbox.addEventListener('change', saveDebugSetting);
|
||
}
|
||
|
||
const errorOkBtn = document.getElementById('errorOk');
|
||
if (errorOkBtn) {
|
||
errorOkBtn.addEventListener('click', hideErrorModal);
|
||
}
|
||
|
||
const errorModal = document.getElementById('errorModal');
|
||
if (errorModal) {
|
||
errorModal.addEventListener('click', function(e) {
|
||
if (e.target === errorModal) {
|
||
hideErrorModal();
|
||
}
|
||
});
|
||
}
|
||
|
||
const confirmYesBtn = document.getElementById('confirmYes');
|
||
if (confirmYesBtn) {
|
||
confirmYesBtn.addEventListener('click', function() {
|
||
if (window.confirmCallback && typeof window.confirmCallback === 'function') {
|
||
window.confirmCallback();
|
||
}
|
||
hideConfirmModal();
|
||
});
|
||
}
|
||
|
||
const confirmNoBtn = document.getElementById('confirmNo');
|
||
if (confirmNoBtn) {
|
||
confirmNoBtn.addEventListener('click', hideConfirmModal);
|
||
}
|
||
|
||
const confirmModal = document.getElementById('confirmModal');
|
||
if (confirmModal) {
|
||
confirmModal.addEventListener('click', function(e) {
|
||
if (e.target === confirmModal) {
|
||
hideConfirmModal();
|
||
}
|
||
});
|
||
}
|
||
|
||
const clearStateBtn = document.getElementById('clearStateBtn');
|
||
if (clearStateBtn) {
|
||
clearStateBtn.addEventListener('click', function() {
|
||
showConfirmModal(
|
||
'Clear AMM State',
|
||
'This will clear the AMM state file. All running offers/bids will be lost. Are you sure?',
|
||
function() {
|
||
const form = clearStateBtn.closest('form');
|
||
if (form) {
|
||
const hiddenInput = document.createElement('input');
|
||
hiddenInput.type = 'hidden';
|
||
hiddenInput.name = 'prune_state';
|
||
hiddenInput.value = 'true';
|
||
form.appendChild(hiddenInput);
|
||
form.submit();
|
||
}
|
||
}
|
||
);
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
|
||
{% include 'footer.html' %}
|