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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion allways/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = '1.0.6'
__version__ = '1.0.7'
version_split = __version__.split('.')
__spec_version__ = (1000 * int(version_split[0])) + (10 * int(version_split[1])) + (1 * int(version_split[2]))
7 changes: 7 additions & 0 deletions allways/chains.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class ChainDefinition:
env_prefix: str # .env variable prefix (e.g. 'BTC' -> BTC_RPC_URL)
seconds_per_block: int = 12 # Average block time on this chain
min_confirmations: int = 1 # Minimum confirmations before accepting a transaction
# Smallest amount that can actually exist/move on-chain, in native units
# (BTC dust floor, TAO existential deposit). 1 = no floor.
min_onchain_amount: int = 1


# ─── Supported Chains ────────────────────────────────────
Expand All @@ -32,6 +35,8 @@ class ChainDefinition:
env_prefix='BTC',
seconds_per_block=600,
min_confirmations=2,
# 1000 sat, not the bare 546 P2PKH dust line: margin vs higher dustrelayfee / wallet quirks, and a tighter executable-rate ceiling.
min_onchain_amount=1000,
)
CHAIN_TAO = ChainDefinition(
id='tao',
Expand All @@ -41,6 +46,8 @@ class ChainDefinition:
env_prefix='TAO',
seconds_per_block=12,
min_confirmations=6,
# Existential deposit: accounts below this are reaped.
min_onchain_amount=500,
)

SUPPORTED_CHAINS = {
Expand Down
34 changes: 19 additions & 15 deletions allways/utils/rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,20 @@ def is_executable_rate(
min_swap_rao: int,
max_swap_rao: int,
) -> bool:
"""True iff the rate is integer-routable in its declared direction.
"""True iff the rate is fundably routable in its declared direction.

Crown-eligibility gate against sentinel rates that no user can route:
Crown-eligibility gate against rates that no user can route. Routable means
a source >= the source chain's ``min_onchain_amount`` (dust / existential
deposit) maps a TAO leg into ``[min_swap_rao, max_swap_rao]`` — a rate whose
only in-bounds source is sub-dust (e.g. 1 sat -> 0.5 TAO at 5e7 TAO/BTC) is
unfundable, so unexecutable.

* BTC→TAO: high-side sentinels (``1e10``, ``1.797e308`` TAO/BTC) — the
smallest positive sat already maps above ``max_swap_rao``, so no
positive integer source produces an in-bounds TAO leg.
* TAO→BTC: low-side sentinels (``1e-8`` TAO/BTC) — the TAO leg IS the
source amount, so it trivially fits any bounds, but the destination
payout implied by the rate is absurd. Catch this by the symmetric
check on the inverse rate: if treating ``1/rate`` as a BTC→TAO rate
has no integer-routable source either, the original rate is at an
extreme of the executable spectrum and a sentinel by symmetry.
* BTC→TAO: high-side rates — even the smallest fundable sat maps above
``max_swap_rao``, so no fundable source produces an in-bounds TAO leg.
* TAO→BTC: low-side rates — the TAO leg IS the source, so it trivially fits
any bounds, but the destination payout is absurd. Caught by the symmetric
check on ``1/rate``: if the inverse direction has no fundable source, the
original rate is at an extreme of the executable spectrum.

A bound at ``0`` is the contract's "unset" sentinel and disables that
side; both at 0 → permissive (no on-chain bounds yet).
Expand All @@ -151,15 +152,18 @@ def is_executable_rate(

def _has_integer_routable_source(forward_rate: float, src_chain: str) -> bool:
# For a "src → tao" direction at ``forward_rate`` (tao per src), is
# there a positive integer src amount whose TAO leg lands in bounds?
src_decimals = get_chain(src_chain).decimals
decimal_factor = 10 ** (get_chain('tao').decimals - src_decimals)
# there an src amount that is fundable on-chain (>= the chain's
# min_onchain_amount) whose TAO leg lands in bounds?
src = get_chain(src_chain)
decimal_factor = 10 ** (get_chain('tao').decimals - src.decimals)
denom = forward_rate * decimal_factor
if not math.isfinite(denom) or denom <= 0:
# rate × decimal_factor overflowed (e.g. 1.797e308 × 10) → smallest
# positive integer source already maps above any finite max bound.
return False
min_source = max(1, math.ceil(max(1, min_swap_rao) / denom))
# Floor at the source chain's dust/existential minimum: a rate whose only
# in-bounds source is below it (e.g. 1 sat) is unfundable, so unexecutable.
min_source = max(src.min_onchain_amount, math.ceil(max(1, min_swap_rao) / denom))
if max_swap_rao <= 0:
return True
max_source = math.floor(max_swap_rao / denom)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "allways"
version = "1.0.6"
version = "1.0.7"
description = "Allways - Universal Transaction Layer: Trustless cross-chain swaps on Bittensor Subnet 7"
license = "MIT"
requires-python = ">=3.10,<3.15"
Expand Down
45 changes: 27 additions & 18 deletions tests/test_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from decimal import Decimal

from allways.chains import get_chain
from allways.constants import BTC_TO_SAT, RATE_PRECISION, TAO_TO_RAO
from allways.utils.rate import apply_fee_deduction, calculate_to_amount, is_executable_rate, normalize_rate

Expand Down Expand Up @@ -326,30 +327,38 @@ def test_tao_to_btc_lowball_rate_rejected(self):
max_swap on the TAO leg, so the original rate is sentinel-low."""
assert is_executable_rate(1e-8, 'tao', 'btc', self.MIN, self.MAX) is False

def test_boundary_rate_executable_at_one_sat(self):
"""At max_swap/10 = 50_000_000 rao per sat, the smallest integer sat
produces exactly max_swap — should be executable."""
rate = self.MAX / 10 # TAO leg at 1 sat == max_swap
assert is_executable_rate(rate, 'btc', 'tao', self.MIN, self.MAX) is True
DUST = get_chain('btc').min_onchain_amount # smallest fundable BTC source

def test_just_past_boundary_rate_rejected(self):
"""A rate that maps 1 sat just above max_swap → no executable sat."""
rate = (self.MAX / 10) * 1.0001
def test_sub_dust_boundary_rate_rejected(self):
"""At max_swap/10, the only in-bounds source is 1 sat — below the BTC
dust floor, so unfundable. Rejected (the crown-squat rate)."""
rate = self.MAX / 10 # TAO leg at 1 sat == max_swap; 1 sat < dust
assert is_executable_rate(rate, 'btc', 'tao', self.MIN, self.MAX) is False

def test_tao_to_btc_boundary_rate_executable_at_one_sat(self):
"""Symmetric boundary: at inverse=max_swap/10 (i.e. r = 10/max_swap),
treating 1/r as btc→tao maps 1 sat to exactly max_swap on the TAO leg.
Just executable."""
rate = 10 / self.MAX # 1/r * 10 == max_swap; 1 sat at inverse hits max
assert is_executable_rate(rate, 'tao', 'btc', self.MIN, self.MAX) is True
def test_dust_floor_boundary_rate_executable(self):
"""At the rate where the dust floor maps exactly to max_swap, the
smallest fundable source is in-bounds — just executable."""
rate = self.MAX / (10 * self.DUST) # DUST sat → max_swap on the TAO leg
assert is_executable_rate(rate, 'btc', 'tao', self.MIN, self.MAX) is True

def test_tao_to_btc_just_past_boundary_rate_rejected(self):
"""One ULP below the boundary: the inverse rate's 1 sat overshoots
max_swap, so no integer source routes by symmetry."""
rate = (10 / self.MAX) * 0.9999
def test_just_past_dust_floor_boundary_rejected(self):
"""Just above the boundary, even the dust floor overshoots max_swap →
no fundable source routes."""
rate = (self.MAX / (10 * self.DUST)) * 1.0001
assert is_executable_rate(rate, 'btc', 'tao', self.MIN, self.MAX) is False

def test_tao_to_btc_sub_dust_boundary_rate_rejected(self):
"""Symmetric: r = 10/max_swap maps 1 sat (sub-dust) to max_swap on the
inverse leg — rejected (the swap-1670 tao→btc crown-squat rate)."""
rate = 10 / self.MAX
assert is_executable_rate(rate, 'tao', 'btc', self.MIN, self.MAX) is False

def test_tao_to_btc_dust_floor_boundary_executable(self):
"""Symmetric boundary at the dust floor: the dust-clearing inverse
source maps in-bounds — just executable."""
rate = (10 * self.DUST) / self.MAX
assert is_executable_rate(rate, 'tao', 'btc', self.MIN, self.MAX) is True

def test_tao_to_btc_sentinel_unset_bounds_still_permissive(self):
"""Unset bounds disable the gate in both directions — keeps the
legacy "no on-chain bounds yet" path permissive."""
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.